diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2023059
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# WhichNot
+
+WhichNot is an experimental offline-first note-taking application, aimed at recreating the look-and-feel and ease of use of a standard messaging app.
+
+**Try and use it now at !** (Includes a demo notebook with more info about the app.)
+
+
\ No newline at end of file
diff --git a/app.js b/app.js
index 7d72f92..490d8da 100644
--- a/app.js
+++ b/app.js
@@ -113,7 +113,7 @@ const uuidv7 = () => {
}
const generateUUID = () => uuidv7(); // crypto.randomUUID();
const genAESKey = async () => crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
-// const genEd25519 = async () => crypto.subtle.generateKey({ name: 'Ed25519', namedCurve: 'Ed25519' }, true, ['sign', 'verify']);
+const genEcdsaP256 = async () => crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']);
const exportJWK = async (key) => btoa(JSON.stringify(await crypto.subtle.exportKey('jwk', key)));
const importJWK = async (b64, alg, usages) => crypto.subtle.importKey('jwk', JSON.parse(atob(b64)), alg, true, usages);
const randBytes = (n=12) => {
@@ -175,6 +175,14 @@ const randomEmoji = () => EMOJIS[Math.floor(Math.random() * EMOJIS.length)];
const randomColor = () => ('#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, '0'));
const closedContextMenu = s => ({ contextMenu: { ...s.contextMenu, visible: false } });
+const makeTextareaHeight = text => {
+ let lines = text.split('\n').length;
+ if (lines > 10) {
+ lines = 10;
+ }
+ return `${lines + 2}em`;
+};
+const textareaInputHandler = el => (el.style.minHeight = makeTextareaHeight(el.value));
function App() {
const [state, setState] = useState({
@@ -186,6 +194,7 @@ function App() {
contextMenu:{ visible: false, messageId: null, x: 0, y: 0 },
searchModal: { visible: false, global: false, query: '' },
editingMessage: null, replyingTo: null, reactionInputFor: null,
+ debugMode: false,
});
const isFirstHashPush = useRef(true);
const messageInputRef = useRef();
@@ -283,13 +292,14 @@ function App() {
let id = /* (type === 'local' ? */ generateUUID(); /* : prompt('Remote ID:')); */
// if (!id) return;
const now = Date.now();
- // const ed = await genEd25519();
+ // const ecdsa = await genEcdsaP256();
const notebook = {
id, name: `${STRINGS.get('Notebook')} ${now}`, description: '',
emoji: randomEmoji(), color: randomColor(),
parseMode: "markdown", // sourceType: type,
nextMessageId: 1, created: now,
- aesKeyB64: await exportJWK(await genAESKey()), // edPrivB64: await exportJWK(ed.privateKey), edPubB64: await exportJWK(ed.publicKey),
+ aesKeyB64: await exportJWK(await genAESKey()),
+ // ecdsaPrivB64: await exportJWK(ecdsa.privateKey), ecdsaPubB64: await exportJWK(ecdsa.publicKey),
};
setState(s => ({ ...s,
notebooks: [ ...s.notebooks, notebook ],
@@ -380,6 +390,7 @@ function App() {
const message = state.messages[state.selectedNotebookId]?.[state.editingMessage];
if (message) {
messageInputRef.current.value = message.text;
+ textareaInputHandler(messageInputRef.current);
}
}
}, [state.editingMessage, state.selectedNotebookId, state.messages]);
@@ -404,6 +415,7 @@ function App() {
}
message = { ...message, text, edited: (state.editingMessage!=null ? (text !== message.text ? Date.now() : message.edited) : false), };
messageInputRef.current.value = '';
+ messageInputRef.current.style.minHeight = null;
// update nextMessageId if new
setState(s => ({ ...s, notebooks: s.notebooks.map(notebook => notebook.id===notebookId
? { ...notebook, nextMessageId: (state.editingMessage==null ? notebook.nextMessageId+1 : notebook.nextMessageId) }
@@ -501,7 +513,7 @@ function ChatScreen({messageInputRef}) {
${!notebook.readonly && html`
${state.replyingTo && html`
- ${STRINGS.get('Reply to')}: "${
+ ${STRINGS.get('Reply to')}: "${
getMessage(state.replyingTo.notebookId, state.replyingTo.messageId)?.text || ''
}"
@@ -514,7 +526,7 @@ function ChatScreen({messageInputRef}) {
ev.preventDefault();
sendMessage();
}
- }}/>
+ }} onInput=${ev => textareaInputHandler(ev.target)} />
`}
@@ -557,7 +569,12 @@ function Message({message, notebook}) {
`)}
${!notebook.readonly && (state.reactionInputFor===message.id
- ? html` e.key==='Enter' && (confirmReaction(message.id, e.target.value), e.target.value='')} />`
+ ? html` {
+ if (ev.key==='Enter') {
+ confirmReaction(message.id, ev.target.value);
+ ev.target.value = '';
+ }
+ }} />`
: html``
)}
@@ -682,7 +699,7 @@ function NotebookSettingsModal() {
-
+