Add quote indicator and distinct callout styling for Note/Tip/Warning/Important
- Description editor: blockquotes get a left accent line and muted color so it's clear when typing in "quote mode" (matches the existing active Quote toolbar button highlight). - Editor block cards: each text block's left border is colored by its level (info/success/warn/error) so Note/Tip/Warning/Important are distinguishable at a glance. - PDF/HTML/DOCX exports: callouts now get a level-specific accent color and tinted background/shading (blue/green/amber/red), instead of all looking identical except for the label text. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
@@ -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 = `<p>${escapeHtml(body.value)}</p>`; save(); });
|
||||
card.append(
|
||||
|
||||
@@ -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;
|
||||
|
||||
+12
-2
@@ -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 }) : ''),
|
||||
'<w:shd w:val="clear" w:fill="F9FAFB"/>'
|
||||
run(label, { bold: true, size: 20, color: style.color }) + (tb.descriptionText ? run('\n' + tb.descriptionText, { size: 20 }) : ''),
|
||||
`<w:shd w:val="clear" w:fill="${style.fill}"/><w:pBdr><w:left w:val="single" w:sz="24" w:space="4" w:color="${style.color}"/></w:pBdr>`
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -78,10 +78,14 @@ const BASE_CSS = `
|
||||
pre.code { background: #f3f4f6; padding: 12px; border-radius: 6px; overflow-x: auto; }
|
||||
table { border-collapse: collapse; margin: .6em 0; }
|
||||
th, td { border: 1px solid #d1d5db; padding: 4px 10px; text-align: left; }
|
||||
.block { border-left: 4px solid #9ca3af; background: #f9fafb; padding: 8px 12px; margin: .6em 0; border-radius: 0 6px 6px 0; }
|
||||
.block { border-left: 4px solid #3b82f6; background: #eff6ff; padding: 8px 12px; margin: .6em 0; border-radius: 0 6px 6px 0; }
|
||||
.block strong { color: #1d4ed8; }
|
||||
.block-warn { border-color: #f59e0b; background: #fffbeb; }
|
||||
.block-warn strong { color: #b45309; }
|
||||
.block-error { border-color: #ef4444; background: #fef2f2; }
|
||||
.block-error strong { color: #b91c1c; }
|
||||
.block-success { border-color: #10b981; background: #ecfdf5; }
|
||||
.block-success strong { color: #047857; }
|
||||
.skipped { opacity: .55; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body { background: #111827; color: #e5e7eb; }
|
||||
|
||||
+13
-2
@@ -11,6 +11,15 @@ const LIST_INDENT = 14;
|
||||
const QUOTE_INDENT = 10;
|
||||
const HEADING_BUMP = { h1: 2.5, h2: 2, h3: 1.5, h4: 1 };
|
||||
|
||||
// Callout styling per text-block level, matching the colors used in the
|
||||
// HTML/editor UI so a "Tip" looks distinct from a "Warning" at a glance.
|
||||
const LEVEL_STYLE = {
|
||||
info: { accent: [59, 130, 246], tint: [239, 246, 255] }, // blue — Note
|
||||
success: { accent: [16, 185, 129], tint: [236, 253, 245] }, // green — Tip
|
||||
warn: { accent: [245, 158, 11], tint: [255, 251, 235] }, // amber — Warning
|
||||
error: { accent: [239, 68, 68], tint: [254, 242, 242] }, // red — Important
|
||||
};
|
||||
|
||||
function fontForRun(run) {
|
||||
if (run.code) return 'F3';
|
||||
if (run.bold && run.italic) return 'F5';
|
||||
@@ -258,9 +267,11 @@ function exportPdf(ast, outDir, template = {}) {
|
||||
? layoutDescription(pdf, tb.descriptionHtml, usableW - 18, 9.5)
|
||||
: { items: [], height: 0 };
|
||||
const blockH = 16 + bodyH;
|
||||
const style = LEVEL_STYLE[tb.level] || LEVEL_STYLE.info;
|
||||
ensure(blockH + 4);
|
||||
pdf.rect(M, y, 3, blockH, { fill: tpl.accentColor });
|
||||
pdf.text(label, M + 10, y + 2, { size: 9.5, font: 'F2' });
|
||||
pdf.rect(M, y, usableW, blockH, { fill: style.tint });
|
||||
pdf.rect(M, y, 3, blockH, { fill: style.accent });
|
||||
pdf.text(label, M + 10, y + 2, { size: 9.5, font: 'F2', color: style.accent });
|
||||
let by = y + 16;
|
||||
for (const item of items) {
|
||||
if (item.kind === 'hr') {
|
||||
|
||||
Reference in New Issue
Block a user