Skip to content

Commit 84b08da

Browse files
os-zhuangclaude
andauthored
docs(builder-ui): document Automation/Interface as built + design Access/Settings (#2506) (#2511)
Complete the living builder-ui design doc. Writeback the Data/shell sections to the shipped StudioDesignSurface reality (pencil edit affordance vs. header-click-sorts; three-pillars-first; Save/Publish placement; Interfaces nav tree), and replace the §5 "to design" stub with four real sections — Automation and Interface documented as-built, Access and Settings designed — each with an annotated shell.css mockup (data-build / data-reuse, dashed = reused). Addresses #2506. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent cabce27 commit 84b08da

6 files changed

Lines changed: 662 additions & 19 deletions

File tree

docs/design/builder-ui.md

Lines changed: 167 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ visual target and, because its DOM maps to the component tree and its regions ar
1616
tagged `data-build` / `data-reuse`, the implementation blueprint (see
1717
[Mockups](#mockups)). The ASCII sketches inline below are for quick reading.
1818

19+
> **Implementation status (2026-07).** The builder is live in the console as
20+
> `StudioDesignSurface` (routed at `/studio/:packageId/:tab`). Shipping **today**:
21+
> three pillars — **Data · Automation · Interface** — each composed around existing
22+
> renderers, plus draft → publish. **Not yet built:** the **Access** and **Settings**
23+
> pillars (designed below, §7–§8), and part of the Data pillar's own v1 scope (the
24+
> `Fields` form designer, the `Validations` rules list, object `Settings`, and the
25+
> left-rail search / New object — §4). Where the built UI deliberately improved on an
26+
> earlier sketch, this doc has been updated to the built shape (noted inline). Each
27+
> per-pillar surface below is marked **[built]** or **[designed]**.
28+
1929
---
2030

2131
## 1. Core constraints — AI-first, human-confirmable, reuse-first
@@ -87,7 +97,9 @@ Mockup: [`builder-ui/data-pillar.html`](./builder-ui/data-pillar.html) (open in
8797

8898
- **Top bar** — the four pillar tabs (Data / Automation / Interface / Access), the
8999
⚙ Settings entry, the app switcher, and the draft indicator + Save draft /
90-
Publish (draft-gated, ADR-0033).
100+
Publish (draft-gated, ADR-0033). *(Built today: three tabs — Access and ⚙ pending;
101+
the package name stands in for the app switcher; Save draft / Publish currently
102+
render per-pillar rather than in the top bar. Target remains as drawn.)*
91103
- **Left rail** — the current pillar's primary entities (objects / automations /
92104
surfaces / roles), with search and New. Collapsible.
93105
- **Main** — the pillar's primary work surface, shaped to the pillar (Data = grid,
@@ -169,9 +181,12 @@ and both configure a selected field through the *same* right-hand **field editor
169181

170182
- **`Records` — grid / list style (data-forward).** The functional grid: columns =
171183
fields, rows = real records. Preview and inline-edit data, `+` add a column
172-
(= add a field), select a column header to configure that field's properties in
173-
the inspector, `+ New record`. Ephemeral filter / sort / hide / group for looking
174-
at the data (not saved — saved views are Interface).
184+
(= add a field), select a column's **edit (pencil) affordance** to configure that
185+
field's properties in the inspector, `+ New record`. Ephemeral filter / sort / hide
186+
/ group for looking at the data (not saved — saved views are Interface). *(A bare
187+
header click **sorts** — the grid is the runtime `ListView`, so the header's native
188+
gesture wins; field config is opened by the per-column pencil, not the header. This
189+
is the built behaviour, corrected from the earlier "click the header" sketch.)*
175190
- **`Fields` — form style (layout-forward).** The field designer as a form canvas:
176191
drag to reorder fields, group them into sections, and configure field properties.
177192
No data rows — this is where the object's default field layout (order + grouping)
@@ -225,24 +240,152 @@ union of six declarative rule types (`packages/spec/src/data/validation.zod.ts`)
225240
form-style designer (reorder + grouping + field editor, incl. field-level
226241
validation) · `Validations` (condition builder) · object `Settings` (label / icon /
227242
name field / compact / search). Relationships via the `lookup` field type.
243+
- **Built so far** (2026-07): owned-objects rail · `Records` grid with inline data,
244+
add-column / add-field, drag-reorder columns, and the reused object/field inspector
245+
(field-level required · unique · options · default · help). Draft → publish wired.
246+
- **Still to build** (v1 gap — the facet tabs never landed): left-rail **search + New
247+
object** · the `Fields` **form-style layout designer** · the `Validations` **rules
248+
list + condition builder** · the object **`Settings`** facet. These are the
249+
Phase-B build (tracked against this doc); each reuses an existing Studio panel.
228250
- **Defer**: Extended objects (objectExtensions) · External objects (datasources) ·
229251
the ERD / model view · formal `Lifecycle` (v1 status = a select field) · `Indexes` ·
230252
seed-data UI · saved views / kanban / calendar (presentation → Interface).
231253

232254
---
233255

234-
## 5. Other pillars (to design — same shell)
256+
## 5. Automation pillar · **[built]**
257+
258+
Automation is the **behaviour workbench**: the package's flows, each edited on the
259+
same flow **canvas** the runtime engine executes. Nothing here is a new editor — the
260+
canvas and node config are the existing Studio flow builder, dropped into the shell.
261+
262+
Mockup: [`builder-ui/automation-pillar.html`](./builder-ui/automation-pillar.html).
263+
264+
```
265+
┌ flows ────┬ 可视化编排 · flow · showcase_task_completed ──────────┬ inspector ┐
266+
│ Notify… ◄ │ Trigger: autolaunched · draft · Run as: user · v1 │ Node │
267+
│ Reassign │ [Variables] [Runs] [Problems] [Debug] + 添加节点 │ config │
268+
│ Approval │ ┌ On Task Update (START) ┐ │ │
269+
│ Digest… │ └──────────┬─────────────┘ │ [reused │
270+
│ + … │ ┌ Send Completion Email (SCRIPT) ┐ → End │ node form]│
271+
└───────────┴──────────────────────────────────────────────────────┴────────────┘
272+
```
273+
274+
- **Left rail** — the package's flows. *Built today: a **flat list** (`client.list('flow')`).
275+
The design target is **grouped by trigger** (record-change / scheduled / API / manual) —
276+
that grouping is a pending refinement, not yet applied.*
277+
- **Main** — the flow **canvas** = reused `FlowCanvas` (the Studio flow builder). Top
278+
strip: Trigger · Status · Run as · version, plus **Variables · Runs · Problems ·
279+
Debug** and add-node / zoom. Flows land **default OFF — review-then-enable**.
280+
- **Right inspector** — the selected node's config, reused (`getMetadataInspector('flow')`).
281+
- **Runs** — a canvas-strip toggle showing execution history *inside the builder*. This
282+
is the read-only peek that reconciles [ADR-0084](../adr/0084-application-builder-information-architecture.md)
283+
("flow runs live in **Operate**, out of the builder"): the builder gets a glance;
284+
the full run inspector stays in Operate.
285+
- **Build boundary** — rail + shell = built; **canvas = reused `FlowCanvas`**;
286+
**inspector = reused node config**.
287+
288+
---
289+
290+
## 6. Interface pillar · **[built]**
291+
292+
Interface is the **presentation workbench**: the app's surfaces (pages · dashboards ·
293+
reports · views), each rendered by the **same renderer the end user gets** — edit the
294+
live artifact, not a proxy.
295+
296+
Mockup: [`builder-ui/interface-pillar.html`](./builder-ui/interface-pillar.html).
297+
298+
```
299+
┌ app nav ─┬ 预览即运行 · page · showcase_start_here ────────┬ inspector ┐
300+
│ Workspace│ ┌ source (JSX) ─────┐ ┌ live preview ─────────┐ │ Component │
301+
│ Start ◄ │ │ <flex dir="col"> │ │ Pick the right page │ │ props │
302+
│ Data │ │ <div …>Showcase │ │ authoring model │ │ │
303+
│ Tasks │ │ … │ │ [Decision tree] │ │ [reused │
304+
│ Analytics│ └───────────────────┘ └────────────────────────┘ │ props] │
305+
└──────────┴─────────────────────────────────────────────────┴────────────┘
306+
```
307+
308+
- **Left rail** — the app's **real navigation tree** (groups + typed leaves:
309+
page / object / dashboard / report / view), read from the App metadata. *This
310+
**supersedes** the earlier "Apps · Pages · Views · Dashboards · Reports" by-type
311+
sketch: the built rail shows the app's actual IA (what a builder edits), which is
312+
truer than a type index. A by-type filter can be a later lens.*
313+
- **Main** — two modes, auto-selected by the surface:
314+
- **source + live preview** for react / html pages — a code editor beside a live
315+
`SchemaRenderer` ("预览即运行 · 同一渲染器" = preview *is* runtime; ADR-0080/0081).
316+
- **structured canvas** for dashboards / slotted pages — widget blocks with
317+
`+ add widget`, edited by selecting a block.
318+
- **Right inspector** — component / block props, reused.
319+
- **Build boundary** — rail (`NavTree` / `AppNavCanvas`) = built; **main = reused
320+
`SchemaRenderer` (react/html) or the dashboard/page renderer (structured)**;
321+
**inspector = reused component props**.
322+
323+
---
324+
325+
## 7. Access pillar · **[designed]**
326+
327+
Access is the **permission workbench**: who can do what to which object. Its natural
328+
shape is a **matrix** (roles × objects × CRUD + record scope), which already exists as
329+
a Studio panel — so the pillar is that matrix dropped into the shell, nothing new.
235330

