@nonoun/native-chat 0.3.0 → 0.5.1

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.
@@ -1,4 +1,4 @@
1
- import { NativeElement as e, createDisabledEffect as t, signal as n } from "@nonoun/native-ui";
1
+ import { NativeElement as e, VirtualScrollController as t, createDisabledEffect as n, signal as r } from "@nonoun/native-ui";
2
2
  /**
3
3
  * Chat message input with textarea, submit button, and Enter-to-send behavior.
4
4
  * @attr {boolean} disabled - Disables interaction
@@ -10,11 +10,11 @@ import { NativeElement as e, createDisabledEffect as t, signal as n } from "@non
10
10
  * @fires native:composer-focus - Fired when the composer textarea gains focus
11
11
  * @fires native:composer-blur - Fired when the composer textarea loses focus
12
12
  */
13
- var r = class extends e {
13
+ var i = class extends e {
14
14
  static observedAttributes = ["disabled", "busy"];
15
15
  #e;
16
- #t = n(!1);
17
- #n = n(!1);
16
+ #t = r(!1);
17
+ #n = r(!1);
18
18
  #r = null;
19
19
  #i = null;
20
20
  constructor() {
@@ -60,7 +60,7 @@ var r = class extends e {
60
60
  }
61
61
  }
62
62
  setup() {
63
- super.setup(), this.addEffect(t(this, this.#t, this.#e)), this.deferChildren(() => {
63
+ super.setup(), this.addEffect(n(this, this.#t, this.#e)), this.deferChildren(() => {
64
64
  this.#a(), this.addEffect(() => {
65
65
  let e = this.#t.value;
66
66
  this.#r && this.#r.toggleAttribute("disabled", e), this.#i && (e ? this.#i.setAttribute("disabled", "") : this.#s());
@@ -131,14 +131,21 @@ var r = class extends e {
131
131
  })) && !this.hasAttribute("no-auto-clear") && (this.value = "", this.#i?.setAttribute("disabled", ""));
132
132
  }
133
133
  }
134
- }, i = class extends e {
135
- static observedAttributes = ["show-stop", "show-restart"];
136
- #e = n(!1);
137
- #t = n(!1);
138
- #n = null;
139
- #r = null;
134
+ }, a = class extends e {
135
+ static observedAttributes = [
136
+ "show-stop",
137
+ "show-restart",
138
+ "auto-focus-policy",
139
+ "open"
140
+ ];
141
+ #e = r(!1);
142
+ #t = r(!1);
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 r = 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 r = 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 r = 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,52 +226,138 @@ var r = 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;
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;
218
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
+ }));
219
293
  }
220
- #s = () => {
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
+ }));
322
+ }
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
230
343
  }));
231
344
  };
232
- }, a = 40, o = class extends e {
233
- static observedAttributes = ["auto-scroll", "scrollable"];
345
+ }, o = 40, s = class extends e {
346
+ static observedAttributes = [
347
+ "auto-scroll",
348
+ "scrollable",
349
+ "virtual",
350
+ "virtual-item-height",
351
+ "virtual-overscan"
352
+ ];
234
353
  #e;
235
- #t = n(!0);
236
- #n = n(!0);
354
+ #t = r(!0);
355
+ #n = r(!0);
237
356
  #r = null;
357
+ #i = null;
358
+ #a = null;
359
+ #o = r([]);
360
+ #s = null;
238
361
  constructor() {
239
362
  super(), this.#e = this.attachInternals();
240
363
  }
@@ -248,20 +371,87 @@ var r = class extends e {
248
371
  behavior: e ? "smooth" : "instant"
249
372
  }), this.#n.value = !0;
250
373
  }
