// --------------------------------------------------------------------------- // customer-grid / ScriptViewPanel.tsx — owner item 6's renderer (W36-T04, contract C3). // // ⛔ ITS OWN MODULE, AND THAT IS A HARNESS FACT AS WELL AS A TIDINESS ONE. It began inside // `viewModes.tsx`, where the other display modes live — and that module `require`s `./cells`, // which imports glide-data-grid, whose CJS build cannot load under this repo's node harness. So // putting it there made the whole component untestable: the ONE thing this surface must prove is // that a hostile spec is rendered as text, and that claim can only be made by RENDERING. Here the // import graph is `react` + `./scriptViews` and nothing else, so `gridUx.test.ts` renders it for // real and asserts on the HTML a person would receive. // ⚠ The stylesheet is imported by `CustomerGrid.tsx` (the mount) for the same reason: tsc // preserves a side-effect CSS import into the emitted CommonJS and node dies on it. // --------------------------------------------------------------------------- import { memo, useEffect, useRef, useState } from "react"; import { barScale, renderableSpec, specKindName } from "./scriptViews"; import type { DrawableSpec, ScriptRun, ScriptView as ScriptViewRecord } from "./scriptViews"; // ═══════════════════════════════════════════════════════════════════════════════════════════ // ⭐⭐ W36-T04 (owner item 6 · rulings R3/R10 · contract C3) — THE CODE-SCRIPT VIEW // // Owner, verbatim: *"Add code script as an interface (database View) so a user can build whatever // they want through the Agent chat interface. be able to create any dashboard they want. User // should have the ability to see the code AND the dashboard output of course."* Both halves are // on screen at once, which is the whole of that sentence. // // ⛔⛔ NO BRANCH OF THIS RENDERER EXECUTES A STRING, AND THAT IS THE TICKET'S OWN `done-when`. // R10 runs the Python in a SERVER sandbox exactly so the browser never has to trust the result; // a renderer that then evaluated one would hand back everything the sandbox contains. So there is // no `dangerouslySetInnerHTML`, no `new Function`, no `eval`, no `href`/`src` taken from a spec, // and no `style` built from spec data anywhere below. Every value reaching the DOM is a SCALAR // that `renderableSpec` admitted, rendered as TEXT — the one exception being a bar's width, which // is a number clamped to a percentage here and never a string from the wire. // // ⛔ AN UNKNOWN `kind` IS A SENTENCE, NOT A BLANK. `spec` is whatever the script emitted (E's C3 // note 1: there is no fixed vocabulary, deliberately), so a view whose author invented // `kind: "sankey"` is a legitimate thing this build cannot draw. Saying so, by name, is the // difference between "this build does not know that shape yet" and "your script is broken". // // ⚠ `ok: false` IS A 200. A script that timed out or divided by zero asked a well-formed question // whose ANSWER is that no view was produced; it is rendered beside the code, never thrown, and // never a toast that disappears while the reader is looking at the line that caused it. // ═══════════════════════════════════════════════════════════════════════════════════════════ function SpecTable({ spec }: { spec: DrawableSpec }) { return (
| {c} | )}
|---|
| {cell} | )}
{run.error ?? "The run ended without emitting anything."}
Run the script to see what it draws.
; const drawable = run.ok ? renderableSpec(run.spec) : null; const claimed = specKindName(run.spec); return ( <> {!run.ok ?{claimed ? `The script emitted a view of kind "${claimed}", which this version does not render. It knows table, metrics, bars and text.` : `The script emitted a view with no recognisable kind. This version renders table, metrics, bars and text.`}
{drawable.body}
: null} > ) : null} {run.stdout ? (
{run.stdout}
{run.truncated ? "\n[output truncated]" : ""}
) : null}
>
);
}
/**
* The whole View: the source on the left, what it drew on the right.
*
* ⚠ A DRAFT RUN NEEDS NO SAVE (C3 note 3), which is what makes this usable: type, run, save when
* it works. The Save button is separate and deliberately not automatic, because a PUT is a NEW
* VERSION and an autosaving editor would fill the 40-deep history with keystrokes.
*/
export const ScriptView = memo(function ScriptView({
view,
running,
run,
saving,
readOnly = false,
onRun,
onSave,
}: {
view: ScriptViewRecord | null;
running: boolean;
run: ScriptRun | null;
saving?: boolean;
readOnly?: boolean;
onRun: (draft: string) => void;
onSave?: (source: string) => void;
}) {
const [draft, setDraft] = useState(view?.source ?? "");
const loadedId = useRefThis script view could not be opened.