236-
- **Automation** — left: automations grouped by trigger (record-change / scheduled /
237-
API / manual action); main: the flow **canvas**; inspector: the selected node's
238-
config; a Runs view for execution history.
239-
- **Interface** — left: surfaces (Apps · Pages · Views · Dashboards · Reports);
240-
main: the page **builder** (structured canvas) or **source + preview** (html/react
241-
pages); inspector: component props.
242-
- **Access** — left: roles; main: the permission **matrix** (objects × CRUD + record
243-
scope); inspector: role / field-level detail.
244-
- **Settings** (⚙) — General (app basic info) and Advanced (Code: hooks · functions;
245-
Connections: datasources), grouped by audience.
331+
Mockup: [`builder-ui/access-pillar.html`](./builder-ui/access-pillar.html).
332+
333+
```
334+
┌ roles ───┬ Permission matrix · role: Sales Rep ──────────────────┬ inspector ┐
335+
│ Admin │ Create Read Update Delete Record scope │ Role │
336+
│ Sales ◄ │ Account ☑ ☑ ☑ ☐ Owned + team │ detail │
337+
│ Support │ Contact ☑ ☑ ☑ ☐ Owned │ │
338+
│ Viewer │ Invoice ☐ ☑ ☐ ☐ All │ [reused │
339+
│ + New │ Task ☑ ☑ ☑ ☑ Owned │ role form]│
340+
└──────────┴───────────────────────────────────────────────────────┴────────────┘
341+
```
342+
343+
- **Left rail** — roles (and permission sets), with search + New role.
344+
- **Main** — the permission **matrix** = reused `PermissionMatrixEditor`: rows =
345+
objects, columns = **C / R / U / D** plus a **record-scope** column (owned / team /
346+
all). Toggling a cell edits the role's object-permission metadata as a draft.
347+
- **Right inspector** — the selected role (or object-permission) detail = reused
348+
protocol form for `role`, with `RolePreview` / `PermissionPreview` for a live "what
349+
this role can see" read-out.
350+
- **Build boundary** — rail + shell = built; **matrix = reused `PermissionMatrixEditor`**;
351+
**inspector = reused `role` protocol form + `RolePreview`/`PermissionPreview`**.
352+
- **v1 scope** — roles × objects **CRUD + record scope**. **Defer**: the field-level
353+
permission matrix, sharing rules, and capability / permission-set *composition*
354+
(the "concept overload" risk — v1 stays at roles × objects × CRUD, one clear grid).
355+
356+
---
357+
358+
## 8. Settings (⚙) · **[designed]**
359+
360+
Settings is **not** a content pillar — it is the app's own configuration, split by
361+
**audience** (ADR-0084 §5): a builder opening Settings for app info must not land in
362+
code. Every panel is a reused protocol form; the area only composes them.
363+
364+
Mockup: [`builder-ui/settings.html`](./builder-ui/settings.html).
365+
366+
```
367+
┌ ⚙ Settings ─────────────────────────────────────────────────────────────────┐
368+
│ General │ the app's identity & defaults [reused app form] │
369+
│ · Basics │ name · id · icon · description · branding │
370+
│ · Navigation │ default landing · nav structure │
371+
│ ───────────────── │ │
372+
│ Advanced (devs) │ Code Hooks (v1) · functions · components (later) │
373+
│ · Code │ [reused hooks list + hook form] │
374+
│ · Connections │ Connections datasource · connector · webhook (later) │
375+
└──────────────────────────────────────────────────────────────────────────────┘
376+
```
377+
378+
- **General** — the app's identity and defaults: name · id · icon · description ·
379+
branding · default landing · navigation structure. This is the `app` definition →
380+
reused **`app` protocol form**.
381+
- **Advanced** — the technical tier, kept visually separate from General so a builder
382+
isn't dropped into code. Sub-grouped **Code** (v1: **Hooks** — a hooks list + reused
383+
hook protocol form; later: functions, custom components, custom field types) and
384+
**Connections** (later: datasources, connectors, webhooks, mappings).
385+
- **Build boundary** — the ⚙ area chrome + audience grouping = built; **every panel =
386+
reused protocol form** (`app`, `hook`, …).
387+
- **v1 scope****General + Advanced/Hooks only** (ADR-0084 §7). The rest of Advanced
388+
is deferred.
246389