374
+ /** Data items for virtual rendering. */
375
+ get items() {
376
+ return this.#o.value;
377
+ }
378
+ set items(e) {
379
+ this.#o.value = e, this.#i && this.#i.updateCount(e.length);
380
+ }
381
+ /** Callback that creates an HTMLElement from a data item. */
382
+ get itemRenderer() {
383
+ return this.#s;
384
+ }
385
+ set itemRenderer(e) {
386
+ this.#s = e;
387
+ }
251
388
  attributeChangedCallback(e, t, n) {
252
- t !== n && (e === "auto-scroll" && (this.#t.value = n !== null), super.attributeChangedCallback(e, t, n));
389
+ if (t !== n) {
390
+ switch (e) {
391
+ case "auto-scroll":
392
+ this.#t.value = n !== null;
393
+ break;
394
+ case "virtual":
395
+ n === null ? this.#l() : this.#c();
396
+ break;
397
+ case "virtual-item-height":
398
+ this.#i && n && (this.#i.itemHeight = Number(n) || 80);
399
+ break;
400
+ case "virtual-overscan":
401
+ this.#i && n && (this.#i.overscan = Number(n) || 5);
402
+ break;
403
+ }
404
+ super.attributeChangedCallback(e, t, n);
405
+ }
253
406
  }
254
407
  setup() {
255
- super.setup(), this.#e.role = "log", this.setAttribute("aria-live", "polite"), this.setAttribute("aria-label", "Conversation"), this.hasAttribute("scrollable") || this.setAttribute("scrollable", ""), this.hasAttribute("auto-scroll") || (this.#t.value = !0), this.addEventListener("scroll", this.#i, { passive: !0 }), this.#r = new MutationObserver(this.#a), this.#r.observe(this, {
408
+ super.setup(), this.#e.role = "log", this.setAttribute("aria-live", "polite"), this.setAttribute("aria-label", "Conversation"), this.hasAttribute("scrollable") || this.setAttribute("scrollable", ""), this.hasAttribute("auto-scroll") || (this.#t.value = !0), this.addEventListener("scroll", this.#f, { passive: !0 }), this.#r = new MutationObserver(this.#p), this.#r.observe(this, {
256
409
  childList: !0,
257
410
  subtree: !0
258
- }), this.addEventListener("native:message-action", this.#o);
411
+ }), this.addEventListener("native:message-action", this.#m), this.hasAttribute("virtual") && this.#c();
259
412
  }
260
413
  teardown() {
261
- this.removeEventListener("scroll", this.#i), this.removeEventListener("native:message-action", this.#o), this.#r?.disconnect(), this.#r = null, super.teardown();
414
+ this.removeEventListener("scroll", this.#f), this.removeEventListener("native:message-action", this.#m), this.#r?.disconnect(), this.#r = null, this.#l(), super.teardown();
415
+ }
416
+ #c() {
417
+ if (this.#i) return;
418
+ let e = Number(this.getAttribute("virtual-item-height")) || 80, n = Number(this.getAttribute("virtual-overscan")) || 5;
419
+ for (this.#i = new t(this, {
420
+ itemHeight: e,
421
+ overscan: n
422
+ }), this.#a = document.createElement("div"), this.#a.className = "n-chat-feed-virtual-container"; this.firstChild;) this.#a.appendChild(this.firstChild);
423
+ this.appendChild(this.#a), this.#i.enable(this, this.#a, this.#o.value.length), this.addEventListener("native:virtual-change", this.#u);
424
+ }
425
+ #l() {
426
+ if (this.#i && (this.removeEventListener("native:virtual-change", this.#u), this.#i.destroy(), this.#i = null, this.#a)) {
427
+ for (; this.#a.firstChild;) this.insertBefore(this.#a.firstChild, this.#a);
428
+ this.#a.remove(), this.#a = null;
429
+ }
430
+ }
431
+ #u = (e) => {
432
+ let { start: t, end: n, totalCount: r } = e.detail;
433
+ this.dispatchEvent(new CustomEvent("native:range-change", {
434
+ bubbles: !0,
435
+ composed: !0,
436
+ detail: {
437
+ start: t,
438
+ end: n,
439
+ total: r
440
+ }
441
+ })), this.#s && this.#a && this.#d(t, n);
442
+ };
443
+ #d(e, t) {
444
+ if (!this.#a || !this.#s) return;
445
+ let n = this.#o.value, r = Array.from(this.#a.children);
446
+ for (let e of r) !e.classList.contains("n-virtual-spacer-top") && !e.classList.contains("n-virtual-spacer-bottom") && e.remove();
447
+ let i = this.#a.querySelector(".n-virtual-spacer-bottom");
448
+ for (let r = e; r < t && r < n.length; r++) {
449
+ let e = this.#s(n[r], r);
450
+ i ? this.#a.insertBefore(e, i) : this.#a.appendChild(e);
451
+ }
262
452
  }
263
- #i = () => {
264
- let e = this.#n.value, t = this.scrollTop + this.clientHeight >= this.scrollHeight - a;
453
+ #f = () => {
454
+ let e = this.#n.value, t = this.scrollTop + this.clientHeight >= this.scrollHeight - o;
265
455
  this.#n.value = t, t !== e && this.dispatchEvent(new CustomEvent("native:feed-scroll", {
266
456
  bubbles: !0,
267
457
  composed: !0,
@@ -271,7 +461,7 @@ var r = class extends e {
271
461
  }
272
462
  }));
