@adia-ai/web-modules 0.3.4 → 0.3.6

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 (57) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/chat/chat-composer/chat-composer.a2ui.json +94 -0
  3. package/chat/chat-composer/chat-composer.examples.html +28 -0
  4. package/chat/chat-composer/chat-composer.html +43 -0
  5. package/chat/chat-composer/chat-composer.js +107 -0
  6. package/chat/chat-composer/chat-composer.test.js +112 -0
  7. package/chat/chat-composer/chat-composer.yaml +91 -0
  8. package/chat/chat-empty/chat-empty.a2ui.json +68 -0
  9. package/chat/chat-empty/chat-empty.examples.html +34 -0
  10. package/chat/chat-empty/chat-empty.html +42 -0
  11. package/chat/chat-empty/chat-empty.yaml +58 -0
  12. package/chat/chat-header/chat-header.a2ui.json +77 -0
  13. package/chat/chat-header/chat-header.examples.html +30 -0
  14. package/chat/chat-header/chat-header.html +42 -0
  15. package/chat/chat-header/chat-header.yaml +68 -0
  16. package/chat/chat-shell/chat-shell.css +1 -0
  17. package/chat/chat-shell/chat-shell.examples.html +43 -2
  18. package/chat/chat-shell/chat-shell.js +35 -7
  19. package/chat/chat-shell/css/chat-shell.bespoke.css +196 -0
  20. package/chat/chat-sidebar/chat-sidebar.a2ui.json +136 -0
  21. package/chat/chat-sidebar/chat-sidebar.examples.html +36 -0
  22. package/chat/chat-sidebar/chat-sidebar.html +43 -0
  23. package/chat/chat-sidebar/chat-sidebar.js +227 -0
  24. package/chat/chat-sidebar/chat-sidebar.test.js +110 -0
  25. package/chat/chat-sidebar/chat-sidebar.yaml +140 -0
  26. package/chat/chat-status/chat-status.a2ui.json +63 -0
  27. package/chat/chat-status/chat-status.examples.html +29 -0
  28. package/chat/chat-status/chat-status.html +42 -0
  29. package/chat/chat-status/chat-status.yaml +52 -0
  30. package/chat/chat-thread/chat-thread.a2ui.json +91 -0
  31. package/chat/chat-thread/chat-thread.examples.html +36 -0
  32. package/chat/chat-thread/chat-thread.html +43 -0
  33. package/chat/chat-thread/chat-thread.js +106 -0
  34. package/chat/chat-thread/chat-thread.test.js +82 -0
  35. package/chat/chat-thread/chat-thread.yaml +89 -0
  36. package/chat/index.js +3 -0
  37. package/editor/editor-canvas/editor-canvas.a2ui.json +87 -0
  38. package/editor/editor-canvas/editor-canvas.js +103 -0
  39. package/editor/editor-canvas/editor-canvas.test.js +100 -0
  40. package/editor/editor-canvas/editor-canvas.yaml +88 -0
  41. package/editor/editor-canvas-empty/editor-canvas-empty.a2ui.json +69 -0
  42. package/editor/editor-canvas-empty/editor-canvas-empty.yaml +56 -0
  43. package/editor/editor-shell/css/editor-shell.bespoke.css +172 -0
  44. package/editor/editor-shell/editor-shell.css +1 -0
  45. package/editor/editor-shell/editor-shell.js +85 -30
  46. package/editor/editor-sidebar/editor-sidebar.a2ui.json +88 -0
  47. package/editor/editor-sidebar/editor-sidebar.js +173 -0
  48. package/editor/editor-sidebar/editor-sidebar.test.js +126 -0
  49. package/editor/editor-sidebar/editor-sidebar.yaml +83 -0
  50. package/editor/editor-statusbar/editor-statusbar.a2ui.json +76 -0
  51. package/editor/editor-statusbar/editor-statusbar.yaml +57 -0
  52. package/editor/editor-toolbar/editor-toolbar.a2ui.json +96 -0
  53. package/editor/editor-toolbar/editor-toolbar.js +58 -0
  54. package/editor/editor-toolbar/editor-toolbar.test.js +99 -0
  55. package/editor/editor-toolbar/editor-toolbar.yaml +81 -0
  56. package/editor/index.js +3 -0
  57. package/package.json +1 -1
