@adia-ai/web-components 0.0.28 → 0.0.29

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 (69) hide show
  1. package/README.md +4 -8
  2. package/a2ui/index.js +1 -1
  3. package/components/canvas/canvas.js +1 -1
  4. package/components/feed/feed.css +9 -0
  5. package/components/feed/feed.js +118 -9
  6. package/components/toast/toast.js +48 -178
  7. package/index.css +3 -2
  8. package/index.js +15 -7
  9. package/package.json +1 -5
  10. package/patterns/a2ui-root/a2ui-root.a2ui.json +0 -125
  11. package/patterns/a2ui-root/a2ui-root.js +0 -191
  12. package/patterns/a2ui-root/a2ui-root.yaml +0 -87
  13. package/patterns/adia-chat/adia-chat.a2ui.json +0 -149
  14. package/patterns/adia-chat/adia-chat.css +0 -10
  15. package/patterns/adia-chat/adia-chat.js +0 -297
  16. package/patterns/adia-chat/adia-chat.yaml +0 -118
  17. package/patterns/adia-chat/css/adia-chat.empty.css +0 -12
  18. package/patterns/adia-chat/css/adia-chat.layout.css +0 -60
  19. package/patterns/adia-chat/css/adia-chat.markdown.css +0 -74
  20. package/patterns/adia-chat/css/adia-chat.messages.css +0 -87
  21. package/patterns/adia-chat/css/adia-chat.streaming.css +0 -30
  22. package/patterns/adia-chat/css/adia-chat.tokens.css +0 -95
  23. package/patterns/adia-editor/adia-editor.a2ui.json +0 -73
  24. package/patterns/adia-editor/adia-editor.css +0 -6
  25. package/patterns/adia-editor/adia-editor.js +0 -56
  26. package/patterns/adia-editor/adia-editor.yaml +0 -59
  27. package/patterns/adia-editor/css/adia-editor.layout.css +0 -171
  28. package/patterns/adia-editor/css/adia-editor.tokens.css +0 -28
  29. package/patterns/app-nav/app-nav.a2ui.json +0 -89
  30. package/patterns/app-nav/app-nav.css +0 -92
  31. package/patterns/app-nav/app-nav.js +0 -112
  32. package/patterns/app-nav/app-nav.yaml +0 -54
  33. package/patterns/app-nav-group/app-nav-group.a2ui.json +0 -82
  34. package/patterns/app-nav-group/app-nav-group.css +0 -264
  35. package/patterns/app-nav-group/app-nav-group.js +0 -116
  36. package/patterns/app-nav-group/app-nav-group.yaml +0 -59
  37. package/patterns/app-nav-item/app-nav-item.a2ui.json +0 -83
  38. package/patterns/app-nav-item/app-nav-item.css +0 -162
  39. package/patterns/app-nav-item/app-nav-item.js +0 -42
  40. package/patterns/app-nav-item/app-nav-item.yaml +0 -62
  41. package/patterns/app-shell/app-shell.a2ui.json +0 -129
  42. package/patterns/app-shell/app-shell.css +0 -14
  43. package/patterns/app-shell/app-shell.js +0 -251
  44. package/patterns/app-shell/app-shell.yaml +0 -89
  45. package/patterns/app-shell/css/app-shell.collapsed.css +0 -86
  46. package/patterns/app-shell/css/app-shell.helpers.css +0 -42
  47. package/patterns/app-shell/css/app-shell.main.css +0 -172
  48. package/patterns/app-shell/css/app-shell.shell.css +0 -44
  49. package/patterns/app-shell/css/app-shell.sidebar.css +0 -161
  50. package/patterns/app-shell/css/app-shell.templates.css +0 -214
  51. package/patterns/app-shell/css/app-shell.tokens.css +0 -119
  52. package/patterns/gen-ui/gen-ui.a2ui.json +0 -72
  53. package/patterns/gen-ui/gen-ui.css +0 -83
  54. package/patterns/gen-ui/gen-ui.js +0 -136
  55. package/patterns/gen-ui/gen-ui.yaml +0 -43
  56. package/patterns/index.js +0 -11
  57. package/patterns/section-nav/section-nav.a2ui.json +0 -91
  58. package/patterns/section-nav/section-nav.css +0 -60
  59. package/patterns/section-nav/section-nav.js +0 -42
  60. package/patterns/section-nav/section-nav.yaml +0 -58
  61. package/patterns/section-nav-group/section-nav-group.a2ui.json +0 -95
  62. package/patterns/section-nav-group/section-nav-group.css +0 -74
  63. package/patterns/section-nav-group/section-nav-group.js +0 -84
  64. package/patterns/section-nav-group/section-nav-group.yaml +0 -66
  65. package/patterns/section-nav-item/section-nav-item.a2ui.json +0 -97
  66. package/patterns/section-nav-item/section-nav-item.css +0 -106
  67. package/patterns/section-nav-item/section-nav-item.js +0 -66
  68. package/patterns/section-nav-item/section-nav-item.yaml +0 -70
  69. package/styles/layouts/admin.css +0 -7
