diff --git a/src/app/components/ChartBuilderModal.module.css b/src/app/components/ChartBuilderModal.module.css index ffbb2e6..51c6762 100644 --- a/src/app/components/ChartBuilderModal.module.css +++ b/src/app/components/ChartBuilderModal.module.css @@ -709,9 +709,35 @@ .previewError { margin: auto 0; padding: var(--space-3); - font-family: var(--font-mono); - font-size: 12px; +} + +.previewErrorHeadline { + margin: 0; + font-size: 13px; line-height: 1.5; - white-space: pre-wrap; color: var(--support-error); } + +.previewErrorDetails { + margin-top: var(--space-2); +} + +.previewErrorSummary { + font-size: 12px; + color: var(--text-secondary); + cursor: pointer; + user-select: none; +} + +.previewErrorDetail { + margin: var(--space-2) 0 0; + padding: var(--space-3); + font-family: var(--font-mono); + font-size: 12px; + line-height: 1.4; + color: var(--text); + background: var(--layer-01); + border: var(--border-width) solid var(--border); + white-space: pre-wrap; + word-break: break-word; +} diff --git a/src/app/components/ChartBuilderModal.tsx b/src/app/components/ChartBuilderModal.tsx index b97774f..8312ee3 100644 --- a/src/app/components/ChartBuilderModal.tsx +++ b/src/app/components/ChartBuilderModal.tsx @@ -737,7 +737,10 @@ function BuilderPreview() { const hostRef = useRef(null); const handleRef = useRef(null); const generationRef = useRef(0); - const [error, setError] = useState(null); + // The error contract (docs/architecture/10): a plain headline that names the + // problem and points at the next step; any raw Vega diagnostic goes in `detail`, + // shown behind a disclosure rather than in the headline. + const [error, setError] = useState<{ message: string; detail?: string } | null>(null); // Set when the chart resolves larger than the canvas backend can draw — a // physical render-size limit, distinct from the readability cardinality warnings. const [tooLarge, setTooLarge] = useState<{ heightPx: number; limitPx: number } | null>(null); @@ -809,16 +812,18 @@ function BuilderPreview() { setTooLarge({ heightPx: e.heightPx, limitPx: e.limitPx }); setError(null); } else if (e instanceof DatasetNotFoundError) { - // TODO: this drops the next-step the error contract wants (arch 10); LivePreview - // gives "Create it from Datasets…". Near-unreachable here (the builder opens from - // an existing dataset), so it's terse — restore the next-step if it can be reached. - setError(`Dataset "${e.datasetName}" not found.`); + // Near-unreachable (the builder opens from an existing dataset), but if the + // backing dataset is deleted mid-session the contract still wants the next step. + setError({ + message: `Dataset "${e.datasetName}" not found — recreate it from Datasets, then reopen the builder.`, + }); setTooLarge(null); } else { - // TODO: arch 10 routes a raw diagnostic into a disclosure, not the headline. The - // editor surfaces the Vega message inline by design; the builder could fold it - // behind a details disclosure and keep the headline plain. - setError(`Couldn't render this chart: ${(e as Error).message}`); + // Plain headline; the raw Vega message folds into the disclosure below. + setError({ + message: "Couldn't render this chart.", + detail: (e as Error).message, + }); setTooLarge(null); } } @@ -853,9 +858,15 @@ function BuilderPreview() {
{valid && tooLarge === null && error !== null && ( -
-          {error}
-        
+
+

{error.message}

+ {error.detail !== undefined && ( +
+ Technical details +
{error.detail}
+
+ )} +
)} );