@@ -0,0 +1,87 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/EditorCanvas.json",
4
+ "title": "EditorCanvas",
5
+ "description": "Module-tier editor canvas surface — replaces legacy <div data-canvas>\ninside <editor-shell> per ADR-0023. Owns scroll/zoom container\nsemantics, [empty] and [focused] reflected attributes, and a stable\ntarget for the host's content rendering pipeline.\n\nSits as the central content region inside <editor-shell>, between\nthe optional <editor-sidebar slot=\"leading\"> and <editor-sidebar\nslot=\"trailing\">. Authors compose <editor-canvas-empty> as an\noptional first child for the empty state; canvas content children\n(artboards, document body, etc.) are appended either statically or\ndynamically by the host.\n\nBackwards compat — <editor-shell> still recognizes the legacy\n<div data-canvas> shape via :is() selector. New code should prefer\n<editor-canvas>.\n",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "component": {
17
+ "const": "EditorCanvas"
18
+ },
19
+ "empty": {
20
+ "description": "Reflected — set when zero non-<editor-canvas-empty> children.\nDrives the <editor-canvas-empty> visibility via parent CSS —\nno JS toggling needed.\n",
21
+ "type": "boolean",
22
+ "default": true
23
+ },
24
+ "focused": {
25
+ "description": "Reflected — set when canvas claims focus ownership (programmatic\n.focus() call or toolbar full-screen toggle).\n",
26
+ "type": "boolean",
27
+ "default": false
28
+ }
29
+ },
30
+ "required": [
31
+ "component"
32
+ ],
33
+ "unevaluatedProperties": false,
34
+ "x-adiaui": {
35
+ "anti_patterns": [],
36
+ "category": "container",
37
+ "events": {},
38
+ "examples": [],
39
+ "keywords": [
40
+ "editor-canvas",
41
+ "canvas",
42
+ "workspace",
43
+ "artboard",
44
+ "editor-surface"
45
+ ],
46
+ "name": "EditorCanvas",
47
+ "related": [
48
+ "EditorShell",
49
+ "EditorCanvasEmpty",
50
+ "EditorToolbar",
51
+ "EditorSidebar"
52
+ ],
53
+ "slots": {
54
+ "default": {
55
+ "description": "Default — canvas content (artboards, document body, blocks, etc.) plus an optional first <editor-canvas-empty> sibling for the empty state."
56
+ }
57
+ },
58
+ "states": [
59
+ {
60
+ "description": "Default canvas mode.",
61
+ "name": "idle"
62
+ },
63
+ {
64
+ "description": "Zero content children — empty state visible.",
65
+ "attribute": "empty",
66
+ "name": "empty"
67
+ },
68
+ {
69
+ "description": "Canvas is the focus surface.",
70
+ "attribute": "focused",
71
+ "name": "focused"
72
+ }
73
+ ],
74
+ "synonyms": {
75
+ "editor-canvas": [
76
+ "canvas",
77
+ "workspace",
78
+ "artboard",
79
+ "design-surface"
80
+ ]
81
+ },
82
+ "tag": "editor-canvas",
83
+ "tokens": {},
84
+ "traits": [],
85
+ "version": 1
86
+ }
87
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * <editor-canvas focused?>
3
+ * <editor-canvas-empty>...</editor-canvas-empty> (optional empty state)
4
+ * <!-- canvas content (children) -->
5
+ * </editor-canvas>
6
+ *
7
+ * Module-tier editor canvas surface — replaces legacy <div data-canvas>
8
+ * inside <editor-shell> per ADR-0023. Owns:
9
+ *
10
+ * - [empty] reflected attribute (set when no non-stub children) —
11
+ * drives <editor-canvas-empty> visibility via parent CSS
12
+ * - [focused] reflected attribute (set when canvas claims focus
13
+ * ownership; e.g., toolbar full-screen toggle, click-to-focus)
14
+ * - Public API for zoom/pan refs (.zoom getter/setter, .resetView())
15
+ * - .focus() / .blur() programmatic — sets [focused] reflected
16
+ *
17
+ * Reflected attributes:
18
+ * [empty] — true when zero non-<editor-canvas-empty> children
19
+ * [focused] — true when canvas is the focus surface
20
+ *
21
+ * Public methods:
22
+ * .focus() — claim focus + reflect [focused]
23
+ * .blur() — release focus + clear [focused]
24
+ * .resetView() — reset zoom/pan to defaults
25
+ *
26
+ * Properties:
27
+ * .zoom — getter/setter (default 1.0)
28
+ *
29
+ * The host (<editor-shell>) reads either <editor-canvas> or
30
+ * [data-canvas] / <div data-canvas> via :is() selector for backwards
31
+ * compat.
32
+ */
33
+
34
+ import { UIElement } from '../../../web-components/core/element.js';
35
+
36
+ class EditorCanvas extends UIElement {
37
+ static properties = {
38
+ empty: { type: Boolean, default: true, reflect: true },
39
+ focused: { type: Boolean, default: false, reflect: true },
40
+ };
41
+
42
+ static template = () => null;
43
+
44
+ #zoom = 1.0;
45
+ #childObserver = null;
46
+
47
+ connected() {
48
+ this.#syncEmptyFromChildren();
49
+ this.#setupChildObserver();
50
+ }
51
+
52
+ disconnected() {
53
+ this.#childObserver?.disconnect();
54
+ this.#childObserver = null;
55
+ }
56
+
57
+ // ── Public API ──
58
+
59
+ get zoom() {
60
+ return this.#zoom;
61
+ }
62
+
63
+ set zoom(value) {
64
+ const v = parseFloat(value);
65
+ if (!isFinite(v) || v <= 0) return;
66
+ this.#zoom = v;
67
+ this.style.setProperty('--editor-canvas-zoom', String(v));
68
+ }
69
+
70
+ resetView() {
71
+ this.zoom = 1.0;
72
+ // Future: also reset pan offsets when implemented
73
+ }
74
+
75
+ focus() {
76
+ super.focus?.();
77
+ this.focused = true;
78
+ }
79
+
80
+ blur() {
81
+ super.blur?.();
82
+ this.focused = false;
83
+ }
84
+
85
+ // ── Internal: empty state from children ──
86
+
87
+ #syncEmptyFromChildren() {
88
+ const contentChildren = Array.from(this.children).filter(
89
+ (c) => c.tagName.toLowerCase() !== 'editor-canvas-empty'
90
+ );
91
+ this.empty = contentChildren.length === 0;
92
+ }
93
+
94
+ #setupChildObserver() {
95
+ this.#childObserver = new MutationObserver(() => {
96
+ this.#syncEmptyFromChildren();
97
+ });
98
+ this.#childObserver.observe(this, { childList: true });
99
+ }
100
+ }
101
+
102
+ customElements.define('editor-canvas', EditorCanvas);
103
+ export { EditorCanvas };
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import '../../../web-components/core/element.js';
3
+ import './editor-canvas.js';
4
+
5
+ const tick = () => new Promise((r) => queueMicrotask(r));
6
+
7
+ function mount(html) {
8
+ const wrap = document.createElement('div');
9
+ wrap.innerHTML = html;
10
+ document.body.appendChild(wrap);
11
+ return wrap.firstElementChild;
12
+ }
13
+
14
+ beforeEach(() => {
15
+ document.body.innerHTML = '';
16
+ globalThis.MutationObserver = class {
17
+ observe() {} disconnect() {}
18
+ };
19
+ });
20
+
21
+ describe('editor-canvas', () => {
22
+ it('registers editor-canvas as a custom element', () => {
23
+ expect(customElements.get('editor-canvas')).toBeDefined();
24
+ });
25
+
26
+ it('defaults to focused=false', () => {
27
+ const c = mount('<editor-canvas></editor-canvas>');
28
+ expect(c.focused).toBe(false);
29
+ });
30
+
31
+ it('defaults to empty=true with no children', async () => {
32
+ const c = mount('<editor-canvas></editor-canvas>');
33
+ await tick();
34
+ expect(c.empty).toBe(true);
35
+ });
36
+
37
+ it('reflects [empty] via property assignment', async () => {
38
+ const c = mount('<editor-canvas></editor-canvas>');
39
+ c.empty = false;
40
+ await tick();
41
+ expect(c.hasAttribute('empty')).toBe(false);
42
+ c.empty = true;
43
+ await tick();
44
+ expect(c.hasAttribute('empty')).toBe(true);
45
+ });
46
+
47
+ it('reflects [focused] via property assignment', async () => {
48
+ const c = mount('<editor-canvas></editor-canvas>');
49
+ c.focused = true;
50
+ await tick();
51
+ expect(c.hasAttribute('focused')).toBe(true);
52
+ });
53
+
54
+ it('does not count <editor-canvas-empty> stub as a content child', () => {
55
+ const c = mount('<editor-canvas><editor-canvas-empty></editor-canvas-empty></editor-canvas>');
56
+ expect(c.empty).toBe(true);
57
+ });
58
+
59
+ it('exposes .zoom getter/setter (default 1.0)', () => {
60
+ const c = mount('<editor-canvas></editor-canvas>');
61
+ expect(c.zoom).toBe(1.0);
62
+ c.zoom = 2.0;
63
+ expect(c.zoom).toBe(2.0);
64
+ expect(c.style.getPropertyValue('--editor-canvas-zoom')).toBe('2');
65
+ });
66
+
67
+ it('rejects invalid zoom values', () => {
68
+ const c = mount('<editor-canvas></editor-canvas>');
69
+ c.zoom = 1.5;
70
+ c.zoom = 'invalid'; // should not change
71
+ expect(c.zoom).toBe(1.5);
72
+ c.zoom = -1; // should not change
73
+ expect(c.zoom).toBe(1.5);
74
+ c.zoom = 0; // should not change
75
+ expect(c.zoom).toBe(1.5);
76
+ });
77
+
78
+ it('.resetView() restores zoom to 1.0', () => {
79
+ const c = mount('<editor-canvas></editor-canvas>');
80
+ c.zoom = 2.5;
81
+ c.resetView();
82
+ expect(c.zoom).toBe(1.0);
83
+ });
84
+
85
+ it('.focus() sets [focused] reflected', async () => {
86
+ const c = mount('<editor-canvas></editor-canvas>');
87
+ c.focus();
88
+ await tick();
89
+ expect(c.focused).toBe(true);
90
+ expect(c.hasAttribute('focused')).toBe(true);
91
+ });
92
+
93
+ it('.blur() clears [focused] reflected', async () => {
94
+ const c = mount('<editor-canvas focused></editor-canvas>');
95
+ c.blur();
96
+ await tick();
97
+ expect(c.focused).toBe(false);
98
+ expect(c.hasAttribute('focused')).toBe(false);
99
+ });
100
+ });
@@ -0,0 +1,88 @@
1
+ # Edit this file; run `npm run build:components` to regenerate a2ui.json.
2
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
3
+ name: EditorCanvas
4
+ tag: editor-canvas
5
+ component: EditorCanvas
6
+ category: container
7
+ version: 1
8
+ description: |
9
+ Module-tier editor canvas surface — replaces legacy <div data-canvas>
10
+ inside <editor-shell> per ADR-0023. Owns scroll/zoom container
11
+ semantics, [empty] and [focused] reflected attributes, and a stable
12
+ target for the host's content rendering pipeline.
13
+
14
+ Sits as the central content region inside <editor-shell>, between
15
+ the optional <editor-sidebar slot="leading"> and <editor-sidebar
16
+ slot="trailing">. Authors compose <editor-canvas-empty> as an
17
+ optional first child for the empty state; canvas content children
18
+ (artboards, document body, etc.) are appended either statically or
19
+ dynamically by the host.
20
+
21
+ Backwards compat — <editor-shell> still recognizes the legacy
22
+ <div data-canvas> shape via :is() selector. New code should prefer
23
+ <editor-canvas>.
24
+
25
+ props:
26
+ empty:
27
+ description: |
28
+ Reflected — set when zero non-<editor-canvas-empty> children.
29
+ Drives the <editor-canvas-empty> visibility via parent CSS —
30
+ no JS toggling needed.
31
+ type: boolean
32
+ default: true
33
+ reflect: true
34
+ focused:
35
+ description: |
36
+ Reflected — set when canvas claims focus ownership (programmatic
37
+ .focus() call or toolbar full-screen toggle).
38
+ type: boolean
39
+ default: false
40
+ reflect: true
41
+
42
+ events: {}
43
+
44
+ slots:
45
+ default:
46
+ description: >-
47
+ Default — canvas content (artboards, document body, blocks, etc.)
48
+ plus an optional first <editor-canvas-empty> sibling for the
49
+ empty state.
50
+
51
+ states:
52
+ - name: idle
53
+ description: Default canvas mode.
54
+ - name: empty
55
+ attribute: empty
56
+ description: Zero content children — empty state visible.
57
+ - name: focused
58
+ attribute: focused
59
+ description: Canvas is the focus surface.
60
+
61
+ traits: []
62
+
63
+ a2ui:
64
+ rules:
65
+ - >-
66
+ editor-canvas is the bespoke replacement for legacy
67
+ <div data-canvas> inside <editor-shell>. Use it as the central
68
+ content region for editor work surfaces.
69
+ - >-
70
+ Place <editor-canvas-empty> as an optional first child for the
71
+ empty state; the [empty] reflected attribute drives its visibility
72
+ via CSS (no JS toggling).
73
+
74
+ keywords:
75
+ - editor-canvas
76
+ - canvas
77
+ - workspace
78
+ - artboard
79
+ - editor-surface
80
+
81
+ synonyms:
82
+ editor-canvas: [canvas, workspace, artboard, design-surface]
83
+
84
+ related:
85
+ - EditorShell
86
+ - EditorCanvasEmpty
87
+ - EditorToolbar
88
+ - EditorSidebar
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/EditorCanvasEmpty.json",
4
+ "title": "EditorCanvasEmpty",
5
+ "description": "Module-tier editor canvas empty state. CSS-only — no behavior, no\nJS. Sits as the first child of <editor-canvas> as the empty state\nplaceholder. Visibility is driven by <editor-canvas>'s [empty]\nreflected attribute via CSS (no JS toggling) — when content arrives,\nthe parent canvas loses [empty] and CSS hides this stub.\n\nReplaces ad-hoc empty-state placement inside the legacy\n<div data-canvas> per ADR-0023.\n",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "component": {
17
+ "const": "EditorCanvasEmpty"
18
+ }
19
+ },
20
+ "required": [
21
+ "component"
22
+ ],
23
+ "unevaluatedProperties": false,
24
+ "x-adiaui": {
25
+ "anti_patterns": [],
26
+ "category": "feedback",
27
+ "events": {},
28
+ "examples": [],
29
+ "keywords": [
30
+ "editor-canvas-empty",
31
+ "empty-state",
32
+ "placeholder",
33
+ "no-content",
34
+ "canvas-empty"
35
+ ],
36
+ "name": "EditorCanvasEmpty",
37
+ "related": [
38
+ "EditorShell",
39
+ "EditorCanvas",
40
+ "EmptyState"
41
+ ],
42
+ "slots": {
43
+ "default": {
44
+ "description": "Empty state content — typically <empty-state-ui> with a \"Create new...\" prompt or onboarding affordance."
45
+ }
46
+ },
47
+ "states": [
48
+ {
49
+ "description": "Default, visible.",
50
+ "name": "idle"
51
+ },
52
+ {
53
+ "description": "Hidden via parent <editor-canvas>:not([empty]) selector.",
54
+ "name": "hidden"
55
+ }
56
+ ],
57
+ "synonyms": {
58
+ "editor-canvas-empty": [
59
+ "empty-state",
60
+ "placeholder",
61
+ "no-document"
62
+ ]
63
+ },
64
+ "tag": "editor-canvas-empty",
65
+ "tokens": {},
66
+ "traits": [],
67
+ "version": 1
68
+ }
69
+ }
@@ -0,0 +1,56 @@
1
+ # Edit this file; run `npm run build:components` to regenerate a2ui.json.
2
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
3
+ name: EditorCanvasEmpty
4
+ tag: editor-canvas-empty
5
+ component: EditorCanvasEmpty
6
+ category: feedback
7
+ version: 1
8
+ description: |
9
+ Module-tier editor canvas empty state. CSS-only — no behavior, no
10
+ JS. Sits as the first child of <editor-canvas> as the empty state
11
+ placeholder. Visibility is driven by <editor-canvas>'s [empty]
12
+ reflected attribute via CSS (no JS toggling) — when content arrives,
13
+ the parent canvas loses [empty] and CSS hides this stub.
14
+
15
+ Replaces ad-hoc empty-state placement inside the legacy
16
+ <div data-canvas> per ADR-0023.
17
+
18
+ props: {}
19
+
20
+ events: {}
21
+
22
+ slots:
23
+ default:
24
+ description: >-
25
+ Empty state content — typically <empty-state-ui> with a
26
+ "Create new..." prompt or onboarding affordance.
27
+
28
+ states:
29
+ - name: idle
30
+ description: Default, visible.
31
+ - name: hidden
32
+ description: Hidden via parent <editor-canvas>:not([empty]) selector.
33
+
34
+ traits: []
35
+
36
+ a2ui:
37
+ rules:
38
+ - >-
39
+ editor-canvas-empty is the bespoke empty-state slot for
40
+ <editor-canvas>. Place as the first child of <editor-canvas>;
41
+ visibility is automatic via the [empty] reflected attribute.
42
+
43
+ keywords:
44
+ - editor-canvas-empty
45
+ - empty-state
46
+ - placeholder
47
+ - no-content
48
+ - canvas-empty
49
+
50
+ synonyms:
51
+ editor-canvas-empty: [empty-state, placeholder, no-document]
52
+
53
+ related:
54
+ - EditorShell
55
+ - EditorCanvas
56
+ - EmptyState
@@ -0,0 +1,172 @@
1
+ /* ═══════════════════════════════════════════════════════════════
2
+ editor-shell — Bespoke shell-tier children (Phase 2 of ADR-0023)
3
+
4
+ The editor-* CSS-only structural children are styled by SHARING
5
+ the same CSS rules as their legacy raw-HTML counterparts. This
6
+ file maps the new bespoke tags to the existing layout.css patterns
7
+ without modifying the layered files themselves.
8
+
9
+ Mirrors admin-shell.bespoke.css and chat-shell.bespoke.css. Keeps
10
+ the editor cluster's CSS modifications isolated in one bridge so
11
+ Phase 3 (legacy shape removal) is a single-file delete.
12
+
13
+ Editor cluster decomposition note:
14
+ - <editor-toolbar> — replaces <header> chrome bar (CSS-only? no,
15
+ it's JS-bearing for [data-toolbar-action] click bubble + slot
16
+ routing, but visually it's just a chrome bar like admin-topbar)
17
+ - <editor-canvas> — replaces <div data-canvas> central content
18
+ (JS-bearing — owns [empty] + [focused] reflected, zoom API)
19
+ - <editor-sidebar> — wraps <pane-ui resizable> (JS-bearing — owns
20
+ [collapsed] + persistence, delegates drag to <pane-ui>)
21
+ - <editor-statusbar> — replaces <footer> chrome bar (CSS-only)
22
+ - <editor-canvas-empty> — empty-state slot for <editor-canvas>
23
+ (CSS-only, visibility via parent [empty])
24
+ ═══════════════════════════════════════════════════════════════ */
25
+
26
+ /* ── editor-toolbar — top chrome bar ── */
27
+ editor-shell > editor-toolbar {
28
+ display: flex;
29
+ align-items: center;
30
+ gap: var(--editor-toolbar-gap, var(--a-space-2));
31
+ padding: 0 var(--editor-toolbar-px, var(--a-space-3));
32
+ height: var(--editor-toolbar-height, var(--a-size-lg));
33
+ font-size: var(--editor-toolbar-font, var(--a-ui-size));
34
+ border-bottom: var(--editor-border, 1px solid var(--a-border-subtle));
35
+ background: var(--editor-bg, var(--a-bg));
36
+ flex-shrink: 0;
37
+ grid-area: toolbar;
38
+ }
39
+
40
+ /* Slot vocabulary inside editor-toolbar */
41
+ editor-toolbar > [slot="title"] {
42
+ font-weight: var(--a-weight-medium, 500);
43
+ color: var(--a-fg);
44
+ }
45
+
46
+ editor-toolbar > [slot="action-leading"] {
47
+ margin-inline-end: var(--a-space-2);
48
+ }
49
+
50
+ editor-toolbar > [slot="status"] {
51
+ margin-inline-start: var(--a-space-2);
52
+ color: var(--a-fg-muted);
53
+ }
54
+
55
+ editor-toolbar > [slot="action"]:first-of-type {
56
+ margin-inline-start: auto;
57
+ }
58
+
59
+ /* Full-screen mode — toolbar can be hidden by host CSS */
60
+ editor-toolbar[full-screen] {
61
+ /* Optional: visual treatment in full-screen mode. Default: no change.
62
+ Authors can override via:
63
+ editor-shell:has(editor-toolbar[full-screen]) editor-toolbar { display: none; }
64
+ */
65
+ }
66
+
67
+ /* ── editor-canvas — central content surface ── */
68
+ editor-shell > editor-canvas {
69
+ flex: 1;
70
+ min-height: 0;
71
+ min-width: 0;
72
+ display: flex;
73
+ flex-direction: column;
74
+ position: relative;
75
+ overflow: auto;
76
+ background: var(--editor-canvas-bg, var(--a-bg-subtle));
77
+ grid-area: canvas;
78
+
79
+ /* Apply zoom transform if the JS sets --editor-canvas-zoom */
80
+ /* Children inherit; the canvas itself is the scroll surface */
81
+ }
82
+
83
+ editor-canvas[focused] {
84
+ /* Optional: visual focus treatment. Default: no visual change. */
85
+ }
86
+
87
+ editor-canvas > * {
88
+ transform: scale(var(--editor-canvas-zoom, 1));
89
+ transform-origin: top left;
90
+ }
91
+
92
+ /* ── editor-canvas-empty — visibility via parent's [empty] ── */
93
+ editor-canvas:not([empty]) > editor-canvas-empty {
94
+ display: none;
95
+ }
96
+
97
+ editor-canvas[empty] > editor-canvas-empty {
98
+ display: flex;
99
+ flex: 1;
100
+ align-items: center;
101
+ justify-content: center;
102
+ padding: var(--a-space-6);
103
+ }
104
+
105
+ /* ── editor-sidebar — wraps <pane-ui resizable> ── */
106
+ :is(editor-sidebar[slot="leading"], editor-sidebar[slot="trailing"]) {
107
+ display: flex;
108
+ flex-direction: column;
109
+ flex-shrink: 0;
110
+ min-height: 0;
111
+ position: relative;
112
+ }
113
+
114
+ editor-sidebar[slot="leading"] {
115
+ grid-area: leading;
116
+ border-right: var(--editor-border, 1px solid var(--a-border-subtle));
117
+ }
118
+
119
+ editor-sidebar[slot="trailing"] {
120
+ grid-area: trailing;
121
+ border-left: var(--editor-border, 1px solid var(--a-border-subtle));
122
+ }
123
+
124
+ /* The inner <pane-ui> takes all available space */
125
+ editor-sidebar > pane-ui {
126
+ flex: 1;
127
+ min-height: 0;
128
+ display: flex;
129
+ flex-direction: column;
130
+ }
131
+
132
+ /* When sidebar is collapsed, hide pane content but keep the rail */
133
+ editor-sidebar[collapsed] > pane-ui > :not(header) {
134
+ display: none;
135
+ }
136
+
137
+ /* ── editor-statusbar — bottom chrome bar ── */
138
+ editor-shell > editor-statusbar {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: var(--editor-statusbar-gap, var(--a-space-2));
142
+ padding: 0 var(--editor-statusbar-px, var(--a-space-3));
143
+ height: var(--editor-statusbar-height, var(--a-size-md));
144
+ font-size: var(--editor-statusbar-font, var(--a-ui-sm));
145
+ border-top: var(--editor-border, 1px solid var(--a-border-subtle));
146
+ background: var(--editor-bg, var(--a-bg));
147
+ color: var(--a-fg-muted);
148
+ flex-shrink: 0;
149
+ grid-area: statusbar;
150
+ }
151
+
152
+ editor-statusbar > [slot="cursor"],
153
+ editor-statusbar > [slot="zoom"] {
154
+ font-family: var(--a-font-mono, monospace);
155
+ margin-inline-start: var(--a-space-2);
156
+ }
157
+
158
+ editor-statusbar > [slot="action"]:first-of-type {
159
+ margin-inline-start: auto;
160
+ }
161
+
162
+ /* ── editor-shell layout — when bespoke children are used ── */
163
+ editor-shell:has(> editor-canvas) {
164
+ display: grid;
165
+ grid-template-areas:
166
+ "toolbar toolbar toolbar"
167
+ "leading canvas trailing"
168
+ "statusbar statusbar statusbar";
169
+ grid-template-rows: auto 1fr auto;
170
+ grid-template-columns: auto 1fr auto;
171
+ height: 100%;
172
+ }
@@ -4,3 +4,4 @@
4
4
 
5
5
  @import "./css/editor-shell.tokens.css";
6
6
  @import "./css/editor-shell.layout.css";
7
+ @import "./css/editor-shell.bespoke.css";