@adia-ai/web-modules 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/chat/chat-shell/chat-shell.js +28 -40
  3. package/chat/chat-shell/css/chat-shell.empty.css +3 -3
  4. package/chat/chat-shell/css/chat-shell.layout.css +2 -2
  5. package/editor/editor-canvas/editor-canvas.examples.html +65 -0
  6. package/editor/editor-canvas/editor-canvas.html +43 -0
  7. package/editor/editor-canvas-empty/editor-canvas-empty.examples.html +65 -0
  8. package/editor/editor-canvas-empty/editor-canvas-empty.html +42 -0
  9. package/editor/editor-shell/css/editor-shell.bespoke.css +67 -2
  10. package/editor/editor-shell/css/editor-shell.layout.css +6 -6
  11. package/editor/editor-shell/editor-shell.js +19 -17
  12. package/editor/editor-sidebar/editor-sidebar.a2ui.json +5 -0
  13. package/editor/editor-sidebar/editor-sidebar.examples.html +65 -0
  14. package/editor/editor-sidebar/editor-sidebar.html +43 -0
  15. package/editor/editor-sidebar/editor-sidebar.js +30 -6
  16. package/editor/editor-sidebar/editor-sidebar.test.js +19 -0
  17. package/editor/editor-sidebar/editor-sidebar.yaml +8 -0
  18. package/editor/editor-statusbar/editor-statusbar.examples.html +65 -0
  19. package/editor/editor-statusbar/editor-statusbar.html +42 -0
  20. package/editor/editor-toolbar/editor-toolbar.examples.html +65 -0
  21. package/editor/editor-toolbar/editor-toolbar.html +43 -0
  22. package/package.json +4 -4
  23. package/shell/admin-shell/admin-shell.js +27 -243
  24. package/shell/admin-shell/css/admin-shell.bespoke.css +22 -26
  25. package/shell/admin-shell/css/admin-shell.main.css +2 -2
  26. package/shell/admin-shell/css/admin-shell.shell.css +2 -2
  27. package/shell/admin-shell/css/admin-shell.sidebar.css +35 -33
package/CHANGELOG.md CHANGED
@@ -11,6 +11,35 @@ Built from `@adia-ai/web-components` primitives.
11
11
 
12
12
  _No pending changes._
13
13
 
14
+ ## [0.4.0] - 2026-05-10
15
+
16
+ **⚠️ BREAKING** — first non-patch release in the 0.x line. Closes the ADR-0023 arc per [ADR-0024](../../.brain/adrs/0024-legacy-shell-shapes-retired.md). All 6 in-repo consumers migrated in v0.3.6; this release retires the legacy authoring shapes from the shell hosts.
17
+
18
+ ### Removed (BREAKING)
19
+
20
+ - **`<admin-shell>`** no longer reads `<aside-ui slot="leading|trailing">`, `<aside data-sidebar>`, `<dialog data-command>`, or `[data-resize]` external handle children. Use `<admin-sidebar slot resizable collapsible>` + `<admin-command>` instead.
21
+ - **`<chat-shell>`** no longer reads `<section data-chat-messages>`, `<chat-input-ui data-chat-input>`, `<empty-state-ui data-chat-empty>`, `<header>` with `[data-chat-name]` / `[data-chat-status]`. Use `<chat-thread>` + `<chat-composer>` + `<chat-empty>` + `<chat-header>` + `<chat-status>` instead.
22
+ - **`<editor-shell>`** no longer reads `<header>` toolbar, `<div data-editor-body>` wrapper, `<pane-ui data-left|data-right>`, `<div data-canvas>`, `<footer>` statusbar. Use `<editor-toolbar>` + `<editor-canvas>` + `<editor-sidebar slot>` + `<editor-statusbar>` + `<editor-canvas-empty>` instead.
23
+
24
+ ### Changed
25
+
26
+ - **`admin-shell.js` host** — 305 → 87 LOC (-71%). The host now does only `[mode]` reflection + 2 attribute-forwarding handlers. All resize / collapse / persistence / dialog wiring is owned by `<admin-sidebar>` and `<admin-command>` per ADR-0023.
27
+ - **`chat-shell.js` host** — `connected()` simplified; only `composer-submit` event recognized (no longer falls back to `submit`).
28
+ - **`editor-shell.js` host** — `connected()` drops dual-shape fallbacks.
29
+ - **CSS layered files** — all `:is(legacy, bespoke)` lifts collapsed to bespoke-only in `admin-shell.sidebar.css`, `admin-shell.shell.css`, `admin-shell.main.css`, `chat-shell.empty.css`, `chat-shell.layout.css`, `editor-shell.layout.css`.
30
+ - **CSS bridge files** — `admin-shell.bespoke.css` / `chat-shell.bespoke.css` / `editor-shell.bespoke.css` updated to canonical-styling commentary (no longer "bridge to existing selectors").
31
+
32
+ ### Verification
33
+
34
+ - Bespoke children unit tests: **78/78** pass (admin 19 + chat 27 + editor 32)
35
+ - `npm run smoke:consumers` Playwright probe: **6/6** pass
36
+ - `npm run build:site`: clean
37
+ - `check-links --all`: 0 broken
38
+
39
+ ### Migration
40
+
41
+ See root [CHANGELOG.md `## [0.4.0]`](../../CHANGELOG.md) for the full diff-style migration recipe. The 6 in-repo consumers (`apps/app-shell/`, `site/index.html`, `apps/chat/`, `apps/genui/gen-ui/`, `apps/construct-canvas/`, `apps/genui/a2ui-editor/`) were all migrated in v0.3.6 — see commit `c477d371`.
42
+
14
43
  ## [0.3.6] - 2026-05-10
