Kanzo UI

Job studio

Three pages of one job — a domain language on CodeEditor, empty sockets you drag connections onto, and diagnostics that reach the chrome.

Open full size

The screen where a product's central artefact gets written. Here that artefact is a fossil program — declare prefixes, bind a source, map it — but the shape is generic: a code surface with what it references beside it, then a settings page, then a receipt.

It runs inside the same shell as App shell and Workspace, on the same tenant, from the same INSTANCES / NAV / USER fixtures — a new screen joins the product rather than inventing a second company.

It is the library's first real consumer of CodeEditor's extensions slot. The component is domain-free by design — a CodeMirror lifecycle core, the Kanzo token theme, and a compartment the caller fills — so the language lives in the showcase: a StreamParser whose token names land on the --syntax-* palette, a linter(), and an @-completion over the org's connections. That is the whole answer to "how do I put my language in this editor", and it is about 200 lines.

Worth exercising in the frame:

  • Drag @aemet from the rail onto the dashed socket in line 11. The socket lights up under the pointer and the drop writes "@aemet/daily-2026.csv" — the path that was already there survives. Clicking the socket does the same thing through the editor's own completion popover, and Enter on it works too: a drop is a pointer-only gesture, so it is never the only route.
  • Pick A program with problems from the header. Three squiggles appear, the badge turns red, and Create job goes disabled — one analysis, read in three places.
  • Walk the steps. Editor is the program and the rail of connections, Configure the settings, Summary the graph the program will emit — read back out of the mappings, not restated from the form. The icon at the end of the status strip closes the rail, and it only appears on Editor, because that is the only page with one.
  • Double-click the job's name in the header to rename it in place (Editable, composed with its pencil/submit/cancel EditableControl), and watch the status strip: the draft saves itself.

The socket

A socket is a real token in the document — @? — not a widget floating over nothing. The program stays plain fossil text a compiler could read, the analyser has an opinion about it (an unwired source is an error, so Create stays shut), and the dashed target is only a view decoration.

It carries two ranges, and that is the whole design. What you SEE is the entire "@?/daily-2026.csv", so you drop a source rather than a prefix and the line has one texture instead of three. What gets REWRITTEN is just the two characters, so a path someone already typed survives the drop.

Neither Ark nor Shark ships a drag-and-drop primitive — checked, and their FileUpload dropzones read dataTransfer.files, which is a different problem. So this is the HTML5 API used directly, in about forty lines, with no state machine to wrap. It stays in the showcase: one call site is not the two admission rule 2 asks for.

What it changes from the original

This is a rebuild of a product's job editor, and three of the original's decisions are not carried over.

The original's wizard hides the program. Each step returns a different tree, so by the time you are choosing an output destination the source @stations/… that motivated it is off screen. Here it is two pages of one job, and the connections stay beside the program that references them.

The original's validation never reaches the chrome. Its diagnostics live inside the editor's LSP and nothing else can see them — its validating flag is declared and never set, its "Validating…" branch unreachable, and the only gate on submission is "is the text non-empty". You can create a job the editor has marked red.

Several of the original's pieces are hand-rolled over parts that already exist. The step indicator, the field frame and a radio card written as a bordered <label> are Steps, Field and RadioGroupCard.

The "coming soon" marker is the exception, and it is the more interesting one: this screen composes it here, out of Float and Badge over a relative box, rather than taking Gated. The two options it marks are radios, and Gated makes what it holds inert, which takes it out of the accessibility tree — the radio group would lose an option rather than announce it as unavailable. A disabled radio already says that, so all the marker adds is the reason, pinned beside it.

Three pages

Steps spans the header because each step is a whole page. A full-width Steps band over a single editor page would span the screen while governing one column, promising to command the editor and not doing it — the same reason workspace's dock explains in writing that its switcher is a deselectable ToggleGroup and not Tabs.

The rail holds connections and nothing else. A rail is for what ACCOMPANIES the thing you are editing; settings you visit once and leave are a page. Only the Editor page has a rail, which is why the dock switcher in the status strip appears there and nowhere else.

The header is ONE row, which is what the references do. shadcn's dashboard and sidebar blocks, with Mantine and Ant, put a single strip inside the content column carrying a trigger, a separator and the trail, and no primary actions: those live in the rail or in the content. Here that is the trigger, the name as an Editable, the steps centred in their own grid column, and one secondary split button. Create job sits on the Summary page, beside what it creates.

No breadcrumb either. The sidebar says where you are, and a trail whose leaf is the rename control clips it to 40px of 183px and loses the name — a trail is the part you can infer, and a name is not.

Configure is a settings page, not a form. A narrow column of Cards would be three controls with a great deal of nothing either side. A card around a single control is chrome standing in for content, and metadata-form's measure is earned by thirty fields, not by three. So it takes the shape a settings page actually has: what the setting is on the left, the control on the right, a rule between sections. The destination shows the path it resolves to rather than the rule for building it.

Summary shows what comes OUT. Restating the form — name, run mode, destination, DCAT, sources, three of which you have just typed and one of which is in the title bar — would make it a receipt, not a review. It reads the mappings: each class, the source it is minted from, the subject template, and every predicate with where its value comes from. metadata-form's third column shows generated Turtle and JSON-LD for the same reason.

Nothing hijacks ⌘+S either. The draft saves itself and the status strip says so, which is a truer thing for a status strip to say than a shortcut announced in a permanent segment of the toolbar. And there is no appearance control in the header either: appearance is a card in PreferencesPanel's Colour section, and one control per preference is the rule.

Source

docs/showcases/job-studio/
├── default.tsx           — the shell, the two pages, the status strip
├── panels.tsx            — the connections rail, the configure form, the summary
├── connection-slots.ts   — the socket: decoration, drag payload, drop handling
├── fossil-lang.ts        — the language: StreamParser, linter, @-completion
└── data.tsx              — one org's connections and a few programs to open