Mass guide editor changes #18

Merged
Tyler merged 25 commits from guide_editor_changes into main 2026-06-15 15:37:48 +00:00
3 changed files with 49 additions and 3 deletions
Showing only changes of commit bb959a4bb9 - Show all commits
+42 -3
View File
@@ -329,10 +329,32 @@ class GuideEditor {
} }
toolbarBtn(action, label, block = null) { toolbarBtn(action, label, block = null) {
return el('button', { const btn = el('button', {
type: 'button', type: 'button',
onClick: () => this.formatDescription(action, block), onClick: () => this.formatDescription(action, block),
}, label); }, label);
btn.dataset.action = action;
if (block) btn.dataset.block = block;
return btn;
}
/** Reflect the current selection's formatting state on the toolbar buttons. */
updateToolbarState() {
if (!this.dom.richToolbar) return;
for (const btn of this.dom.richToolbar.children) {
const { action, block } = btn.dataset;
let active = false;
try {
if (action === 'formatBlock') {
active = document.queryCommandValue('formatBlock').toLowerCase() === (block || 'blockquote');
} else if (action === 'bold' || action === 'italic' || action === 'insertUnorderedList' || action === 'insertOrderedList') {
active = document.queryCommandState(action);
}
} catch {
active = false;
}
btn.classList.toggle('active', active);
}
} }
bindShellEvents() { bindShellEvents() {
@@ -422,7 +444,14 @@ class GuideEditor {
this.dom.addTableBlockBtn.addEventListener('click', () => this.addBlock('table')); this.dom.addTableBlockBtn.addEventListener('click', () => this.addBlock('table'));
this.dom.descEditor.addEventListener('focus', () => { this.dom.descEditor.addEventListener('focus', () => {
// Make Enter start a new paragraph (<p>) rather than a plain <div>,
// so line breaks survive sanitization and show up in exports.
document.execCommand('defaultParagraphSeparator', false, 'p');
if (this.currentStep) this.pushCanvasHistory('description'); if (this.currentStep) this.pushCanvasHistory('description');
this.updateToolbarState();
});
this.dom.descEditor.addEventListener('blur', () => {
for (const btn of this.dom.richToolbar.children) btn.classList.remove('active');
}); });
this.dom.descEditor.addEventListener('input', () => { this.dom.descEditor.addEventListener('input', () => {
if (!this.currentStep) return; if (!this.currentStep) return;
@@ -430,6 +459,12 @@ class GuideEditor {
this.pendingSave = true; this.pendingSave = true;
this.saveStepDebounced(); this.saveStepDebounced();
this.emitMeta(); this.emitMeta();
this.updateToolbarState();
});
this.dom.descEditor.addEventListener('keyup', () => this.updateToolbarState());
this.dom.descEditor.addEventListener('mouseup', () => this.updateToolbarState());
document.addEventListener('selectionchange', () => {
if (document.activeElement === this.dom.descEditor) this.updateToolbarState();
}); });
this.dom.descEditor.addEventListener('paste', (e) => { this.dom.descEditor.addEventListener('paste', (e) => {
@@ -1821,9 +1856,12 @@ class GuideEditor {
case 'insertOrderedList': case 'insertOrderedList':
document.execCommand('insertOrderedList'); document.execCommand('insertOrderedList');
break; break;
case 'formatBlock': case 'formatBlock': {
document.execCommand('formatBlock', false, block || 'blockquote'); const want = block || 'blockquote';
const current = document.queryCommandValue('formatBlock').toLowerCase();
document.execCommand('formatBlock', false, current === want ? 'p' : want);
break; break;
}
case 'createLink': { case 'createLink': {
const selectedText = window.getSelection().toString(); const selectedText = window.getSelection().toString();
const text = selectedText || window.prompt('Link text'); const text = selectedText || window.prompt('Link text');
@@ -1844,6 +1882,7 @@ class GuideEditor {
this.pendingSave = true; this.pendingSave = true;
this.saveStepDebounced(); this.saveStepDebounced();
} }
this.updateToolbarState();
} }
onDocumentKeyDown(e) { onDocumentKeyDown(e) {
+5
View File
@@ -487,6 +487,11 @@ kbd {
margin-bottom: 8px; margin-bottom: 8px;
} }
.rich-toolbar button { padding: 4px 8px; font-size: 12px; } .rich-toolbar button { padding: 4px 8px; font-size: 12px; }
.rich-toolbar button.active {
background: var(--accent);
border-color: var(--accent);
color: var(--accent-fg);
}
.rich-editor { .rich-editor {
min-height: 110px; min-height: 110px;
max-height: 220px; max-height: 220px;
+2
View File
@@ -42,6 +42,8 @@ function htmlToMarkdown(html) {
.replace(/<hr\s*\/?>/gi, '\n---\n') .replace(/<hr\s*\/?>/gi, '\n---\n')
.replace(/<p>/gi, '\n') .replace(/<p>/gi, '\n')
.replace(/<\/p>/gi, '\n') .replace(/<\/p>/gi, '\n')
.replace(/<div>/gi, '\n')
.replace(/<\/div>/gi, '\n')
.replace(/<[^>]+>/g, ''); .replace(/<[^>]+>/g, '');
return decodeEntities(out).replace(/[ \t]+\n/g, '\n').replace(/\n{3,}/g, '\n\n').trim(); return decodeEntities(out).replace(/[ \t]+\n/g, '\n').replace(/\n{3,}/g, '\n\n').trim();