15
44
 
16
45
  ### Added
@@ -15,20 +15,22 @@ let msgId = 0;
15
15
  * Behavior-only orchestrator: stamps no HTML of its own.
16
16
  * The author writes the structure; chat-shell wires the behaviors.
17
17
  *
18
- * Structure:
18
+ * Structure (bespoke vocabulary per ADR-0023; **bespoke-only since v0.4.0**):
19
19
  * <chat-shell proxy-url="/api/chat" model="claude-sonnet-4-20250514">
20
- * <header>
21
- * <span data-chat-name>Claude</span>
22
- * <span data-chat-status></span>
23
- * </header>
24
- * <section data-chat-messages>
25
- * <empty-state-ui data-chat-empty icon="chat-circle"
26
- * heading="Hello!" description="Ask me anything.">
27
- * </empty-state-ui>
28
- * </section>
29
- * <footer>
30
- * <chat-input-ui data-chat-input placeholder="Message..."></chat-input-ui>
31
- * </footer>
20
+ * <chat-header>
21
+ * <span slot="name">Claude</span>
22
+ * <chat-status slot="status"></chat-status>
23
+ * </chat-header>
24
+ * <chat-thread>
25
+ * <chat-empty>
26
+ * <empty-state-ui icon="chat-circle"
27
+ * heading="Hello!" description="Ask me anything.">
28
+ * </empty-state-ui>
29
+ * </chat-empty>
30
+ * </chat-thread>
31
+ * <chat-composer>
32
+ * <chat-input-ui placeholder="Message..."></chat-input-ui>
33
+ * </chat-composer>
32
34
  * </chat-shell>
33
35
  *
34
36
  * Two modes:
