@adia-ai/web-modules 0.3.3 → 0.3.5
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.
- package/CHANGELOG.md +60 -0
- package/chat/chat-composer/chat-composer.a2ui.json +94 -0
- package/chat/chat-composer/chat-composer.examples.html +28 -0
- package/chat/chat-composer/chat-composer.html +43 -0
- package/chat/chat-composer/chat-composer.js +107 -0
- package/chat/chat-composer/chat-composer.test.js +112 -0
- package/chat/chat-composer/chat-composer.yaml +91 -0
- package/chat/chat-empty/chat-empty.a2ui.json +68 -0
- package/chat/chat-empty/chat-empty.examples.html +34 -0
- package/chat/chat-empty/chat-empty.html +42 -0
- package/chat/chat-empty/chat-empty.yaml +58 -0
- package/chat/chat-header/chat-header.a2ui.json +77 -0
- package/chat/chat-header/chat-header.examples.html +30 -0
- package/chat/chat-header/chat-header.html +42 -0
- package/chat/chat-header/chat-header.yaml +68 -0
- package/chat/chat-shell/chat-shell.css +1 -0
- package/chat/chat-shell/chat-shell.examples.html +126 -0
- package/chat/chat-shell/chat-shell.html +42 -0
- package/chat/chat-shell/chat-shell.js +35 -7
- package/chat/chat-shell/css/chat-shell.bespoke.css +196 -0
- package/chat/chat-sidebar/chat-sidebar.a2ui.json +136 -0
- package/chat/chat-sidebar/chat-sidebar.examples.html +36 -0
- package/chat/chat-sidebar/chat-sidebar.html +43 -0
- package/chat/chat-sidebar/chat-sidebar.js +227 -0
- package/chat/chat-sidebar/chat-sidebar.test.js +110 -0
- package/chat/chat-sidebar/chat-sidebar.yaml +140 -0
- package/chat/chat-status/chat-status.a2ui.json +63 -0
- package/chat/chat-status/chat-status.examples.html +29 -0
- package/chat/chat-status/chat-status.html +42 -0
- package/chat/chat-status/chat-status.yaml +52 -0
- package/chat/chat-thread/chat-thread.a2ui.json +91 -0
- package/chat/chat-thread/chat-thread.examples.html +36 -0
- package/chat/chat-thread/chat-thread.html +43 -0
- package/chat/chat-thread/chat-thread.js +106 -0
- package/chat/chat-thread/chat-thread.test.js +82 -0
- package/chat/chat-thread/chat-thread.yaml +89 -0
- package/chat/index.js +3 -0
- package/editor/editor-shell/editor-shell.examples.html +71 -0
- package/editor/editor-shell/editor-shell.html +42 -0
- package/package.json +1 -1
- package/shell/admin-command/admin-command.a2ui.json +102 -0
- package/shell/admin-command/admin-command.examples.html +83 -0
- package/shell/admin-command/admin-command.html +42 -0
- package/shell/admin-command/admin-command.js +161 -0
- package/shell/admin-command/admin-command.test.js +115 -0
- package/shell/admin-command/admin-command.yaml +102 -0
- package/shell/admin-content/admin-content.a2ui.json +73 -0
- package/shell/admin-content/admin-content.examples.html +33 -0
- package/shell/admin-content/admin-content.html +42 -0
- package/shell/admin-content/admin-content.yaml +63 -0
- package/shell/admin-page/admin-page.a2ui.json +74 -0
- package/shell/admin-page/admin-page.examples.html +37 -0
- package/shell/admin-page/admin-page.html +42 -0
- package/shell/admin-page/admin-page.yaml +61 -0
- package/shell/admin-page-body/admin-page-body.a2ui.json +62 -0
- package/shell/admin-page-body/admin-page-body.examples.html +34 -0
- package/shell/admin-page-body/admin-page-body.html +42 -0
- package/shell/admin-page-body/admin-page-body.yaml +49 -0
- package/shell/admin-page-header/admin-page-header.a2ui.json +62 -0
- package/shell/admin-page-header/admin-page-header.examples.html +34 -0
- package/shell/admin-page-header/admin-page-header.html +42 -0
- package/shell/admin-page-header/admin-page-header.yaml +47 -0
- package/shell/admin-scroll/admin-scroll.a2ui.json +62 -0
- package/shell/admin-scroll/admin-scroll.examples.html +31 -0
- package/shell/admin-scroll/admin-scroll.html +42 -0
- package/shell/admin-scroll/admin-scroll.yaml +51 -0
- package/shell/admin-shell/admin-shell.a2ui.json +0 -10
- package/shell/admin-shell/admin-shell.css +1 -0
- package/shell/admin-shell/admin-shell.examples.html +61 -5
- package/shell/admin-shell/admin-shell.js +165 -121
- package/shell/admin-shell/admin-shell.yaml +6 -6
- package/shell/admin-shell/css/admin-shell.bespoke.css +198 -0
- package/shell/admin-shell/css/admin-shell.tokens.css +10 -0
- package/shell/admin-sidebar/admin-sidebar.a2ui.json +138 -0
- package/shell/admin-sidebar/admin-sidebar.examples.html +76 -0
- package/shell/admin-sidebar/admin-sidebar.html +47 -0
- package/shell/admin-sidebar/admin-sidebar.js +227 -0
- package/shell/admin-sidebar/admin-sidebar.test.js +123 -0
- package/shell/admin-sidebar/admin-sidebar.yaml +140 -0
- package/shell/admin-statusbar/admin-statusbar.a2ui.json +81 -0
- package/shell/admin-statusbar/admin-statusbar.examples.html +29 -0
- package/shell/admin-statusbar/admin-statusbar.html +42 -0
- package/shell/admin-statusbar/admin-statusbar.yaml +68 -0
- package/shell/admin-topbar/admin-topbar.a2ui.json +83 -0
- package/shell/admin-topbar/admin-topbar.examples.html +31 -0
- package/shell/admin-topbar/admin-topbar.html +42 -0
- package/shell/admin-topbar/admin-topbar.yaml +75 -0
- package/shell/index.js +2 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <chat-thread streaming?>
|
|
3
|
+
* <chat-empty>...</chat-empty> (optional, hidden when messages present)
|
|
4
|
+
* <!-- assistant/user message divs appended dynamically by chat-shell -->
|
|
5
|
+
* </chat-thread>
|
|
6
|
+
*
|
|
7
|
+
* Module-tier message thread container — replaces legacy
|
|
8
|
+
* <section data-chat-messages> per ADR-0023. Owns:
|
|
9
|
+
*
|
|
10
|
+
* - Scroll-to-bottom on new message (idempotent, respects user
|
|
11
|
+
* scroll-up to read history; resumes auto-scroll when scrolled
|
|
12
|
+
* to bottom)
|
|
13
|
+
* - [streaming] reflected attribute (set by host while LLM streams)
|
|
14
|
+
* - [empty] reflected attribute (set when no message children)
|
|
15
|
+
* - Stable target for chat-shell's #messagesEl reference (host
|
|
16
|
+
* queries this OR the legacy [data-chat-messages] selector)
|
|
17
|
+
*
|
|
18
|
+
* Reflected attributes:
|
|
19
|
+
* [streaming] — true while LLM is streaming a response
|
|
20
|
+
* [empty] — true when zero message children (drives empty-state)
|
|
21
|
+
*
|
|
22
|
+
* Public methods:
|
|
23
|
+
* .scrollToBottom() — programmatic scroll (smooth)
|
|
24
|
+
* .scrollToBottomInstant() — immediate (no animation)
|
|
25
|
+
*
|
|
26
|
+
* The host (<chat-shell>) reads either <chat-thread> or
|
|
27
|
+
* [data-chat-messages] via :is() selector for backwards compat.
|
|
28
|
+
* Message rendering lives in the host (one source of truth for
|
|
29
|
+
* markdown + escape rules); the thread just owns the scroll surface.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { UIElement } from '../../../web-components/core/element.js';
|
|
33
|
+
|
|
34
|
+
const SCROLL_BOTTOM_TOLERANCE = 40; // px from bottom that counts as "at bottom"
|
|
35
|
+
|
|
36
|
+
class ChatThread extends UIElement {
|
|
37
|
+
static properties = {
|
|
38
|
+
streaming: { type: Boolean, default: false, reflect: true },
|
|
39
|
+
empty: { type: Boolean, default: true, reflect: true },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
static template = () => null;
|
|
43
|
+
|
|
44
|
+
#childObserver = null;
|
|
45
|
+
#autoScroll = true;
|
|
46
|
+
#scrollHandler = null;
|
|
47
|
+
|
|
48
|
+
connected() {
|
|
49
|
+
this.#syncEmptyFromChildren();
|
|
50
|
+
this.#setupChildObserver();
|
|
51
|
+
this.#setupScrollListener();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
disconnected() {
|
|
55
|
+
this.#childObserver?.disconnect();
|
|
56
|
+
this.#childObserver = null;
|
|
57
|
+
if (this.#scrollHandler) {
|
|
58
|
+
this.removeEventListener('scroll', this.#scrollHandler);
|
|
59
|
+
this.#scrollHandler = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Public API ──
|
|
64
|
+
|
|
65
|
+
scrollToBottom() {
|
|
66
|
+
this.scrollTo({ top: this.scrollHeight, behavior: 'smooth' });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
scrollToBottomInstant() {
|
|
70
|
+
this.scrollTop = this.scrollHeight;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Internal: empty state from message children ──
|
|
74
|
+
|
|
75
|
+
#syncEmptyFromChildren() {
|
|
76
|
+
// Children that count as "messages" — anything except <chat-empty> stub
|
|
77
|
+
const messageChildren = Array.from(this.children).filter(
|
|
78
|
+
(c) => c.tagName.toLowerCase() !== 'chat-empty'
|
|
79
|
+
);
|
|
80
|
+
this.empty = messageChildren.length === 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#setupChildObserver() {
|
|
84
|
+
this.#childObserver = new MutationObserver(() => {
|
|
85
|
+
this.#syncEmptyFromChildren();
|
|
86
|
+
// New message added — scroll to bottom if user is at bottom
|
|
87
|
+
if (this.#autoScroll) {
|
|
88
|
+
this.scrollToBottomInstant();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
this.#childObserver.observe(this, { childList: true });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Internal: track user scroll to suspend auto-scroll ──
|
|
95
|
+
|
|
96
|
+
#setupScrollListener() {
|
|
97
|
+
this.#scrollHandler = () => {
|
|
98
|
+
const distanceFromBottom = this.scrollHeight - (this.scrollTop + this.clientHeight);
|
|
99
|
+
this.#autoScroll = distanceFromBottom <= SCROLL_BOTTOM_TOLERANCE;
|
|
100
|
+
};
|
|
101
|
+
this.addEventListener('scroll', this.#scrollHandler, { passive: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
customElements.define('chat-thread', ChatThread);
|
|
106
|
+
export { ChatThread };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import '../../../web-components/core/element.js';
|
|
3
|
+
import './chat-thread.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
|
+
let originalRect;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
document.body.innerHTML = '';
|
|
17
|
+
globalThis.MutationObserver = class {
|
|
18
|
+
observe() {} disconnect() {}
|
|
19
|
+
};
|
|
20
|
+
originalRect = HTMLElement.prototype.getBoundingClientRect;
|
|
21
|
+
HTMLElement.prototype.getBoundingClientRect = function () {
|
|
22
|
+
return { width: 480, height: 600, top: 0, left: 0, right: 480, bottom: 600, x: 0, y: 0 };
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
if (originalRect) HTMLElement.prototype.getBoundingClientRect = originalRect;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('chat-thread', () => {
|
|
31
|
+
it('registers chat-thread as a custom element', () => {
|
|
32
|
+
expect(customElements.get('chat-thread')).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('defaults to streaming=false on connect', () => {
|
|
36
|
+
const t = mount('<chat-thread></chat-thread>');
|
|
37
|
+
expect(t.streaming).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('defaults to empty=true when no children', async () => {
|
|
41
|
+
const t = mount('<chat-thread></chat-thread>');
|
|
42
|
+
await tick();
|
|
43
|
+
expect(t.empty).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('reflects [streaming] via property assignment', async () => {
|
|
47
|
+
const t = mount('<chat-thread></chat-thread>');
|
|
48
|
+
t.streaming = true;
|
|
49
|
+
await tick();
|
|
50
|
+
expect(t.hasAttribute('streaming')).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('reflects [empty] via property assignment', async () => {
|
|
54
|
+
const t = mount('<chat-thread></chat-thread>');
|
|
55
|
+
t.empty = false;
|
|
56
|
+
await tick();
|
|
57
|
+
expect(t.hasAttribute('empty')).toBe(false);
|
|
58
|
+
t.empty = true;
|
|
59
|
+
await tick();
|
|
60
|
+
expect(t.hasAttribute('empty')).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('exposes .scrollToBottom() and .scrollToBottomInstant() methods', () => {
|
|
64
|
+
const t = mount('<chat-thread></chat-thread>');
|
|
65
|
+
expect(typeof t.scrollToBottom).toBe('function');
|
|
66
|
+
expect(typeof t.scrollToBottomInstant).toBe('function');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('does not count <chat-empty> stub as a message child for [empty] calc', () => {
|
|
70
|
+
const t = mount('<chat-thread><chat-empty></chat-empty></chat-thread>');
|
|
71
|
+
// chat-empty alone — empty should still be true
|
|
72
|
+
expect(t.empty).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('cleanup on disconnect — removes scroll listener', () => {
|
|
76
|
+
const t = mount('<chat-thread></chat-thread>');
|
|
77
|
+
const removeSpy = vi.spyOn(t, 'removeEventListener');
|
|
78
|
+
t.remove();
|
|
79
|
+
const removedTypes = removeSpy.mock.calls.map((args) => args[0]);
|
|
80
|
+
expect(removedTypes).toContain('scroll');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Edit this file; run `npm run build:components` to regenerate a2ui.json.
|
|
2
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
3
|
+
name: ChatThread
|
|
4
|
+
tag: chat-thread
|
|
5
|
+
component: ChatThread
|
|
6
|
+
category: container
|
|
7
|
+
version: 1
|
|
8
|
+
description: |
|
|
9
|
+
Module-tier chat message thread container — replaces legacy
|
|
10
|
+
<section data-chat-messages> per ADR-0023. Owns scroll-to-bottom
|
|
11
|
+
on new message (with user-scroll-up suspension), [streaming] and
|
|
12
|
+
[empty] reflected attributes, and a stable target for the host's
|
|
13
|
+
message rendering pipeline.
|
|
14
|
+
|
|
15
|
+
Sits inside <chat-shell> as the central scroll surface. Authors
|
|
16
|
+
compose <chat-empty> as an optional first child for the empty
|
|
17
|
+
state; message children are appended dynamically by the host.
|
|
18
|
+
|
|
19
|
+
Backwards compat — <chat-shell> still recognizes the legacy
|
|
20
|
+
<section data-chat-messages> shape via :is() selector. New code
|
|
21
|
+
should prefer <chat-thread>.
|
|
22
|
+
|
|
23
|
+
props:
|
|
24
|
+
streaming:
|
|
25
|
+
description: |
|
|
26
|
+
Reflected — set by the host while an LLM response is streaming.
|
|
27
|
+
Consumers can style streaming-mode (e.g. cursor blink) via
|
|
28
|
+
:has(chat-thread[streaming]) or attribute selectors.
|
|
29
|
+
type: boolean
|
|
30
|
+
default: false
|
|
31
|
+
reflect: true
|
|
32
|
+
|
|
33
|
+
empty:
|
|
34
|
+
description: |
|
|
35
|
+
Reflected — set when zero message children. Drives the
|
|
36
|
+
<chat-empty> visibility via CSS — no JS toggling needed.
|
|
37
|
+
type: boolean
|
|
38
|
+
default: true
|
|
39
|
+
reflect: true
|
|
40
|
+
|
|
41
|
+
events: {}
|
|
42
|
+
|
|
43
|
+
slots:
|
|
44
|
+
default:
|
|
45
|
+
description: >-
|
|
46
|
+
Default — message children (typically appended dynamically by
|
|
47
|
+
<chat-shell>'s rendering pipeline) plus an optional first
|
|
48
|
+
<chat-empty> sibling for the empty state.
|
|
49
|
+
|
|
50
|
+
states:
|
|
51
|
+
- name: idle
|
|
52
|
+
description: Default, no streaming.
|
|
53
|
+
- name: streaming
|
|
54
|
+
attribute: streaming
|
|
55
|
+
description: Host is actively streaming an LLM response.
|
|
56
|
+
- name: empty
|
|
57
|
+
attribute: empty
|
|
58
|
+
description: Zero message children — empty state visible.
|
|
59
|
+
|
|
60
|
+
traits: []
|
|
61
|
+
|
|
62
|
+
a2ui:
|
|
63
|
+
rules:
|
|
64
|
+
- >-
|
|
65
|
+
chat-thread is the bespoke replacement for legacy
|
|
66
|
+
<section data-chat-messages> inside <chat-shell>. Use it for
|
|
67
|
+
the message scroll surface; the host appends dynamic message
|
|
68
|
+
divs as children.
|
|
69
|
+
- >-
|
|
70
|
+
Place <chat-empty> as an optional first child for the empty
|
|
71
|
+
state; the [empty] reflected attribute drives its visibility
|
|
72
|
+
via CSS (no JS toggling).
|
|
73
|
+
|
|
74
|
+
keywords:
|
|
75
|
+
- chat-thread
|
|
76
|
+
- message-list
|
|
77
|
+
- conversation
|
|
78
|
+
- thread
|
|
79
|
+
- scroll-surface
|
|
80
|
+
|
|
81
|
+
synonyms:
|
|
82
|
+
thread: [conversation, dialogue, chat-log, transcript]
|
|
83
|
+
message-list: [messages, message-stream]
|
|
84
|
+
|
|
85
|
+
related:
|
|
86
|
+
- ChatShell
|
|
87
|
+
- ChatEmpty
|
|
88
|
+
- ChatComposer
|
|
89
|
+
- ChatSidebar
|
package/chat/index.js
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Editor Shell</h1>
|
|
4
|
+
<div data-actions>
|
|
5
|
+
<tag-ui size="sm">editor-shell</tag-ui>
|
|
6
|
+
<tag-ui size="sm" variant="ghost">module-tier</tag-ui>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
<p>Behavior-only editor shell module for design-tool layouts. Author provides the structural DOM — header (topbar), <code>[data-editor-body]</code> with pane-ui children (<code>[data-left]</code>, <code>[data-right]</code>), <code>[data-canvas]</code> for the central surface, and a footer (statusbar). editor-shell wires <code>select-ui[data-options]</code> for JSON-parsed option lists.</p>
|
|
10
|
+
</header>
|
|
11
|
+
|
|
12
|
+
<section data-section>
|
|
13
|
+
<h2 variant="section">Basic shape</h2>
|
|
14
|
+
<p data-note>Author supplies the structural DOM — header, body with pane-ui rails + canvas, footer. The shell binds toolbar select wiring + responsive pane collapse.</p>
|
|
15
|
+
<code-ui language="html"><editor-shell>
|
|
16
|
+
<header>
|
|
17
|
+
<icon-ui slot="icon" name="layers"></icon-ui>
|
|
18
|
+
<span slot="heading">Untitled.fig</span>
|
|
19
|
+
<span slot="description">1920 × 1080</span>
|
|
20
|
+
<button-ui slot="action" variant="ghost">Share</button-ui>
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<div data-editor-body>
|
|
24
|
+
<pane-ui data-left>…tools panel…</pane-ui>
|
|
25
|
+
<div data-canvas>…central canvas…</div>
|
|
26
|
+
<pane-ui data-right>…inspector panel…</pane-ui>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<footer>
|
|
30
|
+
<icon-ui slot="icon" name="zoom-in"></icon-ui>
|
|
31
|
+
<span>100%</span>
|
|
32
|
+
</footer>
|
|
33
|
+
</editor-shell></code-ui>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<section data-section>
|
|
37
|
+
<h2 variant="section">Slots</h2>
|
|
38
|
+
<table>
|
|
39
|
+
<thead><tr><th>Slot</th><th>Purpose</th></tr></thead>
|
|
40
|
+
<tbody>
|
|
41
|
+
<tr><td><code>icon</code></td><td>Leading glyph in <code><header></code> or <code><footer></code> — status dot, app icon, zoom badge.</td></tr>
|
|
42
|
+
<tr><td><code>heading</code></td><td>Primary label inside chrome bars.</td></tr>
|
|
43
|
+
<tr><td><code>description</code></td><td>Secondary metadata (document name, artboard size, zoom level).</td></tr>
|
|
44
|
+
<tr><td><code>action</code></td><td>Trailing control cluster. First <code>[slot="action"]</code> child pushes itself + siblings to the end.</td></tr>
|
|
45
|
+
<tr><td><code>action-leading</code></td><td>Leading control cluster (back, undo). Pairs with <code>action</code> for dual-cluster bars.</td></tr>
|
|
46
|
+
</tbody>
|
|
47
|
+
</table>
|
|
48
|
+
</section>
|
|
49
|
+
|
|
50
|
+
<section data-section>
|
|
51
|
+
<h2 variant="section">Behavior wiring</h2>
|
|
52
|
+
<table>
|
|
53
|
+
<thead><tr><th>Affordance</th><th>Author markup</th><th>What the shell does</th></tr></thead>
|
|
54
|
+
<tbody>
|
|
55
|
+
<tr><td>Editor body</td><td><code>[data-editor-body]</code> wrapping <code>pane-ui[data-left]</code>, <code>[data-canvas]</code>, <code>pane-ui[data-right]</code></td><td>Lays out the canvas + flanking panes; handles responsive collapse.</td></tr>
|
|
56
|
+
<tr><td>Toolbar selects</td><td><code>select-ui[data-options]</code> with JSON in the attribute</td><td>Parses <code>data-options</code> JSON to populate option lists at connect.</td></tr>
|
|
57
|
+
</tbody>
|
|
58
|
+
</table>
|
|
59
|
+
</section>
|
|
60
|
+
|
|
61
|
+
<section data-section>
|
|
62
|
+
<h2 variant="section">Family pattern (forward-looking)</h2>
|
|
63
|
+
<p>Per <a href="../../../../.brain/adrs/0023-bespoke-shell-tier-children.md">ADR-0023</a>, future releases extend the bespoke-children family to the editor cluster. Planned children:</p>
|
|
64
|
+
<ul data-features>
|
|
65
|
+
<li><code><editor-canvas></code> — replaces <code>[data-canvas]</code>; owns zoom/pan, viewport state</li>
|
|
66
|
+
<li><code><editor-toolbar></code> — replaces toolbar headers; owns select-ui wiring</li>
|
|
67
|
+
<li><code><editor-inspector></code> + <code><editor-tools></code> — replace <code>pane-ui[data-left/right]</code>; owns pane state attributes</li>
|
|
68
|
+
<li><code><editor-statusbar></code> — zoom level, artboard info</li>
|
|
69
|
+
</ul>
|
|
70
|
+
<p data-note>Today's <code>data-*</code> shape is fully supported and will continue to work alongside the bespoke vocabulary when it lands.</p>
|
|
71
|
+
</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 Shell — 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.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.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-shell.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-shell.examples.html — ${err.message}</p>`;
|
|
37
|
+
console.error('[editor-shell.html]', err);
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-modules",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "AdiaUI composite custom elements \u2014 shell, chat, editor, runtime clusters built from @adia-ai/web-components primitives. Subpath exports per cluster.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/AdminCommand.json",
|
|
4
|
+
"title": "AdminCommand",
|
|
5
|
+
"description": "Module-tier command palette wrapper — wraps a native <dialog> and the\ninner <command-ui>. Owns the keyboard shortcut listener, focus\nmanagement, and dismiss handlers. Reflects [open].\n\nSits inside <admin-shell> as a direct child. The host wires\n[data-command-trigger] elements to <admin-command>.show() via lookup.\n\nThis is the bespoke web-component replacement for the legacy\n<dialog data-command> shape. <admin-shell> still recognizes the\nlegacy shape via :is() selector. New code should prefer <admin-command>.\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": "AdminCommand"
|
|
18
|
+
},
|
|
19
|
+
"no-shortcut": {
|
|
20
|
+
"description": "Opts out of the keyboard listener entirely. Use when the host\nwires its own shortcut handling, or when the palette should be\nmouse-only.\n",
|
|
21
|
+
"type": "boolean",
|
|
22
|
+
"default": false
|
|
23
|
+
},
|
|
24
|
+
"open": {
|
|
25
|
+
"description": "Reflected — set while the dialog is showing. Synced with both\nprogrammatic .show()/.hide() and native dialog close events\n(esc key, backdrop click).\n",
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"default": false
|
|
28
|
+
},
|
|
29
|
+
"shortcut": {
|
|
30
|
+
"description": "Keyboard shortcut binding. \"cmd+k\" responds only on Mac\n(metaKey); \"ctrl+k\" responds only when ctrlKey; \"both\" (default)\nresponds to either, which is the canonical AdiaUI behavior for\ncross-platform Cmd+K affordance.\n",
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": [
|
|
33
|
+
"both",
|
|
34
|
+
"cmd+k",
|
|
35
|
+
"ctrl+k"
|
|
36
|
+
],
|
|
37
|
+
"default": "both"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"required": [
|
|
41
|
+
"component"
|
|
42
|
+
],
|
|
43
|
+
"unevaluatedProperties": false,
|
|
44
|
+
"x-adiaui": {
|
|
45
|
+
"anti_patterns": [],
|
|
46
|
+
"category": "interaction",
|
|
47
|
+
"events": {
|
|
48
|
+
"command-select": {
|
|
49
|
+
"description": "Forwarded from the inner <command-ui> when an option is chosen. Detail mirrors the inner event's detail.",
|
|
50
|
+
"detail": {
|
|
51
|
+
"value": "string"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"examples": [],
|
|
56
|
+
"keywords": [
|
|
57
|
+
"admin-command",
|
|
58
|
+
"command-palette",
|
|
59
|
+
"palette",
|
|
60
|
+
"cmd-k",
|
|
61
|
+
"keyboard-shortcut",
|
|
62
|
+
"quickaction"
|
|
63
|
+
],
|
|
64
|
+
"name": "AdminCommand",
|
|
65
|
+
"related": [
|
|
66
|
+
"AdminShell",
|
|
67
|
+
"AdminSidebar",
|
|
68
|
+
"Command"
|
|
69
|
+
],
|
|
70
|
+
"slots": {
|
|
71
|
+
"default": {
|
|
72
|
+
"description": "Default slot — the inner <command-ui> (and any other content inside the dialog). Authors typically place exactly one <command-ui placeholder=\"…\">."
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"states": [
|
|
76
|
+
{
|
|
77
|
+
"description": "Default, palette closed.",
|
|
78
|
+
"name": "idle"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"description": "Dialog is showing; first input has focus.",
|
|
82
|
+
"attribute": "open",
|
|
83
|
+
"name": "open"
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"synonyms": {
|
|
87
|
+
"cmd-k": [
|
|
88
|
+
"ctrl-k",
|
|
89
|
+
"command-shortcut"
|
|
90
|
+
],
|
|
91
|
+
"command-palette": [
|
|
92
|
+
"palette",
|
|
93
|
+
"quick-action",
|
|
94
|
+
"omnibox"
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
"tag": "admin-command",
|
|
98
|
+
"tokens": {},
|
|
99
|
+
"traits": [],
|
|
100
|
+
"version": 1
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Admin Command</h1>
|
|
4
|
+
<div data-actions>
|
|
5
|
+
<tag-ui size="sm">admin-command</tag-ui>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<p>Module-tier command palette wrapper — wraps a native <code><dialog></code> and the inner <code><command-ui></code>. Owns the keyboard shortcut listener, focus management, and dismiss handlers. Reflects <code>[open]</code>.</p>
|
|
9
|
+
</header>
|
|
10
|
+
|
|
11
|
+
<section data-section>
|
|
12
|
+
<h2 variant="section">Basic shape</h2>
|
|
13
|
+
<p data-note>Place <code><admin-command></code> as a direct child of <code><admin-shell></code>. The host wires <code>[data-command-trigger]</code> elements to <code>.show()</code> via lookup; you don't reach across siblings.</p>
|
|
14
|
+
<code-ui language="html"><admin-shell>
|
|
15
|
+
…sidebars + content…
|
|
16
|
+
|
|
17
|
+
<admin-command>
|
|
18
|
+
<command-ui placeholder="Search…"></command-ui>
|
|
19
|
+
</admin-command>
|
|
20
|
+
</admin-shell></code-ui>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<section data-section>
|
|
24
|
+
<h2 variant="section">Keyboard shortcut</h2>
|
|
25
|
+
<table>
|
|
26
|
+
<thead><tr><th>Value</th><th>Behavior</th></tr></thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
<tr><td><code>both</code> (default)</td><td>Responds to Cmd+K (mac) AND Ctrl+K (other platforms) — the AdiaUI cross-platform convention.</td></tr>
|
|
29
|
+
<tr><td><code>cmd+k</code></td><td>Responds only when <code>metaKey</code> is held.</td></tr>
|
|
30
|
+
<tr><td><code>ctrl+k</code></td><td>Responds only when <code>ctrlKey</code> is held.</td></tr>
|
|
31
|
+
<tr><td><code>[no-shortcut]</code></td><td>Disables the keyboard listener entirely. Use when the host wires its own shortcut handling.</td></tr>
|
|
32
|
+
</tbody>
|
|
33
|
+
</table>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<section data-section>
|
|
37
|
+
<h2 variant="section">State as attribute</h2>
|
|
38
|
+
<table>
|
|
39
|
+
<thead><tr><th>Attribute</th><th>When set</th><th>Use for</th></tr></thead>
|
|
40
|
+
<tbody>
|
|
41
|
+
<tr><td><code>open</code></td><td>Dialog is showing</td><td>CSS <code>:has(admin-command[open])</code>; JS <code>.hasAttribute('open')</code>; programmatic round-trip via <code>.toggle()</code></td></tr>
|
|
42
|
+
</tbody>
|
|
43
|
+
</table>
|
|
44
|
+
<p>The <code>[open]</code> attribute is synced both ways — programmatic <code>.show()</code> sets it, native dialog close (esc key, backdrop click) clears it.</p>
|
|
45
|
+
</section>
|
|
46
|
+
|
|
47
|
+
<section data-section>
|
|
48
|
+
<h2 variant="section">Public methods</h2>
|
|
49
|
+
<table>
|
|
50
|
+
<thead><tr><th>Method</th><th>Effect</th></tr></thead>
|
|
51
|
+
<tbody>
|
|
52
|
+
<tr><td><code>.show()</code></td><td>Open via <code>showModal()</code>; focus the inner <code>command-ui</code>; clear its value.</td></tr>
|
|
53
|
+
<tr><td><code>.hide()</code></td><td>Close the dialog; clear inner palette state.</td></tr>
|
|
54
|
+
<tr><td><code>.toggle()</code></td><td>Flip open state. Returns the new <code>open</code> value.</td></tr>
|
|
55
|
+
</tbody>
|
|
56
|
+
</table>
|
|
57
|
+
</section>
|
|
58
|
+
|
|
59
|
+
<section data-section>
|
|
60
|
+
<h2 variant="section">Events</h2>
|
|
61
|
+
<table>
|
|
62
|
+
<thead><tr><th>Event</th><th>Detail</th></tr></thead>
|
|
63
|
+
<tbody>
|
|
64
|
+
<tr><td><code>command-select</code></td><td>Forwarded from the inner <code>command-ui</code> when an option is chosen. Detail mirrors the inner event's detail.</td></tr>
|
|
65
|
+
</tbody>
|
|
66
|
+
</table>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<section data-section>
|
|
70
|
+
<h2 variant="section">Trigger wiring</h2>
|
|
71
|
+
<p>Triggers (any element with <code>[data-command-trigger]</code>) live anywhere in the shell — typically a search row in the leading sidebar. The host (<code><admin-shell></code>) finds the inner <code><admin-command></code> on connect and wires every trigger's click to <code>command.show()</code>.</p>
|
|
72
|
+
<code-ui language="html"><admin-shell>
|
|
73
|
+
<admin-sidebar slot="leading">
|
|
74
|
+
<nav-ui>
|
|
75
|
+
<nav-item-ui data-command-trigger icon="magnifying-glass">Search</nav-item-ui>
|
|
76
|
+
</nav-ui>
|
|
77
|
+
</admin-sidebar>
|
|
78
|
+
|
|
79
|
+
<admin-command>
|
|
80
|
+
<command-ui placeholder="Search…"></command-ui>
|
|
81
|
+
</admin-command>
|
|
82
|
+
</admin-shell></code-ui>
|
|
83
|
+
</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>Admin Command — 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="../admin-shell/admin-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="./admin-command.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('./admin-command.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 admin-command.examples.html — ${err.message}</p>`;
|
|
37
|
+
console.error('[admin-command.html]', err);
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|