247390
---
248391

@@ -265,8 +408,13 @@ still to design:
265408

266409
Open any `*.html` in a browser to view it. Current mockups:
267410

268-
| Surface | Mockup |
269-
|---|---|
270-
| Data pillar | [`data-pillar.html`](./builder-ui/data-pillar.html) |
411+
| Surface | Mockup | Status |
412+
|---|---|---|
413+
| Data pillar | [`data-pillar.html`](./builder-ui/data-pillar.html) | built (partial — see §4) |
414+
| Automation pillar | [`automation-pillar.html`](./builder-ui/automation-pillar.html) | built |
415+
| Interface pillar | [`interface-pillar.html`](./builder-ui/interface-pillar.html) | built |
416+
| Access pillar | [`access-pillar.html`](./builder-ui/access-pillar.html) | designed |
417+
| Settings (⚙) | [`settings.html`](./builder-ui/settings.html) | designed |
271418

272-
More are added per pillar as they are designed.
419+
The **built** mockups depict the shipped `StudioDesignSurface` (2026-07); the
420+
**designed** ones are the target for the not-yet-built pillars (§7–§8).
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Builder — Access pillar</title>
6+
<link rel="stylesheet" href="shell.css">
7+
</head>
8+
<body>
9+
<!-- Access pillar [designed]: who can do what to which object. Natural shape = matrix
10+
(roles x objects x CRUD + record scope). Role "Sales Rep" selected -> role detail.
11+
data-build="shell" = built in the builder (composition/chrome)
12+
data-reuse="<comp>" = existing Studio/objectui component, dropped in unchanged -->
13+
<div class="builder">
14+
15+
<!-- ===== top bar (built) ===== -->
16+
<div class="topbar" data-build="shell">
17+
<span class="app">app &#9662;</span>
18+
<nav class="pilltabs">
19+
<a href="#">Data</a>
20+
<a href="#">Automation</a>
21+
<a href="#">Interface</a>
22+
<a class="active" href="#">Access</a>
23+
</nav>
24+
<div class="right">
25+
<span>&#9881;</span>
26+
<span>Save draft</span>
27+
<button class="btn-publish">Publish</button>
28+
</div>
29+
</div>
30+
31+
<div class="body">
32+
33+
<!-- ===== left rail: roles (built) ===== -->
34+
<aside class="rail" data-build="shell">
35+
<div class="label">ROLES</div>
36+
<div class="search">Search</div>
37+
<ul>
38+
<li>Admin</li>
39+
<li class="active">Sales Rep</li>
40+
<li>Support</li>
41+
<li>Viewer</li>
42+
</ul>
43+
<div class="new">+ New role</div>
44+
</aside>
45+
46+
<!-- ===== main: permission matrix ===== -->
47+
<main class="main">
48+
<div class="toolbar" data-build="shell">
49+
<span>Permission matrix</span>
50+
<span>role &middot; Sales Rep</span>
51+
</div>
52+
<div class="workarea">
53+
<div class="reuse" data-reuse="PermissionMatrixEditor">
54+
<table class="matrix">
55+
<thead>
56+
<tr><th>Object</th><th>Create</th><th>Read</th><th>Update</th><th>Delete</th><th>Record scope</th></tr>
57+
</thead>
58+
<tbody>
59+
<tr><td>Account</td><td><span class="ck on"></span></td><td><span class="ck on"></span></td><td><span class="ck on"></span></td><td><span class="ck"></span></td><td><span class="scope">Owned + team</span></td></tr>
60+
<tr><td>Contact</td><td><span class="ck on"></span></td><td><span class="ck on"></span></td><td><span class="ck on"></span></td><td><span class="ck"></span></td><td><span class="scope">Owned</span></td></tr>
61+
<tr><td>Invoice</td><td><span class="ck"></span></td><td><span class="ck on"></span></td><td><span class="ck"></span></td><td><span class="ck"></span></td><td><span class="scope">All</span></td></tr>
62+
<tr><td>Task</td><td><span class="ck on"></span></td><td><span class="ck on"></span></td><td><span class="ck on"></span></td><td><span class="ck on"></span></td><td><span class="scope">Owned</span></td></tr>
63+
</tbody>
64+
</table>
65+
<div class="grid-add">Toggle a cell to edit this role's object permission (draft)</div>
66+
<span class="reuse-tag">matrix = existing PermissionMatrixEditor (reused)</span>
67+
</div>
68+
</div>
69+
</main>
70+
71+
<!-- ===== right inspector: role detail = reused form + preview ===== -->
72+
<aside class="inspector" data-build="shell">
73+
<div class="eyebrow">ROLE</div>
74+
<h3>Sales Rep</h3>
75+
<hr>
76+
<div class="reuse form" data-reuse="protocol-form:role">
77+
<div class="flabel">Label</div>
78+
<div class="field">Sales Rep</div>
79+
<div class="flabel">API name</div>
80+
<div class="field">sales_rep</div>
81+
<div class="flabel">Description</div>
82+
<div class="field">Front-line sellers</div>
83+
<div class="flabel">Preview &mdash; can see</div>
84+
<div class="chips">
85+
<span class="chip">Accounts</span>
86+
<span class="chip">Contacts</span>
87+
<span class="chip">Own tasks</span>
88+
</div>
89+
<span class="reuse-tag">role form + RolePreview (reused)</span>
90+
</div>
91+
</aside>
92+
</div>
93+
94+
<!-- ===== legend ===== -->
95+
<div class="legend">
96+
<span><span class="sw built"></span>solid = shell / composition (built in the builder)</span>
97+
<span><span class="sw reused"></span>dashed = existing Studio component (reused, not rebuilt)</span>
98+
</div>
99+
</div>
100+
</body>
101+
</html>

0 commit comments

Comments
 (0)