@nonoun/native-chat 0.4.0 → 0.5.2

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.
@@ -132,13 +132,20 @@ var i = class extends e {
132
132
  }
133
133
  }
134
134
  }, a = class extends e {
135
- static observedAttributes = ["show-stop", "show-restart"];
135
+ static observedAttributes = [
136
+ "show-stop",
137
+ "show-restart",
138
+ "auto-focus-policy",
139
+ "open"
140
+ ];
136
141
  #e = r(!1);
137
142
  #t = r(!1);
138
- #n = null;
139
- #r = null;
143
+ #n = r("open-request");
144
+ #r = r(!1);
140
145
  #i = null;
141
146
  #a = null;
147
+ #o = null;
148
+ #s = null;
142
149
  attributeChangedCallback(e, t, n) {
143
150
  if (t !== n) {
144
151
  switch (e) {
@@ -148,6 +155,14 @@ var i = class extends e {
148
155
  case "show-restart":
149
156
  this.#t.value = n !== null;
150
157
  break;
158
+ case "auto-focus-policy":
159
+ this.#n.value = n ?? "open-request";
160
+ break;
161
+ case "open": {
162
+ let e = this.#r.value;
163
+ this.#r.value = n !== null, !e && n !== null ? this.#f(void 0, !1) : e && n === null && this.#p(void 0);
164
+ break;
165
+ }
151
166
  }
152
167
  super.attributeChangedCallback(e, t, n);
153
168
  }
@@ -164,8 +179,30 @@ var i = class extends e {
164
179
  set showRestart(e) {
165
180
  this.#t.value = e, this.toggleAttribute("show-restart", e);
166
181
  }
182
+ get autoFocusPolicy() {
183
+ return this.#n.value;
184
+ }
185
+ set autoFocusPolicy(e) {
186
+ this.#n.value = e, this.setAttribute("auto-focus-policy", e);
187
+ }
188
+ /** Open the panel. Optionally focus the composer. */
189
+ open(e) {
190
+ let t = this.#r.value;
191
+ this.#r.value = !0, this.toggleAttribute("open", !0);
192
+ let n = this.#n.value, r = e?.focusComposer ?? !1, i = n !== "never" && (n === "open-request" ? r : !1);
193
+ t || this.#f(e?.reason, i), i && this.focusComposer({ cursor: "end" }, "api");
194
+ }
195
+ /** Close the panel. */
196
+ close(e) {
197
+ let t = this.#r.value;
198
+ this.#r.value = !1, this.removeAttribute("open"), t && this.#p(e);
199
+ }
200
+ /** Focus the composer input. */
201
+ focusComposer(e, t = "api") {
202
+ this.#l(e ?? {}, t, 0);
203
+ }
167
204
  setup() {
168
- super.setup();
205
+ super.setup(), this.hasAttribute("open") && (this.#r.value = !0);
169
206
  let e = document.createElement("n-header");
170
207
  e.setAttribute("dividers", "");
171
208
  let t = document.createElement("n-icon");
@@ -173,11 +210,11 @@ var i = class extends e {
173
210
  let n = document.createElement("span");
174
211
  n.setAttribute("slot", "label"), n.textContent = "Assistant", e.appendChild(n);
175
212
  let r = document.createElement("span");
176
- r.setAttribute("slot", "trailing"), r.style.display = "inline-flex", r.style.alignItems = "center", r.style.gap = "calc(var(--n-space) * 2)", e.appendChild(r), this.#a = r;
213
+ r.setAttribute("slot", "trailing"), r.style.display = "inline-flex", r.style.alignItems = "center", r.style.gap = "calc(var(--n-space) * 2)", e.appendChild(r), this.#s = r;
177
214
  let i = document.createElement("n-body"), a = document.createElement("n-chat-content");
178
215
  i.appendChild(a);
179
216
  let o = document.createElement("n-footer");
180
- o.setAttribute("dividers", ""), this.#n = o;
217
+ o.setAttribute("dividers", ""), this.#i = o;
181
218
  let s = document.createElement("n-chat-input");
182
219
  s.setAttribute("variant", "plain");
183
220
  let c = document.createElement("n-textarea");
@@ -189,41 +226,117 @@ var i = class extends e {
189
226
  let f = document.createElement("n-button");
190
227
  f.setAttribute("variant", "primary"), f.setAttribute("intent", "accent"), f.setAttribute("radius", "round"), f.setAttribute("inline", ""), f.setAttribute("disabled", ""), f.classList.add("submit-btn"), f.innerHTML = "<n-icon name=\"arrow-up\"></n-icon>", l.appendChild(f), s.appendChild(l), o.appendChild(s), this.append(e, i, o), this.addEffect(() => {
191
228
  let e = this.#e.value;
192
- if (e && !this.#r) {
229
+ if (e && !this.#a) {
193
230
  let e = document.createElement("n-button");
194
- e.setAttribute("variant", "ghost"), e.setAttribute("inline", ""), e.setAttribute("aria-label", "Stop"), e.innerHTML = "<n-icon name=\"stop\"></n-icon>", e.addEventListener("native:press", this.#s), this.#r = e, this.#a?.prepend(e);
195
- } else !e && this.#r && (this.#r.removeEventListener("native:press", this.#s), this.#r.remove(), this.#r = null);
231
+ e.setAttribute("variant", "ghost"), e.setAttribute("inline", ""), e.setAttribute("aria-label", "Stop"), e.innerHTML = "<n-icon name=\"stop\"></n-icon>", e.addEventListener("native:press", this.#h), this.#a = e, this.#s?.prepend(e);
232
+ } else !e && this.#a && (this.#a.removeEventListener("native:press", this.#h), this.#a.remove(), this.#a = null);
196
233
  }), this.addEffect(() => {
197
234
  let e = this.#t.value;
198
- if (e && !this.#i) {
235
+ if (e && !this.#o) {
199
236
  let e = document.createElement("n-button");
200
- e.setAttribute("variant", "ghost"), e.setAttribute("inline", ""), e.setAttribute("aria-label", "Restart"), e.innerHTML = "<n-icon name=\"arrow-counter-clockwise\"></n-icon>", e.addEventListener("native:press", this.#c), this.#i = e;
201
- let t = this.#r?.nextSibling ?? this.#a?.firstChild ?? null;
202
- this.#a?.insertBefore(e, t);
203
- } else !e && this.#i && (this.#i.removeEventListener("native:press", this.#c), this.#i.remove(), this.#i = null);
237
+ e.setAttribute("variant", "ghost"), e.setAttribute("inline", ""), e.setAttribute("aria-label", "Restart"), e.innerHTML = "<n-icon name=\"arrow-counter-clockwise\"></n-icon>", e.addEventListener("native:press", this.#g), this.#o = e;
238
+ let t = this.#a?.nextSibling ?? this.#s?.firstChild ?? null;
239
+ this.#s?.insertBefore(e, t);
240
+ } else !e && this.#o && (this.#o.removeEventListener("native:press", this.#g), this.#o.remove(), this.#o = null);
204
241
  }), this.deferChildren(() => {
205
- this.#o();
242
+ this.#c();
243
+ }), this.#n.value === "ready" && queueMicrotask(() => {
244
+ this.isConnected && this.focusComposer({ cursor: "end" }, "policy");
206
245
  });
207
246
  }
208
247
  teardown() {
209
- this.#r && this.#r.removeEventListener("native:press", this.#s), this.#i && this.#i.removeEventListener("native:press", this.#c), this.#n = null, this.#r = null, this.#i = null, this.#a = null, this.innerHTML = "", super.teardown();
248
+ this.#a && this.#a.removeEventListener("native:press", this.#h), this.#o && this.#o.removeEventListener("native:press", this.#g), this.#i = null, this.#a = null, this.#o = null, this.#s = null, this.innerHTML = "", super.teardown();
210
249
  }
211
- #o() {
250
+ #c() {
212
251
  let e = this.querySelector(":scope > [slot=\"header-trailing\"]");
213
- e && this.#a && this.#a.appendChild(e);
252
+ e && this.#s && this.#s.appendChild(e);
214
253
  let t = this.querySelector(":scope > [slot=\"footer-leading\"]");
215
- if (t && this.#n) {
216
- let e = this.#n.querySelector(":scope > n-chat-input");
217
- this.#n.insertBefore(t, e);
254
+ if (t && this.#i) {
255
+ let e = this.#i.querySelector(":scope > n-chat-input");
256
+ this.#i.insertBefore(t, e);
257
+ }
258
+ }
259
+ #l(e, t, n) {
260
+ let r = this.#u();
261
+ if (!r) {
262
+ if (n < 2) {
263
+ queueMicrotask(() => this.#l(e, t, n + 1));
264
+ return;
265
+ }
266
+ this.#m("composer-unavailable", 3);
267
+ return;
218
268
  }
269
+ if (r.disabled) {
270
+ if (n < 2) {
271
+ queueMicrotask(() => this.#l(e, t, n + 1));
272
+ return;
273
+ }
274
+ this.#m("disabled", 3);
275
+ return;
276
+ }
277
+ let i = r.querySelector("n-textarea"), a = i ?? r;
278
+ try {
279
+ a.focus();
280
+ } catch {
281
+ if (n < 2) {
282
+ queueMicrotask(() => this.#l(e, t, n + 1));
283
+ return;
284
+ }
285
+ this.#m("blocked", 3);
286
+ return;
287
+ }
288
+ e.cursor && e.cursor !== "preserve" && i && this.#d(i, e.cursor), this.dispatchEvent(new CustomEvent("native:composer-focused", {
289
+ bubbles: !0,
290
+ composed: !0,
291
+ detail: { by: t }
292
+ }));
293
+ }
294
+ #u() {
295
+ return this.querySelector("n-chat-input");
296
+ }
297
+ #d(e, t) {
298
+ let n = e.ownerDocument.getSelection?.();
299
+ if (!n) return;
300
+ let r = e.firstChild ?? e, i = t === "end" ? r.textContent?.length ?? 0 : 0;
301
+ try {
302
+ let e = document.createRange();
303
+ e.setStart(r, i), e.collapse(!0), n.removeAllRanges(), n.addRange(e);
304
+ } catch {}
305
+ }
306
+ #f(e, t) {
307
+ this.dispatchEvent(new CustomEvent("native:chat-opened", {
308
+ bubbles: !0,
309
+ composed: !0,
310
+ detail: {
311
+ source: e,
312
+ focusComposer: t
313
+ }
314
+ }));
315
+ }
316
+ #p(e) {
317
+ this.dispatchEvent(new CustomEvent("native:chat-closed", {
318
+ bubbles: !0,
319
+ composed: !0,
320
+ detail: { reason: e }
321
+ }));
219
322
  }
220
- #s = () => {
323
+ #m(e, t) {
324
+ this.dispatchEvent(new CustomEvent("native:composer-focus-failed", {
325
+ bubbles: !0,
326
+ composed: !0,
327
+ detail: {
328
+ reason: e,
329
+ attempts: t
330
+ }
331
+ }));
332
+ }
333
+ #h = () => {
221
334
  this.dispatchEvent(new CustomEvent("native:chat-stop", {
222
335
  bubbles: !0,
223
336
  composed: !0
224
337
  }));
225
338
  };
226
- #c = () => {
339
+ #g = () => {
227
340
  this.dispatchEvent(new CustomEvent("native:chat-restart", {
228
341
  bubbles: !0,
229
342
  composed: !0
@@ -458,6 +571,7 @@ const u = {
458
571
  * @attr {string} status - `sending` | `sent` | `error` | `streaming` | `partial`
459
572
  * @attr {string} actions - Comma-separated action list, or `"none"` to suppress
460
573
  * @attr {string} actions-style - `"label"` (default) | `"icon"` | `"icon-label"`
574
+ * @attr {string} actions-position - `"inside"` (default) | `"below"` — toolbar placement
461
575
  * @fires native:message-action - Fired when an action button is clicked
462
576
  * @fires native:continue-request - Fired when continue is requested for a partial message
463
577
  */
@@ -468,14 +582,16 @@ var f = class extends e {
468
582
  "timestamp",
469
583
  "status",
470
584
  "actions",
471
- "actions-style"
585
+ "actions-style",
586
+ "actions-position"
472
587
  ];
473
588
  #e;
474
589
  #t = r("assistant");
475
590
  #n = r("sent");
476
591
  #r = r(null);
477
592
  #i = r("label");
478
- #a = null;
593
+ #a = r("inside");
594
+ #o = null;
479
595
  constructor() {
480
596
  super(), this.#e = this.attachInternals();
481
597
  }
@@ -509,6 +625,12 @@ var f = class extends e {
509
625
  set actionsStyle(e) {
510
626
  this.#i.value = e, this.setAttribute("actions-style", e);
511
627
  }
628
+ get actionsPosition() {
629
+ return this.#a.value;
630
+ }
631
+ set actionsPosition(e) {
632
+ this.#a.value = e, this.setAttribute("actions-position", e);
633
+ }
512
634
  attributeChangedCallback(e, t, n) {
513
635
  if (t !== n) {
514
636
  switch (e) {
@@ -524,34 +646,37 @@ var f = class extends e {
524
646
  case "actions-style":
525
647
  this.#i.value = n ?? "label";
526
648
  break;
649
+ case "actions-position":
650
+ this.#a.value = n ?? "inside";
651
+ break;
527
652
  }
528
653
  super.attributeChangedCallback(e, t, n);
529
654
  }
530
655
  }
531
656
  setup() {
532
657
  super.setup(), this.addEffect(() => {
533
- let e = this.#t.value, t = this.#r.value, n = this.#i.value, r = this.#n.value;
534
- this.#o(e, t, n, r);
535
- }), this.#e.role = "article", this.addEventListener("native:press", this.#s);
658
+ let e = this.#t.value, t = this.#r.value, n = this.#i.value, r = this.#a.value, i = this.#n.value;
659
+ this.#s(e, t, n, r, i);
660
+ }), this.#e.role = "article", this.addEventListener("native:press", this.#c);
536
661
  }
537
662
  teardown() {
538
- this.removeEventListener("native:press", this.#s), this.#a = null, super.teardown();
539
- }
540
- #o(e, t, n, r) {
541
- if (this.#a &&= (this.#a.remove(), null), t === "none" || this.querySelector("[slot=\"actions\"]")) return;
542
- let i;
543
- if (i = t ? t.split(",").map((e) => e.trim()).filter(Boolean) : d[e] ?? [], r === "partial" && !i.includes("continue") && (i = [...i, "continue"]), i.length === 0) return;
544
- let a = document.createElement("n-toolbar");
545
- a.className = "n-chat-message-actions", a.setAttribute("padding", "none"), a.setAttribute("aria-label", "Message actions"), n !== "label" && a.setAttribute("data-style", n);
546
- for (let e of i) {
663
+ this.removeEventListener("native:press", this.#c), this.#o = null, super.teardown();
664
+ }
665
+ #s(e, t, n, r, i) {
666
+ if (this.#o &&= (this.#o.remove(), null), t === "none" || this.querySelector("[slot=\"actions\"]")) return;
667
+ let a;
668
+ if (a = t ? t.split(",").map((e) => e.trim()).filter(Boolean) : d[e] ?? [], i === "partial" && !a.includes("continue") && (a = [...a, "continue"]), a.length === 0) return;
669
+ let o = document.createElement("n-toolbar");
670
+ o.className = "n-chat-message-actions", o.setAttribute("padding", "none"), o.setAttribute("aria-label", "Message actions"), n !== "label" && o.setAttribute("data-style", n);
671
+ for (let e of a) {
547
672
  let t = u[e];
548
- t && a.appendChild(p(e, t, n));
673
+ t && o.appendChild(p(e, t, n));
549
674
  }
550
- a.children.length > 0 && (this.appendChild(a), this.#a = a);
675
+ o.children.length !== 0 && (r === "below" ? this.after(o) : this.appendChild(o), this.#o = o);
551
676
  }
552
- #s = (e) => {
677
+ #c = (e) => {
553
678
  let t = e.target, n = t?.getAttribute("data-action");
554
- if (n && this.#a?.contains(t)) {
679
+ if (n && this.#o?.contains(t)) {
555
680
  if (e.stopPropagation(), n === "continue") {
556
681
  this.dispatchEvent(new CustomEvent("native:continue-request", {
557
682
  bubbles: !0,
@@ -596,9 +721,14 @@ function p(e, t, n) {
596
721
  *
597
722
  * @attr {string} role - `user` | `assistant` | `system`
598
723
  * @attr {string} sender - Display name of the sender
724
+ * @attr {string} avatar-align - `"top"` (default) | `"center"` | `"bottom"` — avatar vertical alignment
599
725
  */
600
726
  var m = class extends e {
601
- static observedAttributes = ["role", "sender"];
727
+ static observedAttributes = [
728
+ "role",
729
+ "sender",
730
+ "avatar-align"
731
+ ];
602
732
  #e;
603
733
  constructor() {
604
734
  super(), this.#e = this.attachInternals();
@@ -1,4 +1,12 @@
1
1
  import { NativeElement } from '@nonoun/native-ui';
2
+ export type AutoFocusPolicy = 'open-request' | 'ready' | 'never';
3
+ export interface ChatPanelOpenOptions {
4
+ focusComposer?: boolean;
5
+ reason?: string;
6
+ }
7
+ export interface FocusComposerOptions {
8
+ cursor?: 'start' | 'end' | 'preserve';
9
+ }
2
10
  /**
3
11
  * Stamped panel for the chat interface.
4
12
  *
@@ -28,9 +36,14 @@ import { NativeElement } from '@nonoun/native-ui';
28
36
  * ```
29
37
  * @attr {boolean} show-stop - Show a stop button in the header; fires `native:chat-stop`
30
38
  * @attr {boolean} show-restart - Show a restart button in the header; fires `native:chat-restart`
39
+ * @attr {string} auto-focus-policy - When to auto-focus the composer: 'open-request' (default), 'ready', 'never'
31
40
  * @fires native:chat-stop - Fired when the stop button is pressed
32
41
  * @fires native:chat-restart - Fired when the restart button is pressed
33
42
  * @fires native:send - Fired on submit with `{ value }` detail
43
+ * @fires native:chat-opened - Fired when the panel opens
44
+ * @fires native:chat-closed - Fired when the panel closes
45
+ * @fires native:composer-focused - Fired when the composer receives focus via API/policy
46
+ * @fires native:composer-focus-failed - Fired when focusComposer() fails after retries
34
47
  */
35
48
  export declare class NChatPanel extends NativeElement {
36
49
  #private;
@@ -40,6 +53,14 @@ export declare class NChatPanel extends NativeElement {
40
53
  set showStop(val: boolean);
41
54
  get showRestart(): boolean;
42
55
  set showRestart(val: boolean);
56
+ get autoFocusPolicy(): AutoFocusPolicy;
57
+ set autoFocusPolicy(val: AutoFocusPolicy);
58
+ /** Open the panel. Optionally focus the composer. */
59
+ open(options?: ChatPanelOpenOptions): void;
60
+ /** Close the panel. */
61
+ close(reason?: string): void;
62
+ /** Focus the composer input. */
63
+ focusComposer(options?: FocusComposerOptions, by?: 'api' | 'user' | 'policy'): void;
43
64
  setup(): void;
44
65
  teardown(): void;
45
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"chat-panel-element.d.ts","sourceRoot":"","sources":["../src/chat-panel-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,UAAW,SAAQ,aAAa;;IAC3C,MAAM,CAAC,kBAAkB,WAAiC;IAa1D,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAepF,IAAI,QAAQ,IAAI,OAAO,CAAiC;IACxD,IAAI,QAAQ,CAAC,GAAG,EAAE,OAAO,EAGxB;IAED,IAAI,WAAW,IAAI,OAAO,CAAoC;IAC9D,IAAI,WAAW,CAAC,GAAG,EAAE,OAAO,EAG3B;IAID,KAAK,IAAI,IAAI;IA4Hb,QAAQ,IAAI,IAAI;CA6CjB"}
1
+ {"version":3,"file":"chat-panel-element.d.ts","sourceRoot":"","sources":["../src/chat-panel-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,mBAAmB,CAAC;AAI1D,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,OAAO,GAAG,OAAO,CAAC;AAEjE,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,UAAU,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,qBAAa,UAAW,SAAQ,aAAa;;IAC3C,MAAM,CAAC,kBAAkB,WAA8D;IAevF,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IA4BpF,IAAI,QAAQ,IAAI,OAAO,CAAiC;IACxD,IAAI,QAAQ,CAAC,GAAG,EAAE,OAAO,EAGxB;IAED,IAAI,WAAW,IAAI,OAAO,CAAoC;IAC9D,IAAI,WAAW,CAAC,GAAG,EAAE,OAAO,EAG3B;IAED,IAAI,eAAe,IAAI,eAAe,CAAwC;IAC9E,IAAI,eAAe,CAAC,GAAG,EAAE,eAAe,EAGvC;IAED,qDAAqD;IACrD,IAAI,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,IAAI;IAkB1C,uBAAuB;IACvB,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAU5B,gCAAgC;IAChC,aAAa,CAAC,OAAO,CAAC,EAAE,oBAAoB,EAAE,EAAE,GAAE,KAAK,GAAG,MAAM,GAAG,QAAgB,GAAG,IAAI;IAM1F,KAAK,IAAI,IAAI;IA0Ib,QAAQ,IAAI,IAAI;CA2JjB"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import './register.ts';
2
2
  export { NChatInput } from './chat-input-element.ts';
3
3
  export { NChatPanel } from './chat-panel-element.ts';
4
+ export type { AutoFocusPolicy, ChatPanelOpenOptions, FocusComposerOptions } from './chat-panel-element.ts';
4
5
  export { NChatFeed } from './feed/index.ts';
5
6
  export { NChatAvatar } from './avatar/index.ts';
6
7
  export { NChatMessage, NChatMessages, NChatMessageText, NChatMessageActivity, NChatMessageSeed, NChatMessageGenUI, NChatInputStructured, } from './message/index.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAGvB,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAGnF,YAAY,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,YAAY,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AACnF,YAAY,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAGpG,YAAY,EACV,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,QAAQ,EACR,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAGvB,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAG3G,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAGnF,YAAY,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,YAAY,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AACnF,YAAY,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAGpG,YAAY,EACV,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,YAAY,EACZ,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,QAAQ,EACR,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC"}
@@ -17,6 +17,7 @@ export declare const ROLE_DEFAULTS: Record<string, string[]>;
17
17
  * @attr {string} status - `sending` | `sent` | `error` | `streaming` | `partial`
18
18
  * @attr {string} actions - Comma-separated action list, or `"none"` to suppress
19
19
  * @attr {string} actions-style - `"label"` (default) | `"icon"` | `"icon-label"`
20
+ * @attr {string} actions-position - `"inside"` (default) | `"below"` — toolbar placement
20
21
  * @fires native:message-action - Fired when an action button is clicked
21
22
  * @fires native:continue-request - Fired when continue is requested for a partial message
22
23
  */
@@ -34,6 +35,8 @@ export declare class NChatMessage extends NativeElement {
34
35
  set actions(val: string | null);
35
36
  get actionsStyle(): string;
36
37
  set actionsStyle(val: string);
38
+ get actionsPosition(): string;
39
+ set actionsPosition(val: string);
37
40
  attributeChangedCallback(name: string, old: string | null, val: string | null): void;
38
41
  setup(): void;
39
42
  teardown(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-message-element.d.ts","sourceRoot":"","sources":["../../src/message/chat-message-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,mBAAmB,CAAC;AAI1D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAOhE,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAGlD,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAa,SAAQ,aAAa;;IAC7C,MAAM,CAAC,kBAAkB,WAA6E;;IAgBtG,IAAI,IAAI,IAAI,MAAM,CAA6B;IAC/C,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAGnB;IAED,IAAI,SAAS,IAAI,MAAM,CAAkD;IAEzE,IAAI,SAAS,IAAI,MAAM,CAAwD;IAE/E,IAAI,MAAM,IAAI,MAAM,CAA+B;IACnD,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,EAGrB;IAED,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAAgC;IAC5D,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAO7B;IAED,IAAI,YAAY,IAAI,MAAM,CAAqC;IAC/D,IAAI,YAAY,CAAC,GAAG,EAAE,MAAM,EAG3B;IAID,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAapF,KAAK,IAAI,IAAI;IAkBb,QAAQ,IAAI,IAAI;CAoFjB"}
1
+ {"version":3,"file":"chat-message-element.d.ts","sourceRoot":"","sources":["../../src/message/chat-message-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,mBAAmB,CAAC;AAI1D,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAOhE,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAGlD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAa,SAAQ,aAAa;;IAC7C,MAAM,CAAC,kBAAkB,WAAiG;;IAiB1H,IAAI,IAAI,IAAI,MAAM,CAA6B;IAC/C,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAGnB;IAED,IAAI,SAAS,IAAI,MAAM,CAAkD;IAEzE,IAAI,SAAS,IAAI,MAAM,CAAwD;IAE/E,IAAI,MAAM,IAAI,MAAM,CAA+B;IACnD,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,EAGrB;IAED,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAAgC;IAC5D,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAO7B;IAED,IAAI,YAAY,IAAI,MAAM,CAAqC;IAC/D,IAAI,YAAY,CAAC,GAAG,EAAE,MAAM,EAG3B;IAED,IAAI,eAAe,IAAI,MAAM,CAAwC;IACrE,IAAI,eAAe,CAAC,GAAG,EAAE,MAAM,EAG9B;IAID,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAcpF,KAAK,IAAI,IAAI;IAmBb,QAAQ,IAAI,IAAI;CA0FjB"}
@@ -16,6 +16,7 @@ import { NativeElement } from '@nonoun/native-ui';
16
16
  *
17
17
  * @attr {string} role - `user` | `assistant` | `system`
18
18
  * @attr {string} sender - Display name of the sender
19
+ * @attr {string} avatar-align - `"top"` (default) | `"center"` | `"bottom"` — avatar vertical alignment
19
20
  */
20
21
  export declare class NChatMessages extends NativeElement {
21
22
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-messages-element.d.ts","sourceRoot":"","sources":["../../src/message/chat-messages-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,aAAc,SAAQ,aAAa;;IAC9C,MAAM,CAAC,kBAAkB,WAAsB;;IAS/C,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKpF,KAAK,IAAI,IAAI;IASb,QAAQ,IAAI,IAAI;CAGjB"}
1
+ {"version":3,"file":"chat-messages-element.d.ts","sourceRoot":"","sources":["../../src/message/chat-messages-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,aAAc,SAAQ,aAAa;;IAC9C,MAAM,CAAC,kBAAkB,WAAsC;;IAS/D,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKpF,KAAK,IAAI,IAAI;IASb,QAAQ,IAAI,IAAI;CAGjB"}
@@ -33,6 +33,8 @@
33
33
  --n-chat-bubble-assistant: var(--n-card);
34
34
  --n-chat-bubble-assistant-color: var(--n-ink);
35
35
  --n-chat-bubble-radius: var(--n-radius);
36
+ --n-chat-bubble-radius-avatar-side: var(--n-chat-bubble-radius);
37
+ --n-chat-bubble-radius-far-side: var(--n-chat-bubble-radius);
36
38
  --n-chat-bubble-padding-block: calc(var(--n-space) * 2);
37
39
  --n-chat-bubble-padding-inline: calc(var(--n-space) * 3);
38
40
  --n-chat-bubble-max-width: 85%;
@@ -67,6 +69,15 @@
67
69
  :where(native-chat-panel) {
68
70
  --n-ground: var(--n-panel);
69
71
 
72
+ /* Section surface token hooks — override on native-chat-panel to theme sections */
73
+ --n-chat-panel-header-background: transparent;
74
+ --n-chat-panel-header-border: var(--n-border-muted);
75
+ --n-chat-panel-header-label-font-weight: inherit;
76
+ --n-chat-panel-header-label-letter-spacing: inherit;
77
+ --n-chat-panel-body-background: transparent;
78
+ --n-chat-panel-footer-background: transparent;
79
+ --n-chat-panel-footer-border: var(--n-border-muted);
80
+
70
81
  container-type: inline-size;
71
82
  display: flex;
72
83
  flex: 1 1 0%;
@@ -83,11 +94,22 @@
83
94
  /* ── Sub-container integration ── */
84
95
 
85
96
  :where(native-chat-panel) > :where(n-header) {
86
- border-bottom: 1px solid var(--n-border-muted);
97
+ background: var(--n-chat-panel-header-background);
98
+ border-bottom: 1px solid var(--n-chat-panel-header-border);
99
+ }
100
+
101
+ :where(native-chat-panel) > :where(n-header) :where([slot="label"]) {
102
+ font-weight: var(--n-chat-panel-header-label-font-weight);
103
+ letter-spacing: var(--n-chat-panel-header-label-letter-spacing);
104
+ }
105
+
106
+ :where(native-chat-panel) > :where(n-body) {
107
+ background: var(--n-chat-panel-body-background);
87
108
  }
88
109
 
89
110
  :where(native-chat-panel) > :where(n-footer) {
90
- border-top: 1px solid var(--n-border-muted);
111
+ background: var(--n-chat-panel-footer-background);
112
+ border-top: 1px solid var(--n-chat-panel-footer-border);
91
113
  }
92
114
 
93
115
  /* WHY: Wide padding only above 22rem — asides (280–480px) keep compact defaults. */
@@ -193,6 +215,15 @@
193
215
  min-width: 0;
194
216
  }
195
217
 
218
+ /* Avatar alignment */
219
+ [avatar-align="center"]:where(n-chat-messages) {
220
+ align-items: center;
221
+ }
222
+
223
+ [avatar-align="bottom"]:where(n-chat-messages) {
224
+ align-items: flex-end;
225
+ }
226
+
196
227
  :where(n-chat-messages[hidden]) { display: none; }
197
228
 
198
229
  /* ══════════════════════════════════════════════════
@@ -242,7 +273,13 @@
242
273
 
243
274
  padding-block: var(--n-chat-bubble-padding-block);
244
275
  padding-inline: var(--n-chat-bubble-padding-inline);
245
- border-radius: var(--n-chat-bubble-radius);
276
+
277
+ /* Asymmetric radius: assistant = avatar on left */
278
+ border-radius:
279
+ var(--n-chat-bubble-radius-avatar-side)
280
+ var(--n-chat-bubble-radius-far-side)
281
+ var(--n-chat-bubble-radius-far-side)
282
+ var(--n-chat-bubble-radius-avatar-side);
246
283
 
247
284
  background: var(--n-chat-bubble-assistant);
248
285
  color: var(--n-chat-bubble-assistant-color);
@@ -251,8 +288,14 @@
251
288
  overflow-wrap: break-word;
252
289
  }
253
290
 
254
- /* User bubble */
291
+ /* User bubble: avatar on right — flip avatar-side / far-side corners */
255
292
  [role="user"]:where(n-chat-message) {
293
+ border-radius:
294
+ var(--n-chat-bubble-radius-far-side)
295
+ var(--n-chat-bubble-radius-avatar-side)
296
+ var(--n-chat-bubble-radius-avatar-side)
297
+ var(--n-chat-bubble-radius-far-side);
298
+
256
299
  background: var(--n-chat-bubble-user);
257
300
  color: var(--n-chat-bubble-user-color);
258
301
  }
@@ -290,6 +333,26 @@
290
333
  opacity: 1;
291
334
  }
292
335
 
336
+ /* ── Below-bubble actions position ── */
337
+
338
+ /* When actions-position="below", the toolbar is a sibling of n-chat-message
339
+ in the message column. Show on message hover via adjacent sibling. */
340
+ [actions-position="below"]:where(n-chat-message) + :where(.n-chat-message-actions) {
341
+ opacity: 0;
342
+ transition: opacity var(--n-duration) var(--n-easing);
343
+ }
344
+
345
+ [actions-position="below"]:where(n-chat-message):hover + :where(.n-chat-message-actions),
346
+ [actions-position="below"]:where(n-chat-message[force-hover]) + :where(.n-chat-message-actions),
347
+ [actions-position="below"]:where(n-chat-message) + :where(.n-chat-message-actions):focus-within {
348
+ opacity: 1;
349
+ }
350
+
351
+ /* Partial status — always show below-bubble actions */
352
+ [status="partial"][actions-position="below"]:where(n-chat-message) + :where(.n-chat-message-actions) {
353
+ opacity: 1;
354
+ }
355
+
293
356
  :where(n-chat-message[hidden]) { display: none; }
294
357
 
295
358
  /* ══════════════════════════════════════════════════
@@ -1,4 +1,4 @@
1
- import { a as e, c as t, d as n, f as r, g as i, h as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h } from "./chat-input-structured-element-C80qP8yg.js";
1
+ import { a as e, c as t, d as n, f as r, g as i, h as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h } from "./chat-input-structured-element-uXGIe-HI.js";
2
2
  /**
3
3
  * Parse a Server-Sent Events (SSE) response stream into ChatStreamChunk values.
4
4
  *
package/dist/register.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, d as t, g as n, h as r, i, l as a, m as o, n as s, p as c, r as l, t as u } from "./chat-input-structured-element-C80qP8yg.js";
1
+ import { a as e, d as t, g as n, h as r, i, l as a, m as o, n as s, p as c, r as l, t as u } from "./chat-input-structured-element-uXGIe-HI.js";
2
2
  import { NButton as d, NCard as f, NDialog as p, NIcon as m, NTextarea as h, NToolbar as g, define as _, registerIcon as v } from "@nonoun/native-ui";
3
3
  _("n-chat-input", n), _("native-chat-panel", r), _("n-chat-feed", o), _("n-chat-avatar", c), _("n-chat-message", t), _("n-chat-messages", a), _("n-chat-message-text", e), _("n-chat-message-activity", i), _("n-chat-message-seed", l), _("n-chat-message-genui", s), _("n-chat-input-structured", u), _("n-textarea", h), _("n-button", d), _("n-icon", m), _("n-toolbar", g), _("n-dialog", p), _("n-card", f), v("chat-dots", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M116,128a12,12,0,1,1,12,12A12,12,0,0,1,116,128ZM84,140a12,12,0,1,0-12-12A12,12,0,0,0,84,140Zm88,0a12,12,0,1,0-12-12A12,12,0,0,0,172,140Zm60-76V192a16,16,0,0,1-16,16H83l-32.6,28.16-.09.07A15.89,15.89,0,0,1,40,240a16.13,16.13,0,0,1-6.8-1.52A15.85,15.85,0,0,1,24,224V64A16,16,0,0,1,40,48H216A16,16,0,0,1,232,64ZM40,224h0ZM216,64H40V224l34.77-30A8,8,0,0,1,80,192H216Z\"/></svg>"), v("user", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M230.92,212c-15.23-26.33-38.7-45.21-66.09-54.16a72,72,0,1,0-73.66,0C63.78,166.78,40.31,185.66,25.08,212a8,8,0,1,0,13.85,8C55.71,194.74,89.05,176,128,176s72.29,18.74,89.07,44a8,8,0,0,0,13.85-8ZM72,96a56,56,0,1,1,56,56A56.06,56.06,0,0,1,72,96Z\"/></svg>"), v("stop", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40Zm0,160H56V56H200V200Z\"/></svg>"), v("arrow-counter-clockwise", "<svg viewBox=\"0 0 256 256\" fill=\"currentColor\"><path d=\"M224,128a96,96,0,0,1-94.71,96H128A95.38,95.38,0,0,1,62.1,197.8a8,8,0,0,1,11-11.63A80,80,0,1,0,71.43,71.39a3.07,3.07,0,0,1-.26.25L44.59,96H72a8,8,0,0,1,0,16H24a8,8,0,0,1-8-8V56a8,8,0,0,1,16,0V85.8L60.25,60A96,96,0,0,1,224,128Z\"/></svg>");
4
4
  export { c as NChatAvatar, o as NChatFeed, n as NChatInput, u as NChatInputStructured, t as NChatMessage, i as NChatMessageActivity, s as NChatMessageGenUI, l as NChatMessageSeed, e as NChatMessageText, a as NChatMessages, r as NChatPanel };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nonoun/native-chat",
3
- "version": "0.4.0",
3
+ "version": "0.5.2",
4
4
  "description": "Chat input and message layout components for @nonoun/native-ui",
5
5
  "license": "MIT",
6
6
  "type": "module",