@@ -79,36 +81,22 @@ class ChatShell extends UIElement {
79
81
  // ── Lifecycle ──
80
82
 
81
83
  connected() {
82
- // Per ADR-0023 read BOTH legacy (data-* / raw HTML) AND bespoke
83
- // (chat-thread / chat-composer / chat-empty / chat-status) shapes.
84
- // The :is() selectors let consumers mix shapes without breakage.
85
- this.#messagesEl = this.querySelector('chat-thread')
86
- || this.querySelector('[data-chat-messages]')
87
- || this.querySelector('section');
88
- this.#inputEl = this.querySelector('chat-composer')
89
- || this.querySelector('[data-chat-input]')
90
- || this.querySelector('chat-input-ui');
91
- this.#emptyEl = this.querySelector('chat-empty')
92
- || this.querySelector('[data-chat-empty]');
93
- this.#statusEl = this.querySelector('chat-status')
94
- || this.querySelector('[data-chat-status]');
95
-
96
- // Bespoke <chat-composer> emits 'composer-submit' instead of 'submit'
97
- // (so the legacy 'submit' event from inside <chat-input-ui> doesn't
98
- // double-fire). Listen for both depending on which shape is present.
99
- if (this.#inputEl?.tagName?.toLowerCase() === 'chat-composer') {
100
- this.#inputEl.addEventListener('composer-submit', this.#onSubmit);
101
- } else {
102
- this.#inputEl?.addEventListener('submit', this.#onSubmit);
103
- }
84
+ // **Bespoke-only since v0.4.0** (ADR-0023 Phase 3). The legacy
85
+ // shapes (<section data-chat-messages>, <chat-input-ui data-chat-input>,
86
+ // <empty-state-ui data-chat-empty>, [data-chat-status>, [data-chat-name>)
87
+ // are no longer recognized. Consumers MUST use the bespoke vocabulary.
88
+ this.#messagesEl = this.querySelector('chat-thread');
89
+ this.#inputEl = this.querySelector('chat-composer');
90
+ this.#emptyEl = this.querySelector('chat-empty');
91
+ this.#statusEl = this.querySelector('chat-status');
92
+
93
+ // <chat-composer> forwards inner <chat-input-ui> submit as
94
+ // composer-submit (bespoke event name prevents double-fire).
95
+ this.#inputEl?.addEventListener('composer-submit', this.#onSubmit);
104
96
  }
105
97
 
106
98
  disconnected() {
107
- if (this.#inputEl?.tagName?.toLowerCase() === 'chat-composer') {
108
- this.#inputEl.removeEventListener('composer-submit', this.#onSubmit);
109
- } else {
110
- this.#inputEl?.removeEventListener('submit', this.#onSubmit);
111
- }
99
+ this.#inputEl?.removeEventListener('composer-submit', this.#onSubmit);
112
100
  this.abort();
113
101
  }
114
102
 
@@ -2,11 +2,11 @@
2
2
  chat-shell — Empty state
3
3
  ═══════════════════════════════════════════════════════════════ */
4
4
 
5
- chat-shell [data-chat-empty] {
5
+ chat-shell chat-empty {
6
6
  margin: auto;
7
7
  }
8
8
 
9
- chat-shell[streaming] [data-chat-empty],
10
- chat-shell:has([data-role]) [data-chat-empty] {
9
+ chat-shell[streaming] chat-empty,
10
+ chat-shell:has([data-role]) chat-empty {
11
11
  display: none;
12
12
  }
@@ -29,14 +29,14 @@ chat-shell > header [data-chat-name] {
29
29
  font-size: var(--chat-header-name-font);
30
30
  }
31
31
 
32
- chat-shell > header [data-chat-status] {
32
+ chat-shell > header chat-status {
33
33
  font-size: var(--chat-header-status-font);
34
34
  color: var(--chat-header-status-fg);
35
35
  margin-inline-start: auto;
36
36
  }
37
37
 
38
38
  /* Messages scroll container */
39
- chat-shell > [data-chat-messages],
39
+ chat-shell > chat-thread,
40
40
  chat-shell > section {
41
41
  flex: 1;
42
42
  overflow-y: auto;
@@ -0,0 +1,65 @@
1
+ <header>
2
+ <div>
3
+ <h1>Editor Canvas</h1>
4
+ <div data-actions>
5
+ <tag-ui size="sm">editor-canvas</tag-ui>
6
+ <tag-ui size="sm" variant="ghost">JS-bearing</tag-ui>
7
+ </div>
8
+ </div>
9
+ <p>Module-tier editor canvas surface — replaces legacy <div data-canvas>. Owns scroll/zoom container, [empty] + [focused] reflected.</p>
10
+ </header>
11
+
12
+ <section data-section>
13
+ <h2 variant="section">Role</h2>
14
+ <p>Per <a href="../../../../.brain/adrs/0023-bespoke-shell-tier-children.md">ADR-0023</a>, the editor cluster's bespoke family — <code>&lt;editor-shell&gt;</code> (host), <code>&lt;editor-toolbar&gt;</code>, <code>&lt;editor-canvas&gt;</code>, <code>&lt;editor-sidebar&gt;</code> (JS-bearing) + <code>&lt;editor-statusbar&gt;</code>, <code>&lt;editor-canvas-empty&gt;</code> (CSS-only). Confirms the family pattern is canonical across 3 archetypes (admin/chat/editor).</p>
15
+ </section>
16
+
17
+ <section data-section>
18
+ <h2 variant="section">Composition</h2>
19
+ <p>Typical placement inside <code>&lt;editor-shell&gt;</code>:</p>
20
+ <code-ui language="html">&lt;editor-shell&gt;
21
+ &lt;editor-toolbar&gt;
22
+ &lt;span slot="title"&gt;Untitled.fig&lt;/span&gt;
23
+ &lt;button-ui slot="action" icon="gear"&gt;&lt;/button-ui&gt;
24
+ &lt;/editor-toolbar&gt;
25
+
26
+ &lt;editor-sidebar slot="leading" collapsible&gt;
27
+ &lt;pane-ui resizable&gt;
28
+ &lt;header&gt;Navigator&lt;/header&gt;
29
+ &lt;section&gt;…layers…&lt;/section&gt;
30
+ &lt;/pane-ui&gt;
31
+ &lt;/editor-sidebar&gt;
32
+
33
+ &lt;editor-canvas&gt;
34
+ &lt;editor-canvas-empty&gt;
35
+ &lt;empty-state-ui icon="square" heading="New document" description="Drop content to begin."&gt;&lt;/empty-state-ui&gt;
36
+ &lt;/editor-canvas-empty&gt;
37
+ &lt;/editor-canvas&gt;
38
+
39
+ &lt;editor-sidebar slot="trailing" collapsible&gt;
40
+ &lt;pane-ui resizable&gt;
41
+ &lt;header&gt;Inspector&lt;/header&gt;
42
+ &lt;section&gt;…properties…&lt;/section&gt;
43
+ &lt;/pane-ui&gt;
44
+ &lt;/editor-sidebar&gt;
45
+
46
+ &lt;editor-statusbar&gt;
47
+ &lt;span slot="status"&gt;Saved&lt;/span&gt;
48
+ &lt;span slot="zoom"&gt;100%&lt;/span&gt;
49
+ &lt;/editor-statusbar&gt;
50
+ &lt;/editor-shell&gt;</code-ui>
51
+ </section>
52
+
53
+ <section data-section>
54
+ <h2 variant="section">State as attribute</h2>
55
+ <code-ui language="css">/* Hide all chrome in focus mode */
56
+ editor-shell[focus-mode] :is(editor-toolbar, editor-statusbar, editor-sidebar) {
57
+ display: none;
58
+ }
59
+
60
+ /* Visual treatment for empty canvas */
61
+ editor-canvas[empty] { /* placeholder UI */ }
62
+
63
+ /* Highlight focused canvas */
64
+ editor-canvas[focused] { outline: 2px solid var(--a-accent); }</code-ui>
65
+ </section>
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="auto">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Editor Canvas — AdiaUI</title>
7
+
8
+ <link rel="stylesheet" href="../../../web-components/styles/resets.css">
9
+ <link rel="stylesheet" href="../../../web-components/styles/tokens.css">
10
+ <link rel="stylesheet" href="../editor-shell/editor-shell.css">
11
+ <link rel="stylesheet" href="../../../web-components/components/code/code.css">
12
+ <link rel="stylesheet" href="../../../web-components/components/tag/tag.css">
13
+
14
+ <script type="module" src="../editor-shell/editor-shell.js"></script>
15
+ <script type="module" src="./editor-canvas.js"></script>
16
+ <script type="module" src="../../../web-components/components/code/code.js"></script>
17
+ <script type="module" src="../../../web-components/components/tag/tag.js"></script>
18
+
19
+ <style>
20
+ :where(html, body) { margin: 0; min-height: 100vh; background: var(--a-bg); color: var(--a-fg); font-family: var(--a-font); }
21
+ main { max-width: 960px; margin-inline: auto; padding: var(--a-space-6) var(--a-space-5); }
22
+ </style>
23
+ </head>
24
+ <body>
25
+
26
+ <main id="demo-root">
27
+ <p>Loading examples…</p>
28
+ </main>
29
+
30
+ <script type="module">
31
+ const root = document.getElementById('demo-root');
32
+ try {
33
+ const res = await fetch('./editor-canvas.examples.html');
34
+ if (!res.ok) throw new Error(`fetch failed (${res.status})`);
35
+ root.innerHTML = await res.text();
36
+ } catch (err) {
37
+ root.innerHTML = `<p style="color:var(--a-danger-strong);">Failed to load editor-canvas.examples.html — ${err.message}</p>`;
38
+ console.error('[editor-canvas.html]', err);
39
+ }
40
+ </script>
41
+
42
+ </body>
43
+ </html>
@@ -0,0 +1,65 @@
1
+ <header>
2
+ <div>
3
+ <h1>Editor Canvas Empty</h1>
4
+ <div data-actions>
5
+ <tag-ui size="sm">editor-canvas-empty</tag-ui>
6
+ <tag-ui size="sm" variant="ghost">CSS-only</tag-ui>
7
+ </div>
8
+ </div>
9
+ <p>Module-tier editor canvas empty state. CSS-only. Visibility driven by parent <editor-canvas>[empty].</p>
10
+ </header>
11
+
12
+ <section data-section>
13
+ <h2 variant="section">Role</h2>
14
+ <p>Per <a href="../../../../.brain/adrs/0023-bespoke-shell-tier-children.md">ADR-0023</a>, the editor cluster's bespoke family — <code>&lt;editor-shell&gt;</code> (host), <code>&lt;editor-toolbar&gt;</code>, <code>&lt;editor-canvas&gt;</code>, <code>&lt;editor-sidebar&gt;</code> (JS-bearing) + <code>&lt;editor-statusbar&gt;</code>, <code>&lt;editor-canvas-empty&gt;</code> (CSS-only). Confirms the family pattern is canonical across 3 archetypes (admin/chat/editor).</p>
15
+ </section>
16
+
17
+ <section data-section>
18
+ <h2 variant="section">Composition</h2>
19
+ <p>Typical placement inside <code>&lt;editor-shell&gt;</code>:</p>
20
+ <code-ui language="html">&lt;editor-shell&gt;
21
+ &lt;editor-toolbar&gt;
22
+ &lt;span slot="title"&gt;Untitled.fig&lt;/span&gt;
23
+ &lt;button-ui slot="action" icon="gear"&gt;&lt;/button-ui&gt;
24
+ &lt;/editor-toolbar&gt;
25
+
26
+ &lt;editor-sidebar slot="leading" collapsible&gt;
27
+ &lt;pane-ui resizable&gt;
28
+ &lt;header&gt;Navigator&lt;/header&gt;
29
+ &lt;section&gt;…layers…&lt;/section&gt;
30
+ &lt;/pane-ui&gt;
31
+ &lt;/editor-sidebar&gt;
32
+
33
+ &lt;editor-canvas&gt;
34
+ &lt;editor-canvas-empty&gt;
35
+ &lt;empty-state-ui icon="square" heading="New document" description="Drop content to begin."&gt;&lt;/empty-state-ui&gt;
36
+ &lt;/editor-canvas-empty&gt;
37
+ &lt;/editor-canvas&gt;
38
+
39
+ &lt;editor-sidebar slot="trailing" collapsible&gt;
40
+ &lt;pane-ui resizable&gt;
41
+ &lt;header&gt;Inspector&lt;/header&gt;
42
+ &lt;section&gt;…properties…&lt;/section&gt;
43
+ &lt;/pane-ui&gt;
44
+ &lt;/editor-sidebar&gt;
45
+
46
+ &lt;editor-statusbar&gt;
47
+ &lt;span slot="status"&gt;Saved&lt;/span&gt;
48
+ &lt;span slot="zoom"&gt;100%&lt;/span&gt;
49
+ &lt;/editor-statusbar&gt;
50
+ &lt;/editor-shell&gt;</code-ui>
51
+ </section>
52
+
53
+ <section data-section>
54
+ <h2 variant="section">State as attribute</h2>
55
+ <code-ui language="css">/* Hide all chrome in focus mode */
56
+ editor-shell[focus-mode] :is(editor-toolbar, editor-statusbar, editor-sidebar) {
57
+ display: none;
58
+ }
59
+
60
+ /* Visual treatment for empty canvas */
61
+ editor-canvas[empty] { /* placeholder UI */ }
62
+
63
+ /* Highlight focused canvas */
64
+ editor-canvas[focused] { outline: 2px solid var(--a-accent); }</code-ui>
65
+ </section>
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="auto">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Editor Canvas Empty — AdiaUI</title>
7
+
8
+ <link rel="stylesheet" href="../../../web-components/styles/resets.css">
9
+ <link rel="stylesheet" href="../../../web-components/styles/tokens.css">
10
+ <link rel="stylesheet" href="../editor-shell/editor-shell.css">
11
+ <link rel="stylesheet" href="../../../web-components/components/code/code.css">
12
+ <link rel="stylesheet" href="../../../web-components/components/tag/tag.css">
13
+
14
+ <script type="module" src="../editor-shell/editor-shell.js"></script>
15
+ <script type="module" src="../../../web-components/components/code/code.js"></script>
16
+ <script type="module" src="../../../web-components/components/tag/tag.js"></script>
17
+
18
+ <style>
19
+ :where(html, body) { margin: 0; min-height: 100vh; background: var(--a-bg); color: var(--a-fg); font-family: var(--a-font); }
20
+ main { max-width: 960px; margin-inline: auto; padding: var(--a-space-6) var(--a-space-5); }
21
+ </style>
22
+ </head>
23
+ <body>
24
+
25
+ <main id="demo-root">
26
+ <p>Loading examples…</p>
27
+ </main>
28
+
29
+ <script type="module">
30
+ const root = document.getElementById('demo-root');
31
+ try {
32
+ const res = await fetch('./editor-canvas-empty.examples.html');
33
+ if (!res.ok) throw new Error(`fetch failed (${res.status})`);
34
+ root.innerHTML = await res.text();
35
+ } catch (err) {
36
+ root.innerHTML = `<p style="color:var(--a-danger-strong);">Failed to load editor-canvas-empty.examples.html — ${err.message}</p>`;
37
+ console.error('[editor-canvas-empty.html]', err);
38
+ }
39
+ </script>
40
+
41
+ </body>
42
+ </html>
@@ -129,11 +129,76 @@ editor-sidebar > pane-ui {
129
129
  flex-direction: column;
130
130
  }
131
131
 
132
- /* When sidebar is collapsed, hide pane content but keep the rail */
133
- editor-sidebar[collapsed] > pane-ui > :not(header) {
132
+ /* ── editor-sidebar collapsed-mode polish ──
133
+ When collapsed: pane shrinks to icon-rail width (48px); section
134
+ content hides; header narrows to icon-only with horizontal-center;
135
+ smooth width transition on uncollapse. Mirrors the rail-pattern
136
+ established by admin-sidebar's collapsed state.
137
+ ═══════════════════════════════════════════════════════════════ */
138
+
139
+ editor-sidebar {
140
+ transition: width var(--editor-sidebar-duration, var(--a-duration, 180ms))
141
+ var(--editor-sidebar-easing, var(--a-easing, ease));
142
+ container-type: inline-size;
143
+ container-name: editor-sidebar;
144
+ }
145
+
146
+ /* Inner pane animates the visible width */
147
+ editor-sidebar > pane-ui {
148
+ transition: width var(--editor-sidebar-duration, var(--a-duration, 180ms))
149
+ var(--editor-sidebar-easing, var(--a-easing, ease));
150
+ }
151
+
152
+ /* Collapsed state — hide section/footer content; preserve header icons */
153
+ editor-sidebar[collapsed] > pane-ui {
154
+ width: var(--editor-sidebar-collapsed-width, 48px) !important;
155
+ min-width: var(--editor-sidebar-collapsed-width, 48px);
156
+ overflow: hidden;
157
+ }
158
+
159
+ editor-sidebar[collapsed] > pane-ui > section,
160
+ editor-sidebar[collapsed] > pane-ui > footer {
161
+ display: none;
162
+ }
163
+
164
+ /* Collapsed header — center icons horizontally; hide text labels */
165
+ editor-sidebar[collapsed] > pane-ui > header {
166
+ display: flex;
167
+ flex-direction: column;
168
+ align-items: center;
169
+ justify-content: flex-start;
170
+ gap: var(--a-space-1);
171
+ padding: var(--a-space-2) 0;
172
+ /* Hide direct text nodes via opacity-0 + descendant text containers */
173
+ }
174
+
175
+ /* Collapsed header — hide text labels next to icons */
176
+ editor-sidebar[collapsed] > pane-ui > header :is(
177
+ span:not([data-icon]):not([slot]),
178
+ label,
179
+ h1, h2, h3
180
+ ) {
134
181
  display: none;
135
182
  }
136
183
 
184
+ /* Provide a visual rail affordance */
185
+ editor-sidebar[collapsed] {
186
+ background: var(--editor-sidebar-rail-bg, var(--a-bg-subtle, var(--a-bg)));
187
+ }
188
+
189
+ /* Container query — child elements can adapt to narrow mode */
190
+ @container editor-sidebar (max-width: 96px) {
191
+ /* Children can hide labels / shrink icons via [data-narrow] */
192
+ }
193
+
194
+ /* When resizing — disable transitions to feel responsive */
195
+ editor-sidebar[resizing] {
196
+ transition: none;
197
+ }
198
+ editor-sidebar[resizing] > pane-ui {
199
+ transition: none;
200
+ }
201
+
137
202
  /* ── editor-statusbar — bottom chrome bar ── */
138
203
  editor-shell > editor-statusbar {
139
204
  display: flex;
@@ -4,9 +4,9 @@
4
4
  Structure:
5
5
  editor-shell
6
6
  header — topbar
7
- [data-editor-body] — flex row: pane | canvas | pane
7
+ — flex row: pane | canvas | pane
8
8
  pane-ui — left navigator
9
- [data-canvas] — center canvas
9
+ editor-canvas — center canvas
10
10
  pane-ui — right inspector
11
11
  footer — bottombar
12
12
  ═══════════════════════════════════════════════════════════════ */
@@ -82,7 +82,7 @@ editor-shell > header > [slot="action-leading"] {
82
82
  }
83
83
 
84
84
  /* ── Body: pane | canvas | pane ── */
85
- editor-shell > [data-editor-body] {
85
+ editor-shell > {
86
86
  display: flex;
87
87
  flex: 1;
88
88
  min-width: 0;
@@ -90,7 +90,7 @@ editor-shell > [data-editor-body] {
90
90
  }
91
91
 
92
92
  /* ── Canvas ── */
93
- editor-shell [data-canvas] {
93
+ editor-shell editor-canvas {
94
94
  flex: 1;
95
95
  min-width: 0;
96
96
  display: flex;
@@ -107,11 +107,11 @@ editor-shell pane-ui {
107
107
  flex-shrink: 0;
108
108
  }
109
109
 
110
- editor-shell pane-ui[data-left] {
110
+ editor-shell editor-sidebar[slot="leading"] > pane-ui {
111
111
  width: var(--editor-pane-width-left);
112
112
  }
113
113
 
114
- editor-shell pane-ui[data-right] {
114
+ editor-shell editor-sidebar[slot="trailing"] > pane-ui {
115
115
  width: var(--editor-pane-width-right);
116
116
  }
117
117
 
@@ -2,26 +2,27 @@ import { UIElement } from '../../../web-components/core/element.js';
2
2
 
3
3
  /**
4
4
  * <editor-shell focus-mode?>
5
- * <editor-toolbar>...</editor-toolbar> (or legacy <header>)
6
- * <editor-sidebar slot="leading"> (or legacy <pane-ui data-left>)
5
+ * <editor-toolbar>...</editor-toolbar>
6
+ * <editor-sidebar slot="leading">
7
7
  * <pane-ui resizable>...</pane-ui>
8
8
  * </editor-sidebar>
9
- * <editor-canvas>...</editor-canvas> (or legacy <div data-canvas>)
10
- * <editor-sidebar slot="trailing"> (or legacy <pane-ui data-right>)
9
+ * <editor-canvas>...</editor-canvas>
10
+ * <editor-sidebar slot="trailing">
11
11
  * <pane-ui resizable>...</pane-ui>
12
12
  * </editor-sidebar>
13
- * <editor-statusbar>...</editor-statusbar> (or legacy <footer>)
13
+ * <editor-statusbar>...</editor-statusbar>
14
14
  * </editor-shell>
15
15
  *
16
16
  * Behavior-only orchestrator for design-tool UIs:
17
17
  * topbar, navigator pane, center canvas, inspector pane, bottombar.
18
18
  *
19
- * Per ADR-0023, the bespoke shape uses cluster-namespaced custom
20
- * elements. Both legacy and bespoke shapes coexist via :is() reads
21
- * in the host. Editor cluster has the smallest bespoke family of
22
- * the three (admin, chat, editor) only 3 JS-bearing children +
23
- * 2 CSS-only structural stubs — because <pane-ui> already owns
24
- * resize and the editor doesn't need a command palette.
19
+ * **Bespoke-only since v0.4.0** (ADR-0023 Phase 3). The legacy shapes
20
+ * (<header>, <div data-editor-body>, <pane-ui data-left|data-right>,
21
+ * <div data-canvas>, <footer>) are no longer recognized. The editor
22
+ * cluster has the smallest bespoke family of the three (admin / chat /
23
+ * editor) — only 3 JS-bearing children + 2 CSS-only structural stubs —
24
+ * because <pane-ui> already owns resize and the editor doesn't need
25
+ * a command palette.
25
26
  *
26
27
  * Reflected attributes:
27
28
  * [focus-mode] — distraction-free / canvas-focus mode; consumers
@@ -48,13 +49,14 @@ class EditorShell extends UIElement {
48
49
  #onToolbarAction = null;
49
50
 
50
51
  connected() {
51
- // Per ADR-0023 read BOTH legacy + bespoke shapes
52
- this.#toolbarEl = this.querySelector('editor-toolbar')
53
- || this.querySelector('header');
54
- this.#canvasEl = this.querySelector('editor-canvas')
55
- || this.querySelector('[data-canvas]');
52
+ // **Bespoke-only since v0.4.0** (ADR-0023 Phase 3). The legacy
53
+ // shapes (<header>, <div data-canvas>, <div data-editor-body>,
54
+ // <pane-ui data-left|data-right>, <footer>) are no longer
55
+ // recognized. Consumers MUST use the bespoke vocabulary.
56
+ this.#toolbarEl = this.querySelector('editor-toolbar');
57
+ this.#canvasEl = this.querySelector('editor-canvas');
56
58
 
57
- // Wire select options (legacy concern, kept for backwards compat)
59
+ // Wire select options inside the toolbar / sidebars
58
60
  this.#wireSelects();
59
61
 
60
62
  // Listen for toolbar-action bubbling from <editor-toolbar>
@@ -20,6 +20,11 @@
20
20
  },
21
21
  "component": {
22
22
  "const": "EditorSidebar"
23
+ },
24
+ "resizing": {
25
+ "description": "Reflected — set during an active pointer-drag of the pane's\nresize handle. Used to disable transitions and visual treatments\nthat would feel laggy during drag.\n",
26
+ "type": "boolean",
27
+ "default": false
23
28
  }
24
29
  },
25
30
  "required": [
@@ -0,0 +1,65 @@
1
+ <header>
2
+ <div>
3
+ <h1>Editor Sidebar</h1>
4
+ <div data-actions>
5
+ <tag-ui size="sm">editor-sidebar</tag-ui>
6
+ <tag-ui size="sm" variant="ghost">JS-bearing</tag-ui>
7
+ </div>
8
+ </div>
9
+ <p>Module-tier editor-cluster sidebar — wraps <pane-ui resizable> rather than reimplementing drag. Cluster-distinct localStorage prefix.</p>
10
+ </header>
11
+
12
+ <section data-section>
13
+ <h2 variant="section">Role</h2>
14
+ <p>Per <a href="../../../../.brain/adrs/0023-bespoke-shell-tier-children.md">ADR-0023</a>, the editor cluster's bespoke family — <code>&lt;editor-shell&gt;</code> (host), <code>&lt;editor-toolbar&gt;</code>, <code>&lt;editor-canvas&gt;</code>, <code>&lt;editor-sidebar&gt;</code> (JS-bearing) + <code>&lt;editor-statusbar&gt;</code>, <code>&lt;editor-canvas-empty&gt;</code> (CSS-only). Confirms the family pattern is canonical across 3 archetypes (admin/chat/editor).</p>
15
+ </section>
16
+
17
+ <section data-section>
18
+ <h2 variant="section">Composition</h2>
19
+ <p>Typical placement inside <code>&lt;editor-shell&gt;</code>:</p>
20
+ <code-ui language="html">&lt;editor-shell&gt;
21
+ &lt;editor-toolbar&gt;
22
+ &lt;span slot="title"&gt;Untitled.fig&lt;/span&gt;
23
+ &lt;button-ui slot="action" icon="gear"&gt;&lt;/button-ui&gt;
24
+ &lt;/editor-toolbar&gt;
25
+
26
+ &lt;editor-sidebar slot="leading" collapsible&gt;
27
+ &lt;pane-ui resizable&gt;
28
+ &lt;header&gt;Navigator&lt;/header&gt;
29
+ &lt;section&gt;…layers…&lt;/section&gt;
30
+ &lt;/pane-ui&gt;
31
+ &lt;/editor-sidebar&gt;
32
+
33
+ &lt;editor-canvas&gt;
34
+ &lt;editor-canvas-empty&gt;
35
+ &lt;empty-state-ui icon="square" heading="New document" description="Drop content to begin."&gt;&lt;/empty-state-ui&gt;
36
+ &lt;/editor-canvas-empty&gt;
37
+ &lt;/editor-canvas&gt;
38
+
39
+ &lt;editor-sidebar slot="trailing" collapsible&gt;
40
+ &lt;pane-ui resizable&gt;
41
+ &lt;header&gt;Inspector&lt;/header&gt;
42
+ &lt;section&gt;…properties…&lt;/section&gt;
43
+ &lt;/pane-ui&gt;
44
+ &lt;/editor-sidebar&gt;
45
+
46
+ &lt;editor-statusbar&gt;
47
+ &lt;span slot="status"&gt;Saved&lt;/span&gt;
48
+ &lt;span slot="zoom"&gt;100%&lt;/span&gt;
49
+ &lt;/editor-statusbar&gt;
50
+ &lt;/editor-shell&gt;</code-ui>
51
+ </section>
52
+
53
+ <section data-section>
54
+ <h2 variant="section">State as attribute</h2>
55
+ <code-ui language="css">/* Hide all chrome in focus mode */
56
+ editor-shell[focus-mode] :is(editor-toolbar, editor-statusbar, editor-sidebar) {
57
+ display: none;
58
+ }
59
+
60
+ /* Visual treatment for empty canvas */
61
+ editor-canvas[empty] { /* placeholder UI */ }
62
+
63
+ /* Highlight focused canvas */
64
+ editor-canvas[focused] { outline: 2px solid var(--a-accent); }</code-ui>
65
+ </section>