273
463
  };
274
- #a = () => {
464
+ #p = () => {
275
465
  this.#t.value && this.#n.value && requestAnimationFrame(() => {
276
466
  this.scrollTo({
277
467
  top: this.scrollHeight,
@@ -279,16 +469,16 @@ var r = class extends e {
279
469
  });
280
470
  });
281
471
  };
282
- #o = (e) => {};
283
- }, s = class extends e {
472
+ #m = (e) => {};
473
+ }, c = class extends e {
284
474
  static observedAttributes = [
285
475
  "src",
286
476
  "name",
287
477
  "icon"
288
478
  ];
289
- #e = n("");
290
- #t = n("");
291
- #n = n("");
479
+ #e = r("");
480
+ #t = r("");
481
+ #n = r("");
292
482
  attributeChangedCallback(e, t, n) {
293
483
  if (t !== n) {
294
484
  switch (e) {
@@ -320,7 +510,7 @@ var r = class extends e {
320
510
  }
321
511
  if (n) {
322
512
  let e = document.createElement("span");
323
- e.className = "n-chat-avatar-initials", e.textContent = c(n), e.setAttribute("aria-hidden", "true"), this.appendChild(e);
513
+ e.className = "n-chat-avatar-initials", e.textContent = l(n), e.setAttribute("aria-hidden", "true"), this.appendChild(e);
324
514
  return;
325
515
  }
326
516
  let r = document.createElement("n-icon");
@@ -331,10 +521,44 @@ var r = class extends e {
331
521
  this.textContent = "", super.teardown();
332
522
  }
333
523
  };
334
- function c(e) {
524
+ function l(e) {
335
525
  let t = e.trim().split(/\s+/);
336
526
  return t.length === 0 ? "" : t.length === 1 ? t[0][0].toUpperCase() : (t[0][0] + t[t.length - 1][0]).toUpperCase();
337
527
  }
528
+ const u = {
529
+ copy: {
530
+ label: "Copy",
531
+ icon: "copy"
532
+ },
533
+ retry: {
534
+ label: "Retry",
535
+ icon: "arrow-clockwise"
536
+ },
537
+ edit: {
538
+ label: "Edit",
539
+ icon: "pencil-simple"
540
+ },
541
+ "feedback-up": {
542
+ label: "Helpful",
543
+ icon: "thumbs-up"
544
+ },
545
+ "feedback-down": {
546
+ label: "Not helpful",
547
+ icon: "thumbs-down"
548
+ },
549
+ continue: {
550
+ label: "Continue",
551
+ icon: "arrow-right"
552
+ }
553
+ }, d = {
554
+ assistant: [
555
+ "copy",
556
+ "retry",
557
+ "feedback-up",
558
+ "feedback-down"
559
+ ],
560
+ user: ["edit", "retry"]
561
+ };
338
562
  /**
339
563
  * Individual chat message bubble.
340
564
  *
@@ -344,20 +568,30 @@ function c(e) {
344
568
  * @attr {string} role - `user` | `assistant` | `system`
345
569
  * @attr {string} message-id - Unique message identifier
346
570
  * @attr {string} timestamp - Epoch milliseconds
347
- * @attr {string} status - `sending` | `sent` | `error` | `streaming`
571
+ * @attr {string} status - `sending` | `sent` | `error` | `streaming` | `partial`
572
+ * @attr {string} actions - Comma-separated action list, or `"none"` to suppress
573
+ * @attr {string} actions-style - `"label"` (default) | `"icon"` | `"icon-label"`
574
+ * @attr {string} actions-position - `"inside"` (default) | `"below"` — toolbar placement
348
575
  * @fires native:message-action - Fired when an action button is clicked
576
+ * @fires native:continue-request - Fired when continue is requested for a partial message
349
577
  */
350
- var l = class extends e {
578
+ var f = class extends e {
351
579
  static observedAttributes = [
352
580
  "role",
353
581
  "message-id",
354
582
  "timestamp",
355
- "status"
583
+ "status",
584
+ "actions",
585
+ "actions-style",
586
+ "actions-position"
356
587
  ];
357
588
  #e;
358
- #t = n("assistant");
359
- #n = n("sent");
360
- #r = null;
589
+ #t = r("assistant");
590
+ #n = r("sent");
591
+ #r = r(null);
592
+ #i = r("label");
593
+ #a = r("inside");
594
+ #o = null;
361
595
  constructor() {
362
596
  super(), this.#e = this.attachInternals();
363
597
  }
@@ -379,6 +613,24 @@ var l = class extends e {
379
613
  set status(e) {
380
614
  this.#n.value = e, this.setAttribute("status", e);
381
615
  }
616
+ get actions() {
617
+ return this.#r.value;
618
+ }
619
+ set actions(e) {
620
+ this.#r.value = e, e === null ? this.removeAttribute("actions") : this.setAttribute("actions", e);
621
+ }
622
+ get actionsStyle() {
623
+ return this.#i.value;
624
+ }
625
+ set actionsStyle(e) {
626
+ this.#i.value = e, this.setAttribute("actions-style", e);
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
+ }
382
634
  attributeChangedCallback(e, t, n) {
383
635
  if (t !== n) {
384
636
  switch (e) {
@@ -388,39 +640,69 @@ var l = class extends e {
388
640
  case "status":
389
641
  this.#n.value = n ?? "sent";
390
642
  break;
643
+ case "actions":
644
+ this.#r.value = n;
645
+ break;
646
+ case "actions-style":
647
+ this.#i.value = n ?? "label";
648
+ break;
649
+ case "actions-position":
650
+ this.#a.value = n ?? "inside";
651
+ break;
391
652
  }
392
653
  super.attributeChangedCallback(e, t, n);
393
654
  }
394
655
  }
395
656
  setup() {
396
657
  super.setup(), this.addEffect(() => {
397
- let e = this.#t.value;
398
- this.#i(e);
399
- }), this.#e.role = "article", this.addEventListener("native:press", this.#a);
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);
400
661
  }
401
662
  teardown() {
402
- this.removeEventListener("native:press", this.#a), this.#r = null, super.teardown();
403
- }
404
- #i(e) {
405
- this.#r &&= (this.#r.remove(), null);
406
- let t = document.createElement("n-toolbar");
407
- t.className = "n-chat-message-actions", t.setAttribute("padding", "none"), t.setAttribute("aria-label", "Message actions"), e === "assistant" ? (t.appendChild(u("copy", "Copy")), t.appendChild(u("retry", "Retry")), t.appendChild(u("feedback-up", "Helpful")), t.appendChild(u("feedback-down", "Not helpful"))) : e === "user" && (t.appendChild(u("edit", "Edit")), t.appendChild(u("retry", "Resend"))), t.children.length > 0 && (this.appendChild(t), this.#r = t);
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) {
672
+ let t = u[e];
673
+ t && o.appendChild(p(e, t, n));
674
+ }
675
+ o.children.length !== 0 && (r === "below" ? this.after(o) : this.appendChild(o), this.#o = o);
408
676
  }
409
- #a = (e) => {
677
+ #c = (e) => {
410
678
  let t = e.target, n = t?.getAttribute("data-action");
411
- n && this.#r?.contains(t) && (e.stopPropagation(), this.dispatchEvent(new CustomEvent("native:message-action", {
412
- bubbles: !0,
413
- composed: !0,
414
- detail: {
415
- action: n,
416
- messageId: this.messageId
679
+ if (n && this.#o?.contains(t)) {
680
+ if (e.stopPropagation(), n === "continue") {
681
+ this.dispatchEvent(new CustomEvent("native:continue-request", {
682
+ bubbles: !0,
683
+ composed: !0,
684
+ detail: { messageId: this.messageId }
685
+ }));
686
+ return;
417
687
  }
418
- })));
688
+ this.dispatchEvent(new CustomEvent("native:message-action", {
689
+ bubbles: !0,
690
+ composed: !0,
691
+ detail: {
692
+ action: n,
693
+ messageId: this.messageId
694
+ }
695
+ }));
696
+ }
419
697
  };
420
698
  };
421
- function u(e, t) {
422
- let n = document.createElement("n-button");
423
- return n.setAttribute("variant", "ghost"), n.setAttribute("size", "sm"), n.setAttribute("inline", ""), n.setAttribute("data-action", e), n.setAttribute("aria-label", t), n.textContent = t, n;
699
+ function p(e, t, n) {
700
+ let r = document.createElement("n-button");
701
+ if (r.setAttribute("variant", "ghost"), r.setAttribute("size", "sm"), r.setAttribute("inline", ""), r.setAttribute("data-action", e), r.setAttribute("aria-label", t.label), n === "icon" || n === "icon-label") {
702
+ let e = document.createElement("n-icon");
703
+ e.setAttribute("name", t.icon), e.setAttribute("slot", "leading"), r.appendChild(e);
704
+ }
705
+ return (n === "label" || n === "icon-label") && r.appendChild(document.createTextNode(t.label)), r;
424
706
  }
425
707
  /**
426
708
  * Message group — cluster of messages from the same sender.
@@ -439,9 +721,14 @@ function u(e, t) {
439
721
  *
440
722
  * @attr {string} role - `user` | `assistant` | `system`
441
723
  * @attr {string} sender - Display name of the sender
724
+ * @attr {string} avatar-align - `"top"` (default) | `"center"` | `"bottom"` — avatar vertical alignment
442
725
  */
443
- var d = class extends e {
444
- static observedAttributes = ["role", "sender"];
726
+ var m = class extends e {
727
+ static observedAttributes = [
728
+ "role",
729
+ "sender",
730
+ "avatar-align"
731
+ ];
445
732
  #e;
446
733
  constructor() {
447
734
  super(), this.#e = this.attachInternals();
@@ -457,10 +744,10 @@ var d = class extends e {
457
744
  teardown() {
458
745
  super.teardown();
459
746
  }
460
- }, f = class extends e {
747
+ }, h = class extends e {
461
748
  static observedAttributes = ["format"];
462
- #e = n("markdown");
463
- #t = n("");
749
+ #e = r("markdown");
750
+ #t = r("");
464
751
  #n = null;
465
752
  get content() {
466
753
  return this.#t.value;
@@ -481,14 +768,14 @@ var d = class extends e {
481
768
  super.setup(), this.deferChildren(() => {
482
769
  !this.#t.value && this.textContent?.trim() && (this.#t.value = this.textContent.trim()), this.#n = document.createElement("div"), this.#n.className = "n-chat-prose", this.textContent = "", this.appendChild(this.#n), this.addEffect(() => {
483
770
  let e = this.#t.value, t = this.#e.value;
484
- this.#n && (t === "plain" ? this.#n.textContent = e : this.#n.innerHTML = g(e));
771
+ this.#n && (t === "plain" ? this.#n.textContent = e : this.#n.innerHTML = y(e));
485
772
  });
486
773
  });
487
774
  }
488
775
  teardown() {
489
776
  this.#n = null, super.teardown();
490
777
  }
491
- }, p = new Set([
778
+ }, g = new Set([
492
779
  "p",
493
780
  "br",
494
781
  "strong",
@@ -508,14 +795,14 @@ var d = class extends e {
508
795
  "blockquote",
509
796
  "hr"
510
797
  ]);
511
- function m(e) {
798
+ function _(e) {
512
799
  return e.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
513
800
  }
514
- function h(e) {
515
- let t = m(e);
801
+ function v(e) {
802
+ let t = _(e);
516
803
  return t = t.replace(/`([^`]+)`/g, "<code>$1</code>"), t = t.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), t = t.replace(/__(.+?)__/g, "<strong>$1</strong>"), t = t.replace(/\*(.+?)\*/g, "<em>$1</em>"), t = t.replace(/(?<!\w)_(.+?)_(?!\w)/g, "<em>$1</em>"), t = t.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, "<a href=\"$2\" rel=\"noopener noreferrer\" target=\"_blank\">$1</a>"), t;
517
804
  }
518
- function g(e) {
805
+ function y(e) {
519
806
  if (!e) return "";
520
807
  let t = e.split("\n"), n = [], r = 0;
521
808
  for (; r < t.length;) {
@@ -524,14 +811,14 @@ function g(e) {
524
811
  let i = e.slice(3).trim(), a = [];
525
812
  for (r += 1; r < t.length && !t[r].startsWith("```");) a.push(t[r]), r += 1;
526
813
  r += 1;
527
- let o = i ? ` data-lang="${m(i)}"` : "";
528
- n.push(`<pre${o}><code>${m(a.join("\n"))}</code></pre>`);
814
+ let o = i ? ` data-lang="${_(i)}"` : "";
815
+ n.push(`<pre${o}><code>${_(a.join("\n"))}</code></pre>`);
529
816
  continue;
530
817
  }
531
818
  let i = e.match(/^(#{1,6})\s+(.+)/);
532
819
  if (i) {
533
820
  let e = i[1].length;
534
- n.push(`<h${e}>${h(i[2])}</h${e}>`), r += 1;
821
+ n.push(`<h${e}>${v(i[2])}</h${e}>`), r += 1;
535
822
  continue;
536
823
  }
537
824
  if (/^---+$/.test(e.trim()) || /^\*\*\*+$/.test(e.trim())) {
@@ -541,19 +828,19 @@ function g(e) {
541
828
  if (e.startsWith("> ")) {
542
829
  let e = [];
543
830
  for (; r < t.length && t[r].startsWith("> ");) e.push(t[r].slice(2)), r += 1;
544
- n.push(`<blockquote>${g(e.join("\n"))}</blockquote>`);
831
+ n.push(`<blockquote>${y(e.join("\n"))}</blockquote>`);
545
832
  continue;
546
833
  }
547
834
  if (/^[-*+]\s/.test(e)) {
548
835
  let e = [];
549
836
  for (; r < t.length && /^[-*+]\s/.test(t[r]);) e.push(t[r].replace(/^[-*+]\s/, "")), r += 1;
550
- n.push("<ul>" + e.map((e) => `<li>${h(e)}</li>`).join("") + "</ul>");
837
+ n.push("<ul>" + e.map((e) => `<li>${v(e)}</li>`).join("") + "</ul>");
551
838
  continue;
552
839
  }
553
840
  if (/^\d+\.\s/.test(e)) {
554
841
  let e = [];
555
842
  for (; r < t.length && /^\d+\.\s/.test(t[r]);) e.push(t[r].replace(/^\d+\.\s/, "")), r += 1;
556
- n.push("<ol>" + e.map((e) => `<li>${h(e)}</li>`).join("") + "</ol>");
843
+ n.push("<ol>" + e.map((e) => `<li>${v(e)}</li>`).join("") + "</ol>");
557
844
  continue;
558
845
  }
559
846
  if (!e.trim()) {
@@ -562,15 +849,15 @@ function g(e) {
562
849
  }
563
850
  let a = [];
564
851
  for (; r < t.length && t[r].trim() && !t[r].startsWith("#") && !t[r].startsWith("```") && !t[r].startsWith("> ") && !/^[-*+]\s/.test(t[r]) && !/^\d+\.\s/.test(t[r]) && !/^---+$/.test(t[r].trim());) a.push(t[r]), r += 1;
565
- a.length > 0 && n.push(`<p>${h(a.join("\n"))}</p>`);
852
+ a.length > 0 && n.push(`<p>${v(a.join("\n"))}</p>`);
566
853
  }
567
854
  return n.join("");
568
855
  }
569
856
  /** Sanitize rendered HTML — strip any tags not in allowlist. */
570
- function _(e) {
857
+ function b(e) {
571
858
  return e.replace(/<\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g, (e, t) => {
572
859
  let n = t.toLowerCase();
573
- return p.has(n) ? e : "";
860
+ return g.has(n) ? e : "";
574
861
  });
575
862
  }
576
863
  /**
@@ -591,7 +878,7 @@ function _(e) {
591
878
  * @attr {boolean} expandable - Allow click to expand trace content
592
879
  * @fires native:activity-toggle - Fired when trace is expanded/collapsed
593
880
  */
594
- var v = class extends e {
881
+ var x = class extends e {
595
882
  static observedAttributes = [
596
883
  "type",
597
884
  "label",
@@ -599,11 +886,11 @@ var v = class extends e {
599
886
  "expandable"
600
887
  ];
601
888
  #e;
602
- #t = n("typing");
603
- #n = n("");
604
- #r = n(!1);
605
- #i = n(!1);
606
- #a = n(!1);
889
+ #t = r("typing");
890
+ #n = r("");
891
+ #r = r(!1);
892
+ #i = r(!1);
893
+ #a = r(!1);
607
894
  #o = 0;
608
895
  #s = 0;
609
896
  #c = null;
@@ -649,7 +936,7 @@ var v = class extends e {
649
936
  this.#r.value ? (this.#p(), this.#e.states.add("active")) : (this.#m(), this.#e.states.delete("active"));
650
937
  }), this.addEffect(() => {
651
938
  if (!this.#c) return;
652
- let e = this.#n.value || y(this.#t.value);
939
+ let e = this.#n.value || S(this.#t.value);
653
940
  this.#c.textContent = e;
654
941
  }), this.addEffect(() => {
655
942
  this.#u && (this.#u.hidden = !this.#r.value);
@@ -679,7 +966,7 @@ var v = class extends e {
679
966
  #h = () => {
680
967
  if (!this.#r.value || !this.#l) return;
681
968
  let e = performance.now() - this.#o;
682
- this.#l.textContent = b(e), this.#s = requestAnimationFrame(this.#h);
969
+ this.#l.textContent = C(e), this.#s = requestAnimationFrame(this.#h);
683
970
  };
684
971
  #g = () => {
685
972
  this.#i.value && (this.#a.value = !this.#a.value, this.toggleAttribute("expanded", this.#a.value), this.dispatchEvent(new CustomEvent("native:activity-toggle", {
@@ -689,14 +976,14 @@ var v = class extends e {
689
976
  })));
690
977
  };
691
978
  };
692
- function y(e) {
979
+ function S(e) {
693
980
  switch (e) {
694
981
  case "thinking": return "Thinking…";
695
982
  case "tool-use": return "Using tools…";
696
983
  default: return "Host is typing…";
697
984
  }
698
985
  }
699
- function b(e) {
986
+ function C(e) {
700
987
  let t = Math.floor(e / 1e3);
701
988
  if (t < 60) return `${t}s`;
702
989
  let n = Math.floor(t / 60), r = t % 60;
@@ -716,11 +1003,11 @@ function b(e) {
716
1003
  * @attr {boolean} disabled - Disables all chips
717
1004
  * @fires native:seed-select - Fired when a chip is clicked
718
1005
  */
719
- var x = class extends e {
1006
+ var w = class extends e {
720
1007
  static observedAttributes = ["options", "disabled"];
721
1008
  #e;
722
- #t = n([]);
723
- #n = n(!1);
1009
+ #t = r([]);
1010
+ #n = r(!1);
724
1011
  constructor() {
725
1012
  super(), this.#e = this.attachInternals();
726
1013
  }
@@ -754,7 +1041,7 @@ var x = class extends e {
754
1041
  }
755
1042
  }
756
1043
  setup() {
757
- super.setup(), this.addEffect(t(this, this.#n, this.#e)), this.addEffect(() => {
1044
+ super.setup(), this.addEffect(n(this, this.#n, this.#e)), this.addEffect(() => {
758
1045
  let e = this.#t.value;
759
1046
  this.textContent = "";
760
1047
  for (let t of e) {
@@ -785,7 +1072,7 @@ var x = class extends e {
785
1072
  }
786
1073
  }));
787
1074
  };
788
- }, S = new Set([
1075
+ }, T = new Set([
789
1076
  "script",
790
1077
  "style",
791
1078
  "link",
@@ -802,12 +1089,12 @@ var x = class extends e {
802
1089
  "frame",
803
1090
  "frameset",
804
1091
  "noscript"
805
- ]), C = class extends e {
1092
+ ]), E = class extends e {
806
1093
  static observedAttributes = ["schema-type", "mode"];
807
1094
  #e;
808
- #t = n("a2ui");
809
- #n = n("inline");
810
- #r = n(null);
1095
+ #t = r("a2ui");
1096
+ #n = r("inline");
1097
+ #r = r(null);
811
1098
  #i = null;
812
1099
  constructor() {
813
1100
  super(), this.#e = this.attachInternals();
@@ -847,7 +1134,7 @@ var x = class extends e {
847
1134
  super.setup(), this.#i = document.createElement("div"), this.#i.className = "n-chat-genui-container", this.appendChild(this.#i), this.addEffect(() => {
848
1135
  let e = this.#r.value, t = this.#n.value;
849
1136
  if (!this.#i || (this.#i.textContent = "", !e)) return;
850
- let n = w(e);
1137
+ let n = D(e);
851
1138
  if (n.length > 0) {
852
1139
  this.#a(n), this.dispatchEvent(new CustomEvent("native:genui-error", {
853
1140
  bubbles: !0,
@@ -858,7 +1145,7 @@ var x = class extends e {
858
1145
  }
859
1146
  if (t === "lightbox") this.#o(e);
860
1147
  else {
861
- let t = T(e);
1148
+ let t = O(e);
862
1149
  t && this.#i.appendChild(t);
863
1150
  }
864
1151
  this.#e.states.add("rendered");
@@ -882,7 +1169,7 @@ var x = class extends e {
882
1169
  r.setAttribute("variant", "outline"), r.setAttribute("size", "sm"), r.setAttribute("inline", ""), r.textContent = "Open", r.addEventListener("click", () => this.#s(e)), t.appendChild(r), this.#i.appendChild(t);
883
1170
  }
884
1171
  #s(e) {
885
- let t = document.createElement("n-dialog"), n = T(e);
1172
+ let t = document.createElement("n-dialog"), n = O(e);
886
1173
  n && t.appendChild(n), this.appendChild(t), requestAnimationFrame(() => {
887
1174
  let e = t.querySelector("dialog");
888
1175
  e && e.showModal();
@@ -902,20 +1189,20 @@ var x = class extends e {
902
1189
  }));
903
1190
  };
904
1191
  };
905
- function w(e, t = 0) {
1192
+ function D(e, t = 0) {
906
1193
  let n = [];
907
1194
  if (t > 20) return n.push("Maximum nesting depth (20) exceeded"), n;
908
1195
  if (!e.tag || typeof e.tag != "string") return n.push("Node missing required \"tag\" property"), n;
909
- if (S.has(e.tag.toLowerCase()) && n.push(`Forbidden tag: <${e.tag}>`), e.children) for (let r of e.children) n.push(...w(r, t + 1));
1196
+ if (T.has(e.tag.toLowerCase()) && n.push(`Forbidden tag: <${e.tag}>`), e.children) for (let r of e.children) n.push(...D(r, t + 1));
910
1197
  return n;
911
1198
  }
912
- function T(e) {
913
- if (S.has(e.tag.toLowerCase())) return null;
1199
+ function O(e) {
1200
+ if (T.has(e.tag.toLowerCase())) return null;
914
1201
  let t = document.createElement(e.tag);
915
1202
  if (e.id && (t.id = e.id), e.slot && (t.slot = e.slot), e.attributes) for (let [n, r] of Object.entries(e.attributes)) t.setAttribute(n, r);
916
1203
  if (e.properties) for (let [n, r] of Object.entries(e.properties)) t[n] = r;
917
1204
  if (e.text && (t.textContent = e.text), e.children) for (let n of e.children) {
918
- let e = T(n);
1205
+ let e = O(n);
919
1206
  e && t.appendChild(e);
920
1207
  }
921
1208
  return t;
@@ -940,7 +1227,7 @@ function T(e) {
940
1227
  * @fires native:structured-submit - Fired on submit with selections
941
1228
  * @fires native:structured-cancel - Fired when dismissed without selecting
942
1229
  */
943
- var E = class extends e {
1230
+ var k = class extends e {
944
1231
  static observedAttributes = [
945
1232
  "question",
946
1233
  "type",
@@ -949,12 +1236,12 @@ var E = class extends e {
949
1236
  "disabled"
950
1237
  ];
951
1238
  #e;
952
- #t = n("");
953
- #n = n("single");
954
- #r = n([]);
955
- #i = n(!1);
956
- #a = n(!1);
957
- #o = n(/* @__PURE__ */ new Set());
1239
+ #t = r("");
1240
+ #n = r("single");
1241
+ #r = r([]);
1242
+ #i = r(!1);
1243
+ #a = r(!1);
1244
+ #o = r(/* @__PURE__ */ new Set());
958
1245
  constructor() {
959
1246
  super(), this.#e = this.attachInternals();
960
1247
  }
@@ -1007,7 +1294,7 @@ var E = class extends e {
1007
1294
  }
1008
1295
  }
1009
1296
  setup() {
1010
- super.setup(), this.addEffect(t(this, this.#a, this.#e)), this.addEffect(() => {
1297
+ super.setup(), this.addEffect(n(this, this.#a, this.#e)), this.addEffect(() => {
1011
1298
  this.#s();
1012
1299
  }), this.addEventListener("native:press", this.#c);
1013
1300
  }
@@ -1066,4 +1353,4 @@ var E = class extends e {
1066
1353
  }));
1067
1354
  };
1068
1355
  };
1069
- export { f as a, _ as c, s as d, o as f, v as i, d as l, r as m, C as n, h as o, i as p, x as r, g as s, E as t, l as u };
1356
+ export { h as a, b as c, f as d, d as f, i as g, a as h, x as i, m as l, s as m, E as n, v as o, c as p, w as r, y as s, k as t, u };