Chart builder: fold render diagnostic behind a disclosure

This commit is contained in:
2026-06-11 19:27:03 +03:00
parent 181b9da8ab
commit 2a0b4794df
2 changed files with 52 additions and 15 deletions
@@ -709,9 +709,35 @@
.previewError { .previewError {
margin: auto 0; margin: auto 0;
padding: var(--space-3); padding: var(--space-3);
font-family: var(--font-mono); }
font-size: 12px;
.previewErrorHeadline {
margin: 0;
font-size: 13px;
line-height: 1.5; line-height: 1.5;
white-space: pre-wrap;
color: var(--support-error); 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;
}
+23 -12
View File
@@ -737,7 +737,10 @@ function BuilderPreview() {
const hostRef = useRef<HTMLDivElement>(null); const hostRef = useRef<HTMLDivElement>(null);
const handleRef = useRef<RenderHandle | null>(null); const handleRef = useRef<RenderHandle | null>(null);
const generationRef = useRef(0); const generationRef = useRef(0);
const [error, setError] = useState<string | null>(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 // Set when the chart resolves larger than the canvas backend can draw — a
// physical render-size limit, distinct from the readability cardinality warnings. // physical render-size limit, distinct from the readability cardinality warnings.
const [tooLarge, setTooLarge] = useState<{ heightPx: number; limitPx: number } | null>(null); const [tooLarge, setTooLarge] = useState<{ heightPx: number; limitPx: number } | null>(null);
@@ -809,16 +812,18 @@ function BuilderPreview() {
setTooLarge({ heightPx: e.heightPx, limitPx: e.limitPx }); setTooLarge({ heightPx: e.heightPx, limitPx: e.limitPx });
setError(null); setError(null);
} else if (e instanceof DatasetNotFoundError) { } else if (e instanceof DatasetNotFoundError) {
// TODO: this drops the next-step the error contract wants (arch 10); LivePreview // Near-unreachable (the builder opens from an existing dataset), but if the
// gives "Create it from Datasets…". Near-unreachable here (the builder opens from // backing dataset is deleted mid-session the contract still wants the next step.
// an existing dataset), so it's terse — restore the next-step if it can be reached. setError({
setError(`Dataset "${e.datasetName}" not found.`); message: `Dataset "${e.datasetName}" not found — recreate it from Datasets, then reopen the builder.`,
});
setTooLarge(null); setTooLarge(null);
} else { } else {
// TODO: arch 10 routes a raw diagnostic into a disclosure, not the headline. The // Plain headline; the raw Vega message folds into the disclosure below.
// editor surfaces the Vega message inline by design; the builder could fold it setError({
// behind a details disclosure and keep the headline plain. message: "Couldn't render this chart.",
setError(`Couldn't render this chart: ${(e as Error).message}`); detail: (e as Error).message,
});
setTooLarge(null); setTooLarge(null);
} }
} }
@@ -853,9 +858,15 @@ function BuilderPreview() {
<div className={styles.previewHost} ref={hostRef} /> <div className={styles.previewHost} ref={hostRef} />
</div> </div>
{valid && tooLarge === null && error !== null && ( {valid && tooLarge === null && error !== null && (
<pre className={styles.previewError} role="alert"> <div className={styles.previewError} role="alert">
{error} <p className={styles.previewErrorHeadline}>{error.message}</p>
</pre> {error.detail !== undefined && (
<details className={styles.previewErrorDetails}>
<summary className={styles.previewErrorSummary}>Technical details</summary>
<pre className={styles.previewErrorDetail}>{error.detail}</pre>
</details>
)}
</div>
)} )}
</div> </div>
); );