@adia-ai/web-components 0.0.5 → 0.0.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.
@@ -335,6 +335,15 @@
335
335
  margin-inline-start: 0;
336
336
  }
337
337
 
338
+ /* Dual-cluster footer: leading action (e.g. Delete) on the inline-start
339
+ edge, trailing action cluster on the inline-end. margin-inline-end:
340
+ auto fills the gap between the two groups. */
341
+ > footer > [slot="action-leading"] {
342
+ margin-inline-end: auto;
343
+ display: flex;
344
+ gap: var(--card-footer-gap);
345
+ }
346
+
338
347
  /* ═══════ Images ═══════ */
339
348
 
340
349
  > img,
@@ -49,14 +49,20 @@
49
49
  border-color: var(--chat-input-border-focus);
50
50
  }
51
51
 
52
- /* Textarea: no border/bg of its own — container handles it */
53
- textarea-ui [slot="text"] {
52
+ /* Textarea: no border/bg of its own — container handles it. The
53
+ second selector keeps the transparent bg even when the host
54
+ textarea-ui carries [disabled] (streaming / submit lock) —
55
+ otherwise textarea.css's `:scope[disabled] [slot="text"]` rule
56
+ (specificity 0,3,0) paints --a-ui-bg-disabled over the container
57
+ bg. The `:scope` prefix boosts specificity to 0,3,1 so our rule
58
+ wins. */
59
+ textarea-ui [slot="text"],
60
+ :scope textarea-ui[disabled] [slot="text"] {
54
61
  border: none;
55
62
  background: transparent;
56
63
  border-radius: 0;
57
64
  box-shadow: none;
58
65
  max-height: 8rem;
59
- /*min-height: var(--a-size-md);*/
60
66
  padding: var(--chat-input-textarea-pt) var(--chat-input-textarea-px) 0;
61
67
  }
62
68
 
@@ -23,7 +23,10 @@ import { AdiaElement } from '../../core/element.js';
23
23
  * models — JSON array of model options: [{value, label}] or [{label, options: [...]}]
24
24
  * model — currently selected model value (reflected, two-way with select)
25
25
  * placeholder — textarea placeholder
26
- * disabled — disable entire input
26
+ * disabled — disable entire input (textarea becomes contenteditable=false)
27
+ * busy — in-flight / streaming state: send button disabled, submit
28
+ * events suppressed, but textarea stays editable so the user
29
+ * can draft a follow-up while the model is still responding.
27
30
  *
28
31
  * Events:
29
32
  * submit — user pressed Enter or clicked send (detail: { text, model })
@@ -37,6 +40,7 @@ import { AdiaElement } from '../../core/element.js';
37
40
  class AdiaChatInput extends AdiaElement {
38
41
  static properties = {
39
42
  disabled: { type: Boolean, default: false, reflect: true },
43
+ busy: { type: Boolean, default: false, reflect: true },
40
44
  placeholder: { type: String, default: 'Type a message...', reflect: true },
41
45
  model: { type: String, default: '', reflect: true },
42
46
  };
@@ -138,6 +142,9 @@ class AdiaChatInput extends AdiaElement {
138
142
  this.#textareaEl.disabled = this.disabled;
139
143
  this.#textareaEl.placeholder = this.placeholder;
140
144
  }
145
+ if (this.#sendEl) {
146
+ this.#sendEl.disabled = this.disabled || this.busy;
147
+ }
141
148
  // Sync model value to select (handles late upgrades)
142
149
  if (this.#modelEl && this.model && this.#modelEl.value !== this.model) {
143
150
  this.#modelEl.value = this.model;
@@ -154,7 +161,7 @@ class AdiaChatInput extends AdiaElement {
154
161
  };
155
162
 
156
163
  #onSubmit = () => {
157
- if (this.disabled) return;
164
+ if (this.disabled || this.busy) return;
158
165
  const text = this.value;
159
166
  if (!text && !this.#attachments.length) return;
160
167
  this.dispatchEvent(new CustomEvent('submit', {
@@ -97,7 +97,10 @@
97
97
  "description": "Direct child of <header> — grid row 2, spans the heading + action columns. Also accepts bare <p> / <small> tags."
98
98
  },
99
99
  "action": {
100
- "description": "Direct child of <header> — placed in the grid's last column alongside the stamped close button. Flex container for badge + button combinations."
100
+ "description": "Direct child of <header> — placed in the grid's last column alongside the stamped close button. Flex container for badge + button combinations. In <footer>, self-aligns to the trailing (inline-end) edge; pair with `action-leading` for dual-cluster footers."
101
+ },
102
+ "action-leading": {
103
+ "description": "Direct child of <footer> — leading (inline-start) action cluster. Used for dual-cluster footers where a destructive or secondary action sits on the opposite edge from the primary trailing cluster (e.g. Delete ↔ Cancel/Save, Back ↔ Cancel/Next). Replaces the legacy <span data-spacer> hack."
101
104
  },
102
105
  "backdrop": {
103
106
  "description": "Scrim overlay behind the drawer (stamped by the component)."
@@ -339,4 +339,13 @@
339
339
  [slot="panel"] > [slot="footer"] > [slot="action"] ~ [slot="action"] {
340
340
  margin-inline-start: 0;
341
341
  }
342
+
343
+ /* Dual-cluster footer: leading action (e.g. Delete) on the inline-start
344
+ edge, trailing action cluster on the inline-end. The margin-inline-end:
345
+ auto on the leading slot fills the gap between the two groups. */
346
+ [slot="panel"] > [slot="footer"] > [slot="action-leading"] {
347
+ margin-inline-end: auto;
348
+ display: flex;
349
+ gap: var(--drawer-footer-gap);
350
+ }
342
351
  }
@@ -97,6 +97,15 @@ slots:
97
97
  description: >-
98
98
  Direct child of <header> — placed in the grid's last column alongside the
99
99
  stamped close button. Flex container for badge + button combinations.
100
+ In <footer>, self-aligns to the trailing (inline-end) edge; pair with
101
+ `action-leading` for dual-cluster footers.
102
+ action-leading:
103
+ description: >-
104
+ Direct child of <footer> — leading (inline-start) action cluster. Used
105
+ for dual-cluster footers where a destructive or secondary action sits on
106
+ the opposite edge from the primary trailing cluster (e.g. Delete ↔
107
+ Cancel/Save, Back ↔ Cancel/Next). Replaces the legacy <span data-spacer>
108
+ hack.
100
109
  states:
101
110
  - name: idle
102
111
  description: Default, ready for interaction.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-components",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-utils.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -48,6 +48,9 @@
48
48
  "action": {
49
49
  "description": "Trailing control cluster inside <header> or <footer>. The first [slot=\"action\"] child pushes itself (and siblings) to the end of the bar; subsequent [slot=\"action\"] siblings flow with gap."
50
50
  },
51
+ "action-leading": {
52
+ "description": "Leading (inline-start) control cluster inside <header> or <footer>. Pairs with [slot=\"action\"] to produce a dual-cluster bar with space-between alignment (e.g. Back ↔ Cancel/Next, Discard ↔ Publish). Replaces the legacy <span data-spacer> hack."
53
+ },
51
54
  "heading": {
52
55
  "description": "Primary label inside <header> or <footer>. Rendered with --editor-title-weight + the strong foreground token."
53
56
  },
@@ -37,6 +37,12 @@ slots:
37
37
  Trailing control cluster inside <header> or <footer>. The first
38
38
  [slot="action"] child pushes itself (and siblings) to the end of
39
39
  the bar; subsequent [slot="action"] siblings flow with gap.
40
+ action-leading:
41
+ description: >-
42
+ Leading (inline-start) control cluster inside <header> or <footer>.
43
+ Pairs with [slot="action"] to produce a dual-cluster bar with
44
+ space-between alignment (e.g. Back ↔ Cancel/Next, Discard ↔
45
+ Publish). Replaces the legacy <span data-spacer> hack.
40
46
 
41
47
  states:
42
48
  - name: idle
@@ -72,6 +72,15 @@ adia-editor-ui > header > [slot="action"] ~ [slot="action"] {
72
72
  margin-inline-start: 0;
73
73
  }
74
74
 
75
+ /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
76
+ adia-editor-ui > header > [slot="action-leading"] {
77
+ display: flex;
78
+ align-items: center;
79
+ gap: var(--editor-bar-gap);
80
+ flex-shrink: 0;
81
+ margin-inline-end: auto;
82
+ }
83
+
75
84
  /* ── Body: pane | canvas | pane ── */
76
85
  adia-editor-ui > [data-editor-body] {
77
86
  display: flex;
@@ -151,3 +160,12 @@ adia-editor-ui > footer > [slot="action"] {
151
160
  adia-editor-ui > footer > [slot="action"] ~ [slot="action"] {
152
161
  margin-inline-start: 0;
153
162
  }
163
+
164
+ /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
165
+ adia-editor-ui > footer > [slot="action-leading"] {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: var(--editor-bar-gap);
169
+ flex-shrink: 0;
170
+ margin-inline-end: auto;
171
+ }
@@ -83,6 +83,9 @@
83
83
  "action": {
84
84
  "description": "Trailing control cluster inside any chrome bar. The first [slot=\"action\"] child pushes itself (and siblings) to the end; subsequent siblings flow with gap. Coexists with legacy <span data-spacer> / <div data-actions> hooks for one release — new code should prefer slots."
85
85
  },
86
+ "action-leading": {
87
+ "description": "Leading (inline-start) control cluster inside any chrome bar. Pairs with [slot=\"action\"] for dual-cluster chrome (e.g. back button + breadcrumb on the left, primary actions on the right). Replaces the legacy <span data-spacer> hack."
88
+ },
86
89
  "heading": {
87
90
  "description": "Primary label inside any chrome bar. Medium-weight + strong fg."
88
91
  },
@@ -56,6 +56,12 @@ slots:
56
56
  subsequent siblings flow with gap. Coexists with legacy
57
57
  <span data-spacer> / <div data-actions> hooks for one release —
58
58
  new code should prefer slots.
59
+ action-leading:
60
+ description: >-
61
+ Leading (inline-start) control cluster inside any chrome bar.
62
+ Pairs with [slot="action"] for dual-cluster chrome (e.g. back
63
+ button + breadcrumb on the left, primary actions on the right).
64
+ Replaces the legacy <span data-spacer> hack.
59
65
 
60
66
  states:
61
67
  - name: idle
@@ -61,6 +61,14 @@ app-shell-ui > main > header > [slot="action"] {
61
61
  app-shell-ui > main > header > [slot="action"] ~ [slot="action"] {
62
62
  margin-inline-start: 0;
63
63
  }
64
+ /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
65
+ app-shell-ui > main > header > [slot="action-leading"] {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: var(--page-actions-gap);
69
+ flex-shrink: 0;
70
+ margin-inline-end: auto;
71
+ }
64
72
 
65
73
  /* ── Main > section (scroll container) ──
66
74
  Wraps [data-content-root]. Scrolls vertically, hides scrollbar. */
@@ -125,3 +133,11 @@ app-shell-ui > main > footer > [slot="action"] {
125
133
  app-shell-ui > main > footer > [slot="action"] ~ [slot="action"] {
126
134
  margin-inline-start: 0;
127
135
  }
136
+ /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
137
+ app-shell-ui > main > footer > [slot="action-leading"] {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: var(--page-actions-gap);
141
+ flex-shrink: 0;
142
+ margin-inline-end: auto;
143
+ }
@@ -118,6 +118,15 @@
118
118
  [data-sidebar] > footer > [slot="action"] ~ [slot="action"] {
119
119
  margin-inline-start: 0;
120
120
  }
121
+ /* Dual-cluster: leading group on inline-start, trailing cluster on inline-end. */
122
+ [data-sidebar] > header > [slot="action-leading"],
123
+ [data-sidebar] > footer > [slot="action-leading"] {
124
+ display: flex;
125
+ align-items: center;
126
+ gap: var(--page-actions-gap);
127
+ flex-shrink: 0;
128
+ margin-inline-end: auto;
129
+ }
121
130
 
122
131
  /* ── Sidebar section (scrollable body) ── */
123
132
  [data-sidebar] > section {