diff --git a/frontend/editor-poc.html b/frontend/editor-poc.html
index a8d628a..dc7aca6 100644
--- a/frontend/editor-poc.html
+++ b/frontend/editor-poc.html
@@ -1703,55 +1703,56 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
var before = ta.value.slice(0, start);
var after = ta.value.slice(end);
- // Smart insertion: if we matched a prefix, replace it
- var lineStart = before.lastIndexOf('\n') + 1;
- var lineBefore = before.slice(lineStart);
- var ctx = detectContext(ta.value, start);
+ // Lazy-load detectContext for smart insertion
+ import('/static/js/autocomplete.js').then(function(m) {
+ var lineStart = before.lastIndexOf('\n') + 1;
+ var ctx = m.detectContext(ta.value, start);
- if (ctx.type === 'wikilink') {
- // Replace [[partial with [[filename]]
- var bracketPos = before.lastIndexOf('[[');
- if (bracketPos >= 0) {
- before = before.slice(0, bracketPos) + '[[' + item.insert;
+ if (ctx.type === 'wikilink') {
+ var bracketPos = before.lastIndexOf('[[');
+ if (bracketPos >= 0) {
+ before = before.slice(0, bracketPos) + '[[' + item.insert;
+ ta.value = before + after;
+ var newPos = before.length;
+ ta.setSelectionRange(newPos, newPos);
+ }
+ } else if (ctx.type === 'frontmatter') {
+ before = before.slice(0, lineStart) + item.insert;
ta.value = before + after;
- var newPos = before.length;
+ ta.setSelectionRange(before.length, before.length);
+ } else {
+ ta.value = before + item.insert + after;
+ var newPos = start + item.insert.length;
ta.setSelectionRange(newPos, newPos);
}
- } else if (ctx.type === 'frontmatter') {
- // Replace current line with the field
- before = before.slice(0, lineStart) + item.insert;
- ta.value = before + after;
- ta.setSelectionRange(before.length, before.length);
- } else {
- // Standard insert at cursor
- ta.value = before + item.insert + after;
- var newPos = start + item.insert.length;
- ta.setSelectionRange(newPos, newPos);
- }
- hideAutocomplete();
- ta.focus();
- markDirty();
- autoHeight();
- updateLineNumbers();
+ hideAutocomplete();
+ ta.focus();
+ markDirty();
+ autoHeight();
+ updateLineNumbers();
+ });
}
function triggerAutocomplete() {
- var result = suggest(ta.value, ta.selectionStart);
- if (result.async) {
- // Wikilink: fetch file list
+ // Lazy-load the autocomplete module on first use
+ var loadAndTrigger = function() {
import('/static/js/autocomplete.js').then(function(m) {
- m.searchWikilinks(result.partial || '', function(items) {
- showAutocomplete(items, 'Wikilinks');
- });
+ var result = m.suggest(ta.value, ta.selectionStart);
+ if (result.async) {
+ m.searchWikilinks(result.partial || '', function(items) {
+ showAutocomplete(items, 'Wikilinks');
+ });
+ } else if (result.items.length > 0) {
+ var ctxLabels = {
+ mermaid: 'Mermaid', code: 'Code', fence_open: 'Langage',
+ frontmatter: 'Frontmatter', markdown: 'Markdown'
+ };
+ showAutocomplete(result.items, ctxLabels[result.context] || result.context);
+ }
});
- } else if (result.items.length > 0) {
- var ctxLabels = {
- mermaid: 'Mermaid', code: 'Code', fence_open: 'Langage',
- frontmatter: 'Frontmatter', markdown: 'Markdown'
- };
- showAutocomplete(result.items, ctxLabels[result.context] || result.context);
- }
+ };
+ loadAndTrigger();
}
function getCaretCoordinates(textarea) {
@@ -1786,8 +1787,8 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
else if (e.key === 'Escape') { e.preventDefault(); hideAutocomplete(); }
return;
}
- // Alt+\ : force autocomplete
- if (e.altKey && !e.ctrlKey && !e.metaKey && e.key === '\\') {
+ // Alt+\ : force autocomplete (key may be '\' or 'Backslash' depending on browser)
+ if (e.altKey && !e.ctrlKey && !e.metaKey && (e.key === '\\' || e.key === 'Backslash')) {
e.preventDefault();
triggerAutocomplete();
return;
@@ -1801,24 +1802,30 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
var before = ta.value.slice(0, ta.selectionStart);
var after = ta.value.slice(ta.selectionEnd);
var lineStart = before.lastIndexOf('\n') + 1;
- var currentLine = before.slice(lineStart);
- var indent = (currentLine.match(/^(\s*)/) || [''])[0];
+ // Reconstruct FULL current line (before cursor + after cursor until newline)
+ var afterNewline = after.indexOf('\n');
+ var restOfLine = afterNewline >= 0 ? after.slice(0, afterNewline) : after;
+ var fullLine = before.slice(lineStart) + restOfLine;
+ var cursorInLine = ta.selectionStart - lineStart;
+ var indent = (fullLine.match(/^(\s*)/) || [''])[0];
// Ordered list: 1. 2. 3. etc
- var olMatch = currentLine.match(/^(\s*)(\d+)\.\s/);
+ var olMatch = fullLine.match(/^(\s*)(\d+)\.\s/);
// Unordered list: - * +
- var ulMatch = currentLine.match(/^(\s*)([-*+])\s/);
+ var ulMatch = fullLine.match(/^(\s*)([-*+])\s/);
// Task checkbox: - [ ] or - [x]
- var taskMatch = currentLine.match(/^(\s*)([-*+])\s+\[([ xX])\]\s/);
+ var taskMatch = fullLine.match(/^(\s*)([-*+])\s+\[([ xX])\]\s/);
if (taskMatch) {
- var afterTaskTrim = currentLine.slice(taskMatch[0].length).trim();
- if (afterTaskTrim.length === 0) {
- // Empty checkbox: break list (remove this line)
+ var textAfterMarker = fullLine.slice(taskMatch[0].length);
+ // Only continue if cursor is at or after the marker text
+ if (cursorInLine < taskMatch[0].length) return; // cursor in prefix, let browser handle
+ if (textAfterMarker.trim().length === 0) {
+ // Empty checkbox: break list
e.preventDefault();
- ta.value = before.slice(0, lineStart) + after;
+ ta.value = before.slice(0, lineStart) + after.slice(restOfLine.length);
ta.setSelectionRange(lineStart, lineStart);
} else {
- // Continue checkbox (empty, unchecked)
+ // Continue checkbox
e.preventDefault();
ta.value = before + '\n' + indent + taskMatch[2] + ' [ ] ';
ta.setSelectionRange(ta.value.length, ta.value.length);
@@ -1826,6 +1833,7 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
markDirty(); autoHeight(); updateLineNumbers();
return;
} else if (olMatch) {
+ if (cursorInLine < olMatch[0].length) return;
// Auto-increment ordered list
e.preventDefault();
var num = parseInt(olMatch[2]) + 1;
@@ -1834,11 +1842,12 @@ body { font-family: var(--sans); background: var(--bg); color: var(--text); heig
markDirty(); autoHeight(); updateLineNumbers();
return;
} else if (ulMatch) {
- var afterTrim = currentLine.slice(ulMatch[0].length).trim();
- if (afterTrim.length === 0) {
- // Empty bullet: remove it and break list
+ if (cursorInLine < ulMatch[0].length) return;
+ var textAfter = fullLine.slice(ulMatch[0].length);
+ if (textAfter.trim().length === 0) {
+ // Empty bullet: break list
e.preventDefault();
- ta.value = before.slice(0, lineStart) + '\n' + after.slice(after.indexOf('\n') + 1);
+ ta.value = before.slice(0, lineStart) + after.slice(restOfLine.length);
ta.setSelectionRange(lineStart, lineStart);
} else {
// Continue bullet