Cybrkyd's Git Repositories

jottings - commit: 2cc12c0

commit 2cc12c0c62231c1fa5d9d8b68de717388650045999996c38d33aac61e179c36d
author Cybrkyd <git@cybrkyd.com> 2026-06-23 10:03:42 +0100
committer Cybrkyd <git@cybrkyd.com> 2026-06-23 10:03:42 +0100
v0.6

Commit Message

Snippets

- Added snippets for "linkx" and "yaml"
- Execute snippets for Ctrl+Space

- Some comment clean-up

📊 Diffstat

resources/js/app.js 89
1 files changed, 82 insertions(+), 7 deletions(-)

Diff

diff --git a/resources/js/app.js b/resources/js/app.js
index aed4350..7762477 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -244,7 +244,7 @@ function runSpellcheck() {
const text = getEditorText();
- // Red squiggles only after a word is committed by space, punctuation, newline.
+ // Red squiggles only after a word is committed by space, punctuation, new line.
const activeToken = getActiveWordToken();
// Find misspelled tokens (and hyphen segments)
@@ -315,7 +315,6 @@ function runSpellcheck() {
return rects;
}
-
// Iterate tokens and draw squiggles for misspelled pieces
TOKEN_RE.lastIndex = 0;
while ((m = TOKEN_RE.exec(text)) !== null) {
@@ -667,7 +666,6 @@ async function loadDocuments() {
const notes = await readNotes();
const note = notes.find(n => n.name === doc.name);
if (note) {
- //Clear any stale auto-save - they were firing against wrong doc
clearTimeout(autoSaveTimer);
setEditorText(note.content);
updateCounts(note.content);
@@ -737,7 +735,6 @@ async function deleteDocument(name) {
await writeNotes(notes);
if (currentDocName === name) {
- //Clear any stale auto-save - they were firing against wrong doc
clearTimeout(autoSaveTimer);
currentDocName = null;
setEditorText('');
@@ -832,6 +829,87 @@ function updateStatus() {
: "New Jot";
}
+ // ========
+ // Snippets
+ // ========
+
+ // Snippet array of keywords
+ const SNIPPETS = [
+ {
+ keyword: 'linkx',
+ getText: () => '<a href="xxx" target="&#95;blank" rel="noopener">xxx</a>'
+ },
+ {
+ keyword: 'yaml',
+ getText: () => {
+ const now = new Date();
+ const pad = (n, w = 2) => String(n).padStart(w, '0');
+ const timestamp =
+ `${now.getUTCFullYear()}-${pad(now.getUTCMonth() + 1)}-${pad(now.getUTCDate())}` +
+ `T${pad(now.getUTCHours())}:${pad(now.getUTCMinutes())}:${pad(now.getUTCSeconds())}+0000`;
+ return `---\ntitle: \nsummary: \ndate: ${timestamp}\n---\n`;
+ }
+ }
+ ];
+
+ // Return the keyword typed immediately before the caret, or null.
+ function getKeywordBeforeCaret() {
+ const sel = window.getSelection();
+ if (!sel || sel.rangeCount === 0) return null;
+
+ const range = sel.getRangeAt(0);
+ if (!range.collapsed) return null;
+ if (!editor.contains(range.startContainer)) return null;
+
+ const container = range.startContainer;
+ if (container.nodeType !== Node.TEXT_NODE) return null;
+
+ const text = container.textContent;
+ const offset = range.startOffset;
+ if (offset === 0) return null;
+
+ // Walk backwards from the caret to find a run of word characters
+ let start = offset;
+ while (start > 0 && /\S/.test(text[start - 1])) start--;
+
+ return start < offset ? { word: text.slice(start, offset), start, node: container } : null;
+ }
+
+ // Expand snippet: delete keyword and insert the snippet text
+ function expandSnippet(snippet, caretInfo) {
+ const { start, node } = caretInfo;
+ const offset = window.getSelection().getRangeAt(0).startOffset;
+
+ // Select the keyword range
+ const replaceRange = document.createRange();
+ replaceRange.setStart(node, start);
+ replaceRange.setEnd(node, offset);
+
+ const sel = window.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(replaceRange);
+
+ // Insert the snippet text in place of the keyword
+ insertTextAtCursor(snippet.getText());
+ }
+
+ // Ctrl+Space: attempt snippet expansion
+ editor.addEventListener("keydown", function(e) {
+ if (e.ctrlKey && e.code === 'Space') {
+ const caretInfo = getKeywordBeforeCaret();
+ if (caretInfo) {
+ const match = SNIPPETS.find(s => s.keyword === caretInfo.word);
+ if (match) {
+ e.preventDefault();
+ expandSnippet(match, caretInfo);
+ return;
+ }
+ }
+ // No matching snippet? Just swallow
+ e.preventDefault();
+ }
+ });
+
// EVENT LISTENERS
document.getElementById("saveBtn").onclick = function() {
const text = getEditorText().trim();
@@ -840,7 +918,6 @@ document.getElementById("saveBtn").onclick = function() {
};
document.getElementById("newBtn").onclick = function() {
- //Clear any stale auto-save - they were firing against wrong doc
clearTimeout(autoSaveTimer);
setEditorText('');
updateCounts('');
@@ -903,8 +980,6 @@ editor.addEventListener("input", function() {
});
// Intercept Enter and insert a real "\n" text node.
- // Of course, this makes it necessary to Enter twice just to go to a new line, and
- // Enter thrice for a new paragraph. A nice UI bug in the making!
editor.addEventListener("keydown", function(e) {
if (e.key === "Enter") {
e.preventDefault();