@@ -1,297 +0,0 @@
1
- import { AdiaElement } from '../../core/element.js';
2
- import { renderMarkdown } from '../../core/markdown.js';
3
- import { streamChat } from '../../../a2ui/compose/llm/adapters/index.js';
4
-
5
- function escapeHTML(s) {
6
- return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
7
- }
8
-
9
- let msgId = 0;
10
-
11
- /**
12
- * <adia-chat-ui> — Chat pattern component.
13
- *
14
- * Behavior-only orchestrator: stamps no HTML of its own.
15
- * The author writes the structure; adia-chat wires the behaviors.
16
- *
17
- * Structure:
18
- * <adia-chat-ui proxy-url="/api/chat" model="claude-sonnet-4-20250514">
19
- * <header>
20
- * <span data-chat-name>Claude</span>
21
- * <span data-chat-status></span>
22
- * </header>
23
- * <section data-chat-messages>
24
- * <empty-state-ui data-chat-empty icon="chat-circle"
25
- * heading="Hello!" description="Ask me anything.">
26
- * </empty-state-ui>
27
- * </section>
28
- * <footer>
29
- * <chat-input-ui data-chat-input placeholder="Message..."></chat-input-ui>
30
- * </footer>
31
- * </adia-chat-ui>
32
- *
33
- * Two modes:
34
- * 1. Built-in: set proxy-url + model, adia-chat streams via LLM adapters
35
- * 2. External: listen for 'submit', call your API, use appendChunk() etc.
36
- *
37
- * Events: submit, chunk, thinking, done, error, abort, clear
38
- */
39
- class AdiaChatElement extends AdiaElement {
40
- static properties = {
41
- streaming: { type: Boolean, default: false, reflect: true },
42
- provider: { type: String, default: '', reflect: true },
43
- model: { type: String, default: '', reflect: true },
44
- system: { type: String, default: '', reflect: true },
45
- proxyUrl: { type: String, default: '', attribute: 'proxy-url', reflect: true },
46
- thinking: { type: Boolean, default: false, reflect: true },
47
- };
48
-
49
- static template = () => null;
50
-
51
- #messages = [];
52
- #abortController = null;
53
- #messagesEl = null;
54
- #inputEl = null;
55
- #emptyEl = null;
56
- #statusEl = null;
57
- #apiKey = '';
58
-
59
- // ── Accessors ──
60
-
61
- get messages() { return [...this.#messages]; }
62
-
63
- get conversation() {
64
- return this.#messages
65
- .filter(m => m.role === 'user' || m.role === 'assistant')
66
- .map(m => ({ role: m.role, content: m.content }));
67
- }
68
-
69
- set conversation(msgs) {
70
- this.clear();
71
- for (const m of msgs) {
72
- this.appendMessage({ role: m.role, content: m.content, render: true });
73
- }
74
- }
75
-
76
- set apiKey(key) { this.#apiKey = key; }
77
-
78
- // ── Lifecycle ──
79
-
80
- connected() {
81
- this.#messagesEl = this.querySelector('[data-chat-messages]') || this.querySelector('section');
82
- this.#inputEl = this.querySelector('[data-chat-input]') || this.querySelector('chat-input-ui');
83
- this.#emptyEl = this.querySelector('[data-chat-empty]');
84
- this.#statusEl = this.querySelector('[data-chat-status]');
85
-
86
- this.#inputEl?.addEventListener('submit', this.#onSubmit);
87
- }
88
-
89
- disconnected() {
90
- this.#inputEl?.removeEventListener('submit', this.#onSubmit);
91
- this.abort();
92
- }
93
-
94
- // ── Events ──
95
-
96
- #emit(name, detail) {
97
- this.dispatchEvent(new CustomEvent(name, { bubbles: true, detail }));
98
- }
99
-
100
- #onSubmit = (e) => {
101
- if (this.streaming) return;
102
- const { text, model } = e.detail || {};
103
- if (!text) return;
104
- this.#inputEl.clear();
105
-
106
- this.#emit('submit', { text, model });
107
-
108
- // If proxy-url or apiKey is set, auto-send
109
- if (this.proxyUrl || this.#apiKey) {
110
- this.send(text, model ? { model } : {});
111
- }
112
- };
113
-
114
- // ── Message management ──
115
-
116
- appendMessage({ role, content = '', render: renderMd = false }) {
117
- const id = `msg_${++msgId}`;
118
- this.#messages.push({ id, role, content });
119
-
120
- const el = document.createElement('div');
121
- el.setAttribute('data-role', role);
122
- el.setAttribute('data-id', id);
123
-
124
- if (role === 'user') {
125
- el.innerHTML = `<div data-bubble>${escapeHTML(content)}</div>`;
126
- } else if (role === 'assistant') {
127
- const rendered = renderMd && content ? renderMarkdown(content) : escapeHTML(content);
128
- el.innerHTML = `
129
- <span data-avatar>AI</span>
130
- <div data-bubble><div data-content>${rendered}</div>${!renderMd ? '<span data-cursor></span>' : ''}</div>
131
- `;
132
- } else if (role === 'error') {
133
- el.innerHTML = `<icon-ui name="warning"></icon-ui><span>${escapeHTML(content)}</span>`;
134
- }
135
-
136
- this.#messagesEl?.appendChild(el);
137
- this.#scrollToBottom();
138
- this.#emit('message', { id, role, content });
139
- return el;
140
- }
141
-
142
- appendChunk(text) {
143
- const last = this.#messages[this.#messages.length - 1];
144
- if (!last || last.role !== 'assistant') return;
145
- last.content += text;
146
-
147
- const contentEl = this.#messagesEl?.querySelector('[data-role]:last-child [data-content]');
148
- if (contentEl) {
149
- contentEl.insertAdjacentText('beforeend', text);
150
- }
151
- this.#scrollToBottom();
152
- }
153
-
154
- deleteMessage(id) {
155
- const idx = this.#messages.findIndex(m => m.id === id);
156
- if (idx === -1) return;
157
- this.#messages.splice(idx, 1);
158
- this.#messagesEl?.querySelector(`[data-id="${id}"]`)?.remove();
159
- }
160
-
161
- clear() {
162
- this.#messages.length = 0;
163
- if (this.#messagesEl) {
164
- // Preserve empty state element
165
- const empty = this.#emptyEl;
166
- this.#messagesEl.innerHTML = '';
167
- if (empty) this.#messagesEl.appendChild(empty);
168
- }
169
- this.#emit('clear');
170
- }
171
-
172
- // ── Streaming control ──
173
-
174
- startStreaming() {
175
- this.streaming = true;
176
- if (this.#inputEl) this.#inputEl.disabled = true;
177
- if (this.#statusEl) this.#statusEl.textContent = 'Typing...';
178
- }
179
-
180
- stopStreaming() {
181
- this.streaming = false;
182
- if (this.#inputEl) this.#inputEl.disabled = false;
183
- if (this.#statusEl) this.#statusEl.textContent = '';
184
-
185
- // Remove cursor
186
- this.#messagesEl?.querySelector('[data-role]:last-child [data-cursor]')?.remove();
187
-
188
- // Render markdown in last assistant bubble
189
- const last = this.#messages[this.#messages.length - 1];
190
- if (last?.role === 'assistant' && last.content) {
191
- const contentEl = this.#messagesEl?.querySelector('[data-role]:last-child [data-content]');
192
- if (contentEl) {
193
- contentEl.innerHTML = renderMarkdown(last.content);
194
- // Upgrade code blocks to code-ui
195
- this.#upgradeCodeBlocks(contentEl);
196
- }
197
- }
198
-
199
- this.#inputEl?.focus();
200
- }
201
-
202
- abort() {
203
- if (this.#abortController) {
204
- this.#abortController.abort();
205
- this.#abortController = null;
206
- this.#emit('abort');
207
- }
208
- if (this.streaming) this.stopStreaming();
209
- }
210
-
211
- // ── LLM send ──
212
-
213
- async send(text, opts = {}) {
214
- const model = opts.model || this.model || this.#inputEl?.model;
215
- if (!model) throw new Error('No model specified');
216
-
217
- this.appendMessage({ role: 'user', content: text });
218
- this.appendMessage({ role: 'assistant', content: '' });
219
- this.startStreaming();
220
-
221
- this.#abortController = new AbortController();
222
-
223
- try {
224
- const streamOpts = {
225
- provider: this.provider || undefined,
226
- apiKey: this.#apiKey || undefined,
227
- model,
228
- system: this.system || undefined,
229
- proxyUrl: this.proxyUrl || undefined,
230
- thinking: this.thinking || undefined,
231
- messages: this.conversation.slice(0, -1), // exclude empty assistant
232
- signal: this.#abortController.signal,
233
- ...opts,
234
- };
235
-
236
- for await (const chunk of streamChat(streamOpts)) {
237
- if (chunk.type === 'text') {
238
- this.appendChunk(chunk.text);
239
- this.#emit('chunk', { text: chunk.text, snapshot: chunk.snapshot });
240
- } else if (chunk.type === 'thinking') {
241
- this.#emit('thinking', { text: chunk.text });
242
- } else if (chunk.type === 'done') {
243
- this.#emit('done', { text: chunk.text, usage: chunk.usage, stopReason: chunk.stopReason });
244
- } else if (chunk.type === 'error') {
245
- this.appendMessage({ role: 'error', content: chunk.error.message });
246
- this.#emit('error', { error: chunk.error });
247
- }
248
- }
249
- } catch (err) {
250
- if (err.name !== 'AbortError') {
251
- this.appendMessage({ role: 'error', content: err.message });
252
- this.#emit('error', { error: err });
253
- }
254
- }
255
-
256
- this.#abortController = null;
257
- this.stopStreaming();
258
- }
259
-
260
- // ── Export / import ──
261
-
262
- export() {
263
- return {
264
- messages: this.#messages.map(m => ({ role: m.role, content: m.content })),
265
- model: this.model,
266
- system: this.system,
267
- };
268
- }
269
-
270
- import(data) {
271
- if (data.model) this.model = data.model;
272
- if (data.system) this.system = data.system;
273
- if (data.messages) this.conversation = data.messages;
274
- }
275
-
276
- // ── Private ──
277
-
278
- #scrollToBottom() {
279
- const el = this.#messagesEl;
280
- if (el) requestAnimationFrame(() => { el.scrollTop = el.scrollHeight; });
281
- }
282
-
283
- #upgradeCodeBlocks(container) {
284
- for (const pre of container.querySelectorAll('pre')) {
285
- const code = pre.querySelector('code');
286
- if (!code) continue;
287
- const lang = code.getAttribute('data-lang') || '';
288
- const codeN = document.createElement('code-ui');
289
- if (lang) codeN.setAttribute('language', lang);
290
- codeN.textContent = code.textContent;
291
- pre.replaceWith(codeN);
292
- }
293
- }
294
- }
295
-
296
- customElements.define('adia-chat-ui', AdiaChatElement);
297
- export { AdiaChatElement };
@@ -1,118 +0,0 @@
1
- $schema: ../../../../scripts/schemas/component.yaml.schema.json
2
- name: AdiaChatElement
3
- tag: adia-chat-ui
4
- component: Chat
5
- category: container
6
- version: 1
7
- description: |
8
- Behavior-only chat orchestrator. Author supplies the DOM structure via
9
- [data-chat-messages], [data-chat-input], [data-chat-empty], [data-chat-status]
10
- elements; adia-chat wires message streaming, markdown rendering, code-block
11
- upgrades, and an LLM integration path via proxy-url (or via external submit).
12
-
13
- props:
14
- streaming:
15
- type: boolean
16
- default: false
17
- reflect: true
18
- description: Active streaming indicator; toggled while a response is being received.
19
-
20
- provider:
21
- type: string
22
- default: ""
23
- reflect: true
24
- description: LLM provider name (anthropic | openai | google | stub).
25
-
26
- model:
27
- type: string
28
- default: ""
29
- reflect: true
30
- description: Model identifier.
31
-
32
- system:
33
- type: string
34
- default: ""
35
- reflect: true
36
- description: System prompt prepended to conversations.
37
-
38
- proxyUrl:
39
- type: string
40
- default: ""
41
- reflect: true
42
- attribute: proxy-url
43
- description: API proxy endpoint for LLM calls; enables self-contained chat without external wiring.
44
-
45
- thinking:
46
- type: boolean
47
- default: false
48
- reflect: true
49
- description: Enable Anthropic extended-thinking mode.
50
-
51
- events:
52
- submit:
53
- description: Fired on user message submit (before LLM call begins).
54
- detail:
55
- text: string
56
- model: string
57
-
58
- chunk:
59
- description: Fired for each streaming chunk.
60
- detail:
61
- text: string
62
- snapshot: string
63
-
64
- thinking:
65
- description: Fired when the model emits extended-thinking content.
66
- detail:
67
- text: string
68
-
69
- done:
70
- description: Fired when a response completes.
71
- detail:
72
- text: string
73
- usage: object
74
- stopReason: string
75
-
76
- error:
77
- description: Fired on any LLM / network error.
78
- detail:
79
- error: Error
80
-
81
- abort:
82
- description: Fired when the user aborts an in-flight request.
83
-
84
- clear:
85
- description: Fired when the conversation is cleared.
86
-
87
- message:
88
- description: Fired after each message (user or assistant) is appended.
89
- detail:
90
- id: string
91
- role: string
92
- content: string
93
-
94
- slots:
95
- default:
96
- description: >-
97
- Author provides the structural DOM. Expected markers —
98
- [data-chat-messages] (message list), [data-chat-input] (input surface),
99
- [data-chat-empty] (empty state), [data-chat-status] (streaming indicator).
100
-
101
- states:
102
- - name: idle
103
- description: No active request.
104
- - name: streaming
105
- attribute: streaming
106
- description: An LLM request is in-flight; [data-chat-status] visible.
107
-
108
- traits: []
109
-
110
- a2ui:
111
- rules:
112
- - adia-chat is a behavior wrapper; don't nest col-ui/row-ui inside it directly —
113
- author the structural DOM using [data-chat-*] markers instead.
114
-
115
- keywords: [adia-chat, chat, llm, streaming, conversation, agent]
116
- synonyms:
117
- chat: [conversation, messages, thread]
118
- related: [ChatInput, Code]
@@ -1,12 +0,0 @@
1
- /* ═══════════════════════════════════════════════════════════════
2
- adia-chat-ui — Empty state
3
- ═══════════════════════════════════════════════════════════════ */
4
-
5
- adia-chat-ui [data-chat-empty] {
6
- margin: auto;
7
- }
8
-
9
- adia-chat-ui[streaming] [data-chat-empty],
10
- adia-chat-ui:has([data-role]) [data-chat-empty] {
11
- display: none;
12
- }
@@ -1,60 +0,0 @@
1
- /* ═══════════════════════════════════════════════════════════════
2
- adia-chat-ui — Root layout
3
- ═══════════════════════════════════════════════════════════════ */
4
-
5
- adia-chat-ui {
6
- box-sizing: border-box;
7
- display: flex;
8
- flex-direction: column;
9
- height: 100%;
10
- overflow: hidden;
11
- border: var(--chat-border);
12
- border-radius: var(--chat-radius);
13
- background: var(--chat-bg);
14
- }
15
-
16
- /* Header */
17
- adia-chat-ui > header {
18
- display: flex;
19
- align-items: center;
20
- gap: var(--chat-header-gap);
21
- min-height: var(--chat-header-height);
22
- padding: 0 var(--chat-header-px);
23
- border-bottom: var(--chat-header-border);
24
- flex-shrink: 0;
25
- }
26
-
27
- adia-chat-ui > header [data-chat-name] {
28
- font-weight: var(--chat-weight-semibold);
29
- font-size: var(--chat-header-name-font);
30
- }
31
-
32
- adia-chat-ui > header [data-chat-status] {
33
- font-size: var(--chat-header-status-font);
34
- color: var(--chat-header-status-fg);
35
- margin-inline-start: auto;
36
- }
37
-
38
- /* Messages scroll container */
39
- adia-chat-ui > [data-chat-messages],
40
- adia-chat-ui > section {
41
- flex: 1;
42
- overflow-y: auto;
43
- padding: var(--chat-messages-py) var(--chat-messages-px);
44
- display: flex;
45
- flex-direction: column;
46
- gap: var(--chat-messages-gap);
47
- min-height: 0;
48
- }
49
-
50
- /* Footer */
51
- adia-chat-ui > footer {
52
- flex-shrink: 0;
53
- display: flex;
54
- align-items: center;
55
- padding: var(--chat-footer-py) var(--chat-footer-px);
56
- }
57
-
58
- adia-chat-ui > footer chat-input-ui {
59
- flex: 1;
60
- }
@@ -1,74 +0,0 @@
1
- /* ═══════════════════════════════════════════════════════════════
2
- adia-chat-ui — Markdown prose inside bubbles
3
- ═══════════════════════════════════════════════════════════════ */
4
-
5
- adia-chat-ui [data-bubble] [data-content] {
6
- white-space: normal;
7
- }
8
-
9
- adia-chat-ui [data-bubble] [data-content] p {
10
- margin: 0 0 0.5em;
11
- }
12
-
13
- adia-chat-ui [data-bubble] [data-content] p:last-child {
14
- margin-bottom: 0;
15
- }
16
-
17
- adia-chat-ui [data-bubble] [data-content] strong {
18
- font-weight: var(--chat-weight-semibold);
19
- }
20
-
21
- adia-chat-ui [data-bubble] [data-content] code {
22
- background: var(--chat-code-bg);
23
- padding: 0.1em 0.15em;
24
- border-radius: var(--chat-code-inline-radius);
25
- font-family: var(--chat-code-inline-family);
26
- font-size: 0.875em;
27
- }
28
-
29
- adia-chat-ui [data-bubble] [data-content] pre {
30
- background: var(--chat-code-bg);
31
- border-radius: var(--chat-code-radius);
32
- padding: var(--chat-code-block-px);
33
- overflow-x: auto;
34
- margin: var(--chat-code-block-my) 0;
35
- font-size: var(--chat-code-block-font);
36
- line-height: 1.5;
37
- }
38
-
39
- adia-chat-ui [data-bubble] [data-content] pre code {
40
- background: none;
41
- padding: 0;
42
- border-radius: 0;
43
- font-size: inherit;
44
- }
45
-
46
- adia-chat-ui [data-bubble] [data-content] ul,
47
- adia-chat-ui [data-bubble] [data-content] ol {
48
- margin: 0.25em 0;
49
- padding-inline-start: 1.25em;
50
- }
51
-
52
- adia-chat-ui [data-bubble] [data-content] li {
53
- margin-bottom: 0.15em;
54
- }
55
-
56
- adia-chat-ui [data-bubble] [data-content] a {
57
- color: inherit;
58
- text-decoration: underline;
59
- text-underline-offset: 2px;
60
- }
61
-
62
- adia-chat-ui [data-bubble] [data-content] h1,
63
- adia-chat-ui [data-bubble] [data-content] h2,
64
- adia-chat-ui [data-bubble] [data-content] h3,
65
- adia-chat-ui [data-bubble] [data-content] h4 {
66
- font-weight: var(--chat-weight-semibold);
67
- margin: 0.75em 0 0.25em;
68
- }
69
-
70
- adia-chat-ui [data-bubble] [data-content] h1:first-child,
71
- adia-chat-ui [data-bubble] [data-content] h2:first-child,
72
- adia-chat-ui [data-bubble] [data-content] h3:first-child {
73
- margin-top: 0;
74
- }
@@ -1,87 +0,0 @@
1
- /* ═══════════════════════════════════════════════════════════════
2
- adia-chat-ui — Message bubbles
3
- ═══════════════════════════════════════════════════════════════ */
4
-
5
- /* Message row */
6
- adia-chat-ui [data-role] {
7
- display: flex;
8
- gap: var(--chat-header-gap);
9
- align-items: flex-end;
10
- max-width: var(--chat-message-max-width);
11
- }
12
-
13
- adia-chat-ui [data-role="user"] {
14
- align-self: flex-end;
15
- flex-direction: row-reverse;
16
- }
17
-
18
- adia-chat-ui [data-role="assistant"] {
19
- align-self: flex-start;
20
- }
21
-
22
- /* Avatar */
23
- adia-chat-ui [data-role] [data-avatar] {
24
- width: var(--chat-avatar-size);
25
- height: var(--chat-avatar-size);
26
- border-radius: var(--chat-avatar-radius);
27
- background: var(--chat-avatar-bg);
28
- color: var(--chat-avatar-fg);
29
- display: flex;
30
- align-items: center;
31
- justify-content: center;
32
- font-size: var(--chat-avatar-font);
33
- font-weight: var(--chat-avatar-weight);
34
- flex-shrink: 0;
35
- }
36
-
37
- /* Bubble */
38
- adia-chat-ui [data-role] [data-bubble] {
39
- padding: var(--chat-assistant-py) var(--chat-assistant-px);
40
- border-radius: var(--chat-assistant-radius);
41
- font-size: var(--chat-font-size);
42
- line-height: var(--chat-line-height);
43
- white-space: pre-wrap;
44
- word-break: break-word;
45
- }
46
-
47
- adia-chat-ui [data-role] [data-bubble] > :first-child { margin-block-start: 0; }
48
- adia-chat-ui [data-role] [data-bubble] > :last-child { margin-block-end: 0; }
49
-
50
- /* User bubble */
51
- adia-chat-ui [data-role="user"] [data-bubble] {
52
- background: var(--chat-user-bg);
53
- color: var(--chat-user-fg);
54
- padding: var(--chat-user-py) var(--chat-user-px);
55
- border-radius: var(--chat-user-radius);
56
- border-bottom-right-radius: var(--chat-user-tail-radius);
57
- }
58
-
59
- /* Assistant bubble */
60
- adia-chat-ui [data-role="assistant"] [data-bubble] {
61
- background: var(--chat-assistant-bg);
62
- color: var(--chat-assistant-fg);
63
- border-bottom-left-radius: var(--chat-assistant-tail-radius);
64
- }
65
-
66
- /* Error message */
67
- adia-chat-ui [data-role="error"] {
68
- align-self: center;
69
- max-width: none;
70
- display: flex;
71
- align-items: center;
72
- gap: var(--chat-error-gap);
73
- color: var(--chat-error-fg);
74
- font-size: var(--chat-error-font);
75
- }
76
-
77
- /* Message actions (copy, regenerate) */
78
- adia-chat-ui [data-role] [data-actions] {
79
- display: flex;
80
- gap: var(--chat-actions-gap);
81
- opacity: 0;
82
- transition: opacity var(--chat-actions-duration) var(--chat-actions-easing);
83
- }
84
-
85
- adia-chat-ui [data-role]:hover [data-actions] {
86
- opacity: 1;
87
- }
@@ -1,30 +0,0 @@
1
- /* ═══════════════════════════════════════════════════════════════
2
- adia-chat-ui — Streaming states (cursor, thinking)
3
- ═══════════════════════════════════════════════════════════════ */
4
-
5
- /* Blinking cursor during streaming */
6
- adia-chat-ui [data-cursor] {
7
- display: inline-block;
8
- width: var(--chat-cursor-width);
9
- height: 1em;
10
- background: var(--chat-cursor-color);
11
- margin-inline-start: 1px;
12
- vertical-align: text-bottom;
13
- animation: adia-chat-blink var(--chat-cursor-speed) step-end infinite;
14
- }
15
-
16
- @keyframes adia-chat-blink {
17
- 50% { opacity: 0; }
18
- }
19
-
20
- @media (prefers-reduced-motion: reduce) {
21
- adia-chat-ui [data-cursor] {
22
- animation: none;
23
- opacity: 0.7;
24
- }
25
- }
26
-
27
- /* Thinking indicator */
28
- adia-chat-ui [data-role="assistant"][data-thinking] [data-bubble] {
29
- color: var(--chat-thinking-fg);
30
- }