[ Escoffier Labs · Course 11 (l’ardoise) ]
Mise en Scène: staging the explainer
A Vite + React 19 studio that turns raw source material into a self-contained, audience-tuned technical explainer rendered as a dark editorial SVG scene - exported as one standalone HTML file. An honest early spike, scoped without overclaiming.
The Chalkboard Out Front
Mise en Scène is "the studio": paste a repo, an OpenAPI spec, a README, an incident report, a transcript, or architecture notes, and it produces a polished, editable technical explainer rendered as a dark editorial HTML/SVG scene. Pick an audience and a mode, click the cards, then export the whole thing as one standalone HTML artifact you can hand to anyone.
It solves the editorial-diagram problem: producing a legible, audience-tuned architecture picture from raw notes without hand-drawing it, and shipping it as a portable file. On escoffierlabs.dev it is course 11 - station "l’ardoise", the chalkboard out front - the fleet’s front-of-house explainer surface.
Mise en Scène vs Mise en Place
The name is a deliberate counterpart to Brigade’s mise en place. Mise en place is prep - everything in its place before you cook. Mise en scène is stagecraft - the arrangement of a scene so it reads. This tool’s job is composing extracted elements into a legible scene. Being honest about the current spike matters as much as the pitch.
What it is
- A staging surfaceArranges extracted facts, terms, and blocks into a composed, audience-tuned scene.
- A single-file exporterEmits a standalone HTML artifact (styles included) plus a JSON export.
- Honestly scopedThe README calls it "an early working spike". v0.1.0, private package, published MIT.
What it is not (yet)
- A generic graph editorThe diagram is a fixed 8-block pipeline self-portrait today, not a graph synthesized from arbitrary input.
- Model-driven extractionIngestion is local text heuristics (regex sentences + frequency terms) - no model calls yet.
- Wired to BrigadeBrigade appears only as a depicted block in the demo scene; there is no Brigade code dependency.
The Data Model: One Scene Object
Everything an author touches is one plain Scene object. Blocks are absolutely-positioned cards; edges are directed connectors keyed to the four explainer modes. No backing store - just a typed value.
src/App.tsx
type SceneBlock = {
id: string; label: string; kicker: string; detail: string;
x: number; y: number; w: number; h: number; // absolute layout
};
type SceneEdge = {
from: string; to: string; label: string;
modes: Array<ExplainerMode>; // which modes light this edge up
dashed?: boolean;
};
type Scene = {
title: string; subtitle: string; summary: string;
facts: string[]; terms: string[];
blocks: SceneBlock[]; edges: SceneEdge[];
};x / y / w / h on every blockCards are absolutely positioned in a fixed 1280x780 viewBox - layout is data, not CSS flow.modes: Array<ExplainerMode>An edge belongs to one or more of the 4 modes; switching mode filters what lights up.facts / terms: string[]These (plus title and card detail) are the parts actually extracted from pasted source.
Source → Scene: the Heuristic Pipeline
createScene is the transform. Today it is pure local heuristics: it extracts text, then merges it into a fixed block/edge template. Knowing exactly which parts are dynamic vs templated is the key to reading the tool honestly.
- Paste sourceRepo text, OpenAPI spec, README, incident report, transcript, or notes go into the studio textarea.
- splitFacts()A sentence-splitting regex pulls candidate facts from the source.
- extractTerms()Stopword-filtered frequency analysis surfaces the key terms.
- titleFromSource()Derives a title for the chosen mode.
- createScene()Merges extracted title/facts/terms/detail into a fixed 8-block pipeline template with mode-keyed edges.
- Render SceneSvgThe scene draws as an interactive SVG; click cards, switch audience and mode.
The foreignObject Card Trick
SVG <text> cannot wrap or clamp, so early cards overflowed when detail ran long (character-width estimation never holds). The fix: draw the border as a <rect>, then put real HTML inside a <foreignObject> and let CSS line-clamp do the work.
src/App.tsx · src/sceneStyles.ts
<rect x={block.x} y={block.y} width={block.w} height={block.h} rx="10" className="card-rect" />
<foreignObject x={block.x} y={block.y} width={block.w} height={block.h}>
<div className="card">
<h3>{block.label}</h3>
<p>{block.detail}</p>
</div>
</foreignObject>
/* sceneStyles.ts — the clamp that keeps text in bounds */
.card p {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}<rect ... /> then <foreignObject>The rect is the visible border; the foreignObject hosts real, wrappable HTML.-webkit-line-clamp: 3CSS clamps detail to 3 lines - the overflow fix that replaced fragile SVG text measuring.styles live in sceneStyles.ts as literalsToken literals (not CSS vars) so the styles can be inlined into the exported SVG.
One Component, Two Outputs (No Drift)
The live studio and the exported file render the same SceneSvg, and the scene’s CSS is injected inside the SVG - so styles travel with the artifact and the export can never visually drift from the app. standaloneHtml serializes a full document with React 19’s renderToStaticMarkup.
src/App.tsx
function StandalonePage({ scene, mode }) {
return (
<html lang="en">
<head><style>{standaloneCss}</style></head>
<body>
<main>
<section className="scene">
<SceneSvg scene={scene} activeMode={mode} staticExport />
</section>
</main>
</body>
</html>
);
}
// standaloneHtml(scene, mode):
// "<!doctype html>" + renderToStaticMarkup(<StandalonePage scene mode/>)the same <SceneSvg> in both pathsLive app and export share one renderer - a single source of truth, so they cannot diverge.styles injected inside the SVGThe scene’s CSS lives in the SVG, so a saved file looks identical with no external stylesheet.renderToStaticMarkup(<StandalonePage/>)react-dom/server is bundled client-side (≈ a deliberate +bundle tradeoff) to serialize the artifact.
An Honest Spike: Shipped vs Planned
The repo does not oversell. Here is the real line between what works today and what is aspirational - the kind of scoping the fleet values.
Where It Sits in the Kitchen
Mise en Scène is independent of Brigade in code, but its demo scene depicts a future where "Brigade runtime" is the backend that runs grounding, model routing, tool calls, and artifact export once the pipeline grows past local heuristics.