diff --git a/app/renderer/editor.js b/app/renderer/editor.js index 2d6a51b..af20fd7 100644 --- a/app/renderer/editor.js +++ b/app/renderer/editor.js @@ -674,6 +674,7 @@ class GuideEditor { }, header); if (kind === 'text') { + card.dataset.level = block.level || 'info'; const position = makeSelect(block.position, [ { value: 'before-title', label: 'Before title' }, { value: 'after-title', label: 'After title' }, @@ -696,7 +697,7 @@ class GuideEditor { body.dataset.blockField = 'body'; body.value = (block.descriptionHtml || '').replace(/<[^>]+>/g, ''); position.addEventListener('change', () => { block.position = position.value; save(); }); - level.addEventListener('change', () => { block.level = level.value; save(); }); + level.addEventListener('change', () => { block.level = level.value; card.dataset.level = level.value; save(); }); title.addEventListener('input', () => { block.title = title.value; save(); }); body.addEventListener('input', () => { block.descriptionHtml = `
${escapeHtml(body.value)}
`; save(); }); card.append( diff --git a/app/renderer/style.css b/app/renderer/style.css index 137b1a0..06f6675 100644 --- a/app/renderer/style.css +++ b/app/renderer/style.css @@ -514,6 +514,12 @@ kbd { border-radius: 10px; background: var(--panel-2); } +.rich-editor blockquote { + margin: 6px 0; + padding: 2px 0 2px 12px; + border-left: 3px solid var(--accent); + color: var(--muted); +} .block-card, .annotation-editor-inner { @@ -523,12 +529,17 @@ kbd { } .block-card { border: 1px solid var(--border); + border-left: 4px solid var(--border); border-radius: 12px; padding: 10px; background: var(--panel-solid); } .block-card[draggable="true"] { cursor: grab; } .block-card[draggable="true"]:active { cursor: grabbing; } +.block-card[data-level="info"] { border-left-color: #3b82f6; } +.block-card[data-level="success"] { border-left-color: #10b981; } +.block-card[data-level="warn"] { border-left-color: #f59e0b; } +.block-card[data-level="error"] { border-left-color: #ef4444; } .annotation-list { display: flex; flex-direction: column; diff --git a/exporters/docx.js b/exporters/docx.js index 5ca8972..ef761ce 100644 --- a/exporters/docx.js +++ b/exporters/docx.js @@ -18,6 +18,15 @@ const DEFAULT_TEMPLATE = { imageWidthTwips: 9000, // ~15.9cm inside A4 margins }; +// Callout styling per text-block level, matching the colors used in the +// HTML/PDF exports so a "Tip" looks distinct from a "Warning" at a glance. +const LEVEL_STYLE = { + info: { fill: 'EFF6FF', color: '1D4ED8' }, // blue — Note + success: { fill: 'ECFDF5', color: '047857' }, // green — Tip + warn: { fill: 'FFFBEB', color: 'B45309' }, // amber — Warning + error: { fill: 'FEF2F2', color: 'B91C1C' }, // red — Important +}; + const EMU_PER_PX = 9525; // 96 dpi function p(children, props = '') { @@ -117,9 +126,10 @@ function exportDocx(ast, outDir, template = {}) { function emitTextBlocks(step, position) { for (const tb of stepBlocks(step).filter((b) => b.kind === 'text' && b.position === position)) { const label = `${LEVEL_LABEL[tb.level] || 'Note'}${tb.title ? `: ${tb.title}` : ''}`; + const style = LEVEL_STYLE[tb.level] || LEVEL_STYLE.info; body.push(p( - run(label, { bold: true, size: 20 }) + (tb.descriptionText ? run('\n' + tb.descriptionText, { size: 20 }) : ''), - '