@aparte/core 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/aparte-client.d.ts +8 -0
- package/dist/client/aparte-client.d.ts.map +1 -1
- package/dist/client/stream-adapter.d.ts.map +1 -1
- package/dist/components/bubble/aparte-chat-bubble.d.ts +64 -0
- package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
- package/dist/components/chat/aparte-chat.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-action.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-add-attachment.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-attachments.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-cancel.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-input.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-send.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-toolbar.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer.d.ts.map +1 -1
- package/dist/components/context/aparte-context.d.ts.map +1 -1
- package/dist/components/conversation-list/aparte-conversation-list.d.ts +38 -0
- package/dist/components/conversation-list/aparte-conversation-list.d.ts.map +1 -1
- package/dist/components/conversation-list/index.d.ts +1 -1
- package/dist/components/conversation-list/index.d.ts.map +1 -1
- package/dist/components/elicitation/aparte-elicitation.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/scroll-rail/aparte-scroll-rail.d.ts.map +1 -1
- package/dist/components/sidebar/aparte-sidebar.d.ts.map +1 -1
- package/dist/components/split/aparte-split.d.ts.map +1 -1
- package/dist/components/status/aparte-chat-status.d.ts.map +1 -1
- package/dist/components/suggestions/aparte-suggestions.d.ts.map +1 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
- package/dist/config/aparte-config.d.ts +40 -1
- package/dist/config/aparte-config.d.ts.map +1 -1
- package/dist/config/locale.d.ts +4 -0
- package/dist/config/locale.d.ts.map +1 -1
- package/dist/config/tool-table.d.ts +2 -0
- package/dist/config/tool-table.d.ts.map +1 -1
- package/dist/conversations/conversation-controller.d.ts +19 -0
- package/dist/conversations/conversation-controller.d.ts.map +1 -1
- package/dist/conversations/conversation-status.d.ts +27 -0
- package/dist/conversations/conversation-status.d.ts.map +1 -0
- package/dist/custom-elements.json +11477 -10170
- package/dist/{dialog-triggers-3G9TqG9g.js → defer-hydration-BwMqeRHq.js} +816 -211
- package/dist/defer-hydration-BwMqeRHq.js.map +1 -0
- package/dist/{glyphs-C8z2oJUg.js → glyphs-2bym5jl5.js} +1 -1
- package/dist/{glyphs-C8z2oJUg.js.map → glyphs-2bym5jl5.js.map} +1 -1
- package/dist/host/aparte-chat-host.d.ts +22 -0
- package/dist/host/aparte-chat-host.d.ts.map +1 -1
- package/dist/icons.js +2 -2
- package/dist/index.css +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +338 -58
- package/dist/index.js.map +1 -1
- package/dist/index.node.d.ts +4 -3
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +35 -29
- package/dist/index.node.js.map +1 -1
- package/dist/interop/defer-hydration.d.ts +34 -0
- package/dist/interop/defer-hydration.d.ts.map +1 -0
- package/dist/interop/element-props.d.ts +1 -1
- package/dist/interop/element-props.d.ts.map +1 -1
- package/dist/primitives/icon/aparte-icon.d.ts.map +1 -1
- package/dist/primitives/progress-spinner/aparte-progress-spinner.d.ts.map +1 -1
- package/dist/primitives/select/aparte-optgroup.d.ts.map +1 -1
- package/dist/primitives/select/aparte-option.d.ts.map +1 -1
- package/dist/primitives/select/aparte-select.d.ts.map +1 -1
- package/dist/renderers/index.d.ts +2 -1
- package/dist/renderers/index.d.ts.map +1 -1
- package/dist/renderers/mounted-renderer.d.ts +34 -0
- package/dist/renderers/mounted-renderer.d.ts.map +1 -0
- package/dist/renderers/segment-renderers.d.ts +14 -1
- package/dist/renderers/segment-renderers.d.ts.map +1 -1
- package/dist/renderers/segments/thinking.d.ts.map +1 -1
- package/dist/renderers/segments/tool-call.d.ts +2 -1
- package/dist/renderers/segments/tool-call.d.ts.map +1 -1
- package/dist/types/event-map.d.ts +4 -2
- package/dist/types/event-map.d.ts.map +1 -1
- package/dist/types/events.d.ts +15 -0
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/segments.d.ts +21 -3
- package/dist/types/segments.d.ts.map +1 -1
- package/dist/types/tools.d.ts +95 -0
- package/dist/types/tools.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/dialog-triggers-3G9TqG9g.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A, a, b, c, d, f, g, h, i, j, k, l, m, n, p, q, s,
|
|
1
|
+
import { K as deferHydrationOf, a6 as resolveConfig, ag as eventOrigin, ah as activeElementIn, ab as subscribeConfigChange, a8 as runWithConfig, o as AparteElicitationAbortError, w as buildApprovalPanel, x as buildElicitationPanel, ai as gateGlide, O as escapeAttr, aj as toolCallRenderer, ak as toolRendererThatDrew, t as aparteGlobalConfig, al as isMountedRenderer, am as renderingVersionOf, H as cssEscape, an as onRenderersChange, ao as mergeSegmentUpdate, P as escapeHtml, Y as isSegmentSettled, ap as writeStreamedMarkdown, X as isSafeUrl, C as copyText, S as getSegmentRenderer, T as installDefaultRenderersOnce, r as AparteMessageRepository, aq as stampSegmentActivity, ar as stampSegmentOnInsert, as as stampSegmentOnUpdate, at as renumberSegments, au as openSegmentIds, av as isTerminalStatus, aw as adoptMessageSegments, V as isAwaitingReply, a0 as populateBubbleFromMessage, af as uuid, a7 as revokeAttachmentUrls, $ as parseMarkdownToSegments, ax as APARTE_DERIVED_SEGMENTS, ay as rememberConversationStatus, az as tellConversationStatus, aA as conversationStatusOf, e as APARTE_DEFAULT_LOCALE, aB as chatBoundaryOf, aC as rootOf, U as installDialogTriggersOnce } from "./defer-hydration-BwMqeRHq.js";
|
|
2
|
+
import { A, a, b, c, d, f, g, h, i, j, k, l, m, n, p, q, s, u, v, y, z, B, D, E, F, G, I, J, L, M, N, Q, R, W, Z, _, a1, a2, a3, a4, a5, a9, aa, ac, ad, ae } from "./defer-hydration-BwMqeRHq.js";
|
|
3
3
|
function presenceOn(value) {
|
|
4
4
|
return value === "" ? true : Boolean(value);
|
|
5
5
|
}
|
|
@@ -64,6 +64,7 @@ class AparteOption extends HTMLElement {
|
|
|
64
64
|
dot.setAttribute("data-status", status);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
+
deferHydrationOf(AparteOption);
|
|
67
68
|
if (!customElements.get("aparte-option")) {
|
|
68
69
|
customElements.define("aparte-option", AparteOption);
|
|
69
70
|
}
|
|
@@ -173,6 +174,7 @@ class AparteOptgroup extends HTMLElement {
|
|
|
173
174
|
});
|
|
174
175
|
}
|
|
175
176
|
}
|
|
177
|
+
deferHydrationOf(AparteOptgroup);
|
|
176
178
|
if (!customElements.get("aparte-optgroup")) {
|
|
177
179
|
customElements.define("aparte-optgroup", AparteOptgroup);
|
|
178
180
|
}
|
|
@@ -726,6 +728,7 @@ function optionLabelText(option) {
|
|
|
726
728
|
}
|
|
727
729
|
return text.trim();
|
|
728
730
|
}
|
|
731
|
+
deferHydrationOf(AparteSelect);
|
|
729
732
|
if (!customElements.get("aparte-select")) {
|
|
730
733
|
customElements.define("aparte-select", AparteSelect);
|
|
731
734
|
}
|
|
@@ -760,6 +763,7 @@ class AparteProgressSpinner extends HTMLElement {
|
|
|
760
763
|
this.innerHTML = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle class="aparte-spinner-track" cx="12" cy="12" r="${this._r}"/><circle class="aparte-spinner-fill" cx="12" cy="12" r="${this._r}" stroke-dasharray="${dasharray}" stroke-dashoffset="${dashoffset.toFixed(2)}"/></svg>`;
|
|
761
764
|
}
|
|
762
765
|
}
|
|
766
|
+
deferHydrationOf(AparteProgressSpinner);
|
|
763
767
|
if (!customElements.get("aparte-progress-spinner")) {
|
|
764
768
|
customElements.define("aparte-progress-spinner", AparteProgressSpinner);
|
|
765
769
|
}
|
|
@@ -786,6 +790,7 @@ class AparteIcon extends HTMLElement {
|
|
|
786
790
|
this.firstElementChild?.setAttribute("aria-hidden", "true");
|
|
787
791
|
}
|
|
788
792
|
}
|
|
793
|
+
deferHydrationOf(AparteIcon);
|
|
789
794
|
if (!customElements.get("aparte-icon")) {
|
|
790
795
|
customElements.define("aparte-icon", AparteIcon);
|
|
791
796
|
}
|
|
@@ -1100,6 +1105,7 @@ class AparteElicitation extends HTMLElement {
|
|
|
1100
1105
|
}
|
|
1101
1106
|
}
|
|
1102
1107
|
if (typeof customElements !== "undefined" && !customElements.get("aparte-elicitation")) {
|
|
1108
|
+
deferHydrationOf(AparteElicitation);
|
|
1103
1109
|
customElements.define("aparte-elicitation", AparteElicitation);
|
|
1104
1110
|
}
|
|
1105
1111
|
function isTranscriptEmpty(viewport) {
|
|
@@ -1297,6 +1303,7 @@ class AparteChat extends HTMLElement {
|
|
|
1297
1303
|
this.toggleAttribute("data-empty", !viewport || isTranscriptEmpty(viewport));
|
|
1298
1304
|
}
|
|
1299
1305
|
}
|
|
1306
|
+
deferHydrationOf(AparteChat);
|
|
1300
1307
|
if (!customElements.get("aparte-chat")) {
|
|
1301
1308
|
customElements.define("aparte-chat", AparteChat);
|
|
1302
1309
|
}
|
|
@@ -1400,6 +1407,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1400
1407
|
this._updateLocalizedLabels();
|
|
1401
1408
|
this._updateWaiting();
|
|
1402
1409
|
this._renderAvatar();
|
|
1410
|
+
this._redrawSegmentsWhoseRendererChanged();
|
|
1403
1411
|
this._relabelSegments();
|
|
1404
1412
|
this._updateTimestamp(this.getAttribute("timestamp"));
|
|
1405
1413
|
if (!this._redrawQueued) {
|
|
@@ -1414,16 +1422,151 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1414
1422
|
_redrawQueued = false;
|
|
1415
1423
|
/** The {@link renderingVersionOf} count the content was last drawn with. */
|
|
1416
1424
|
_drawnWithVersion = 0;
|
|
1425
|
+
/** Every segment on screen, by id: see {@link DrawnSegment}. */
|
|
1426
|
+
_drawn = /* @__PURE__ */ new Map();
|
|
1427
|
+
/** The segments were released because the bubble was still out of the page at the next microtask. */
|
|
1428
|
+
_releasedWhileDetached = false;
|
|
1429
|
+
/** A {@link _scheduleReleaseCheck} is queued. */
|
|
1430
|
+
_releaseCheckQueued = false;
|
|
1431
|
+
/** Unsubscribes from the renderer registries' channel; set while connected. */
|
|
1432
|
+
_offRenderers = null;
|
|
1433
|
+
/** The tool renderer in effect for a call: none for a hidden call or another segment. */
|
|
1434
|
+
_toolRendererFor(segment) {
|
|
1435
|
+
if (segment.type !== "tool_call") return null;
|
|
1436
|
+
const cfg = this._cfg;
|
|
1437
|
+
if (resolveSegmentRenderer("tool_call", cfg) !== toolCallRenderer) return null;
|
|
1438
|
+
const name = segment.toolCall?.name;
|
|
1439
|
+
if (!name || cfg.isToolHidden(name)) return null;
|
|
1440
|
+
return cfg.getToolRenderer(name) ?? null;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Record a segment as drawn, set it up, then read back which tool renderer is behind the
|
|
1444
|
+
* row (tool-call.ts records it as the row is set up — one record, not a second lookup).
|
|
1445
|
+
* Recorded BEFORE `setup`: a setup that throws (a framework mount) leaves an element that
|
|
1446
|
+
* will still be cleaned up. The same id drawn twice releases the first element.
|
|
1447
|
+
*/
|
|
1448
|
+
_install(segment, el2, renderer) {
|
|
1449
|
+
const previous = this._drawn.get(segment.id);
|
|
1450
|
+
if (previous && previous.el !== el2) this._release(segment.id);
|
|
1451
|
+
const drawn = { el: el2, renderer, tool: null, segment };
|
|
1452
|
+
this._drawn.set(segment.id, drawn);
|
|
1453
|
+
if (!this.isConnected) this._scheduleReleaseCheck();
|
|
1454
|
+
if (renderer?.setup) runWithConfig(this._cfg, () => renderer.setup(el2, segment));
|
|
1455
|
+
if (segment.type === "tool_call") drawn.tool = toolRendererThatDrew(el2);
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* The segment's element leaves for good: its renderer's `cleanup`, once. A cleanup that
|
|
1459
|
+
* throws is reported and costs nothing else — the element still goes, and the others
|
|
1460
|
+
* are still released.
|
|
1461
|
+
*/
|
|
1462
|
+
_release(id) {
|
|
1463
|
+
const drawn = this._drawn.get(id);
|
|
1464
|
+
if (!drawn) return;
|
|
1465
|
+
this._drawn.delete(id);
|
|
1466
|
+
const cleanup = drawn.renderer?.cleanup;
|
|
1467
|
+
if (!cleanup) return;
|
|
1468
|
+
try {
|
|
1469
|
+
runWithConfig(this._cfg, () => cleanup.call(drawn.renderer, drawn.el, drawn.segment));
|
|
1470
|
+
} catch (err) {
|
|
1471
|
+
console.error("[aparte] a segment renderer's cleanup threw", err);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
_releaseAll() {
|
|
1475
|
+
for (const id of [...this._drawn.keys()]) this._release(id);
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Gone for good, or only moved? A keyed list moving a row takes it out and puts it back
|
|
1479
|
+
* before the next microtask, and must not unmount what its segments hold: decide then.
|
|
1480
|
+
*/
|
|
1481
|
+
_scheduleReleaseCheck() {
|
|
1482
|
+
if (this._releaseCheckQueued) return;
|
|
1483
|
+
this._releaseCheckQueued = true;
|
|
1484
|
+
queueMicrotask(() => {
|
|
1485
|
+
this._releaseCheckQueued = false;
|
|
1486
|
+
if (this.isConnected || this._drawn.size === 0) return;
|
|
1487
|
+
this._releaseAll();
|
|
1488
|
+
this._releasedWhileDetached = true;
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
/** One segment drawn again in place: its renderer cleans up, the current one draws. False when it was never drawn. */
|
|
1492
|
+
_redrawSegment(segment) {
|
|
1493
|
+
const drawn = this._drawn.get(segment.id);
|
|
1494
|
+
if (!drawn) return false;
|
|
1495
|
+
const cfg = this._cfg;
|
|
1496
|
+
const renderer = resolveSegmentRenderer(segment.type, cfg);
|
|
1497
|
+
const old = drawn.el;
|
|
1498
|
+
this._release(segment.id);
|
|
1499
|
+
const el2 = renderer ? segmentRenderResultToElement(runWithConfig(cfg, () => renderer.render(segment)), segment) : unrenderedSegment(segment);
|
|
1500
|
+
if (!el2) {
|
|
1501
|
+
old.remove();
|
|
1502
|
+
return true;
|
|
1503
|
+
}
|
|
1504
|
+
old.replaceWith(el2);
|
|
1505
|
+
this._install(segment, el2, renderer);
|
|
1506
|
+
return true;
|
|
1507
|
+
}
|
|
1508
|
+
/** The renderer that drew this segment is still the one in effect (for a tool call, the tool renderer too). */
|
|
1509
|
+
_stillDrawnByCurrent(segment, drawn) {
|
|
1510
|
+
return resolveSegmentRenderer(segment.type, this._cfg) === drawn.renderer && this._toolRendererFor(segment) === drawn.tool;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* A renderer registered, unregistered or overridden since these segments were drawn: draw
|
|
1514
|
+
* again only the segments whose renderer is no longer the one that drew them (for a tool
|
|
1515
|
+
* call, the tool renderer counts), in place. The others keep their elements — and with
|
|
1516
|
+
* them anything mounted in them, which a redraw of the whole message would unmount.
|
|
1517
|
+
*/
|
|
1518
|
+
_redrawSegmentsWhoseRendererChanged() {
|
|
1519
|
+
if (!this.isConnected || !this._segmentsEl || this._drawn.size === 0) return;
|
|
1520
|
+
for (const segment of this._segments) {
|
|
1521
|
+
const drawn = this._drawn.get(segment.id);
|
|
1522
|
+
if (!drawn || this._stillDrawnByCurrent(segment, drawn)) continue;
|
|
1523
|
+
this._redrawSegment(segment);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* The registries' channel: a renderer changed on our config — or on the global one, whose
|
|
1528
|
+
* segment renderers a chat with its own config inherits.
|
|
1529
|
+
*/
|
|
1530
|
+
_onRenderersChange = (cfg) => {
|
|
1531
|
+
if (cfg !== this._cfg && cfg !== aparteGlobalConfig) return;
|
|
1532
|
+
this._redrawSegmentsWhoseRendererChanged();
|
|
1533
|
+
};
|
|
1534
|
+
/**
|
|
1535
|
+
* A card a mounted renderer keeps (a framework component): its drawing reads none of the
|
|
1536
|
+
* providers a rendering-version bump announces, so it stays mounted through one — as long
|
|
1537
|
+
* as that renderer is still the one in effect.
|
|
1538
|
+
*/
|
|
1539
|
+
_keepsItsDrawing(segment, drawn) {
|
|
1540
|
+
const mounted = isMountedRenderer(drawn.renderer) || segment.type === "tool_call" && isMountedRenderer(drawn.tool);
|
|
1541
|
+
return mounted && this._stillDrawnByCurrent(segment, drawn);
|
|
1542
|
+
}
|
|
1417
1543
|
/**
|
|
1418
1544
|
* A Markdown or highlight provider set after this message was drawn: draw it again.
|
|
1419
1545
|
* Not mid-stream — the stream reads the providers as it goes, and its end redraws — and
|
|
1420
1546
|
* not mid-edit, where the drawn message is hidden behind the editor and drawing it would
|
|
1421
1547
|
* show it again beside the editor; leaving the edit calls this.
|
|
1548
|
+
*
|
|
1549
|
+
* Segment by segment, keeping the cards a mounted renderer holds: a highlighter that loads
|
|
1550
|
+
* after the first answer must not remount every card on the page and lose its state.
|
|
1422
1551
|
*/
|
|
1423
1552
|
_redrawIfStale() {
|
|
1424
1553
|
if (this._streaming || this._editing || renderingVersionOf(this._cfg) === this._drawnWithVersion) return;
|
|
1425
|
-
if (this._segments.length
|
|
1426
|
-
|
|
1554
|
+
if (this._segments.length === 0) {
|
|
1555
|
+
this._updateContent();
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
if (this._segments.some((s2) => !this._drawn.has(s2.id))) {
|
|
1559
|
+
this._renderSegments();
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
this._drawnWithVersion = renderingVersionOf(this._cfg);
|
|
1563
|
+
for (const segment of this._segments) {
|
|
1564
|
+
const drawn = this._drawn.get(segment.id);
|
|
1565
|
+
if (this._keepsItsDrawing(segment, drawn)) continue;
|
|
1566
|
+
this._redrawSegment(segment);
|
|
1567
|
+
}
|
|
1568
|
+
this._reflectError();
|
|
1569
|
+
this._syncCopyAction();
|
|
1427
1570
|
}
|
|
1428
1571
|
/**
|
|
1429
1572
|
* Ask every rendered segment to re-read its config-derived text.
|
|
@@ -1482,7 +1625,12 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1482
1625
|
this._render();
|
|
1483
1626
|
this._updateContent();
|
|
1484
1627
|
this._updateTimestamp(this.getAttribute("timestamp"));
|
|
1628
|
+
if (this._releasedWhileDetached) {
|
|
1629
|
+
this._releasedWhileDetached = false;
|
|
1630
|
+
if (this._segments.length > 0) this._renderSegments();
|
|
1631
|
+
}
|
|
1485
1632
|
window.addEventListener("aparte-config-change", this._onConfigChange);
|
|
1633
|
+
this._offRenderers ??= onRenderersChange(this._onRenderersChange);
|
|
1486
1634
|
this.addEventListener("click", this._onBranchPickerClick);
|
|
1487
1635
|
this.addEventListener("click", this._onLinkClick);
|
|
1488
1636
|
}
|
|
@@ -1499,7 +1647,10 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1499
1647
|
* into one.
|
|
1500
1648
|
*/
|
|
1501
1649
|
aparteConfigChanged() {
|
|
1502
|
-
queueMicrotask(() =>
|
|
1650
|
+
queueMicrotask(() => {
|
|
1651
|
+
this._redrawFrameIfChanged();
|
|
1652
|
+
this._redrawSegmentsWhoseRendererChanged();
|
|
1653
|
+
});
|
|
1503
1654
|
}
|
|
1504
1655
|
_redrawFrameIfChanged() {
|
|
1505
1656
|
if (!this.isConnected || this._editing) return;
|
|
@@ -1512,6 +1663,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1512
1663
|
this._avatarCleanup = null;
|
|
1513
1664
|
}
|
|
1514
1665
|
this._resetMarkdownStream();
|
|
1666
|
+
this._releaseAll();
|
|
1515
1667
|
this.replaceChildren();
|
|
1516
1668
|
this._render();
|
|
1517
1669
|
this._updateContent();
|
|
@@ -1522,6 +1674,8 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1522
1674
|
}
|
|
1523
1675
|
disconnectedCallback() {
|
|
1524
1676
|
window.removeEventListener("aparte-config-change", this._onConfigChange);
|
|
1677
|
+
this._offRenderers?.();
|
|
1678
|
+
this._offRenderers = null;
|
|
1525
1679
|
this.removeEventListener("click", this._onBranchPickerClick);
|
|
1526
1680
|
this.removeEventListener("click", this._onLinkClick);
|
|
1527
1681
|
if (this._avatarCleanup) {
|
|
@@ -1531,6 +1685,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1531
1685
|
}
|
|
1532
1686
|
this._avatarCleanup = null;
|
|
1533
1687
|
}
|
|
1688
|
+
this._scheduleReleaseCheck();
|
|
1534
1689
|
}
|
|
1535
1690
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
1536
1691
|
if (oldValue === newValue) return;
|
|
@@ -1591,10 +1746,38 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1591
1746
|
}
|
|
1592
1747
|
/** Set segments for rich content */
|
|
1593
1748
|
setSegments(segments) {
|
|
1594
|
-
this.
|
|
1595
|
-
this._renderSegments();
|
|
1749
|
+
this._replaceSegments(segments);
|
|
1596
1750
|
this._updateWaiting();
|
|
1597
1751
|
}
|
|
1752
|
+
/** A whole new list, from `setSegments` or `updateMessage({ segments })`: copied in, then drawn. */
|
|
1753
|
+
_replaceSegments(segments) {
|
|
1754
|
+
const next = [...segments];
|
|
1755
|
+
if (this._resyncKeepingCards(next)) return;
|
|
1756
|
+
this._segments = next;
|
|
1757
|
+
this._renderSegments();
|
|
1758
|
+
}
|
|
1759
|
+
/**
|
|
1760
|
+
* The same segments again, in the same order — the host re-syncing a message when its turn
|
|
1761
|
+
* ends — while a mounted renderer holds one of them: the cards it holds are handed the new
|
|
1762
|
+
* state, and only the other segments are drawn again, in place. Drawing everything again
|
|
1763
|
+
* unmounted every framework card at the end of every turn and lost its state. False, and
|
|
1764
|
+
* nothing touched, in every other case: those keep the full redraw.
|
|
1765
|
+
*/
|
|
1766
|
+
_resyncKeepingCards(next) {
|
|
1767
|
+
if (!this._segmentsEl || next.length !== this._segments.length) return false;
|
|
1768
|
+
if (next.some((s2, i2) => s2.id !== this._segments[i2].id || !this._drawn.has(s2.id))) return false;
|
|
1769
|
+
const kept = new Set(next.filter((s2) => this._keepsItsDrawing(s2, this._drawn.get(s2.id))).map((s2) => s2.id));
|
|
1770
|
+
if (kept.size === 0) return false;
|
|
1771
|
+
this._segments = next;
|
|
1772
|
+
this._drawnWithVersion = renderingVersionOf(this._cfg);
|
|
1773
|
+
for (const segment of next) {
|
|
1774
|
+
if (kept.has(segment.id)) this._applySegmentUpdate(segment.id, segment, segment);
|
|
1775
|
+
else this._redrawSegment(segment);
|
|
1776
|
+
}
|
|
1777
|
+
this._reflectError();
|
|
1778
|
+
this._syncCopyAction();
|
|
1779
|
+
return true;
|
|
1780
|
+
}
|
|
1598
1781
|
/** Add a segment */
|
|
1599
1782
|
addSegment(segment) {
|
|
1600
1783
|
this._segments.push(segment);
|
|
@@ -1645,6 +1828,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1645
1828
|
if (index !== -1) {
|
|
1646
1829
|
this._segments.splice(index, 1);
|
|
1647
1830
|
}
|
|
1831
|
+
this._release(segmentId);
|
|
1648
1832
|
const el2 = this._segmentsEl?.querySelector(`:scope > [data-segment-id="${cssEscape(segmentId)}"]`);
|
|
1649
1833
|
el2?.remove();
|
|
1650
1834
|
this._updateWaiting();
|
|
@@ -1693,8 +1877,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1693
1877
|
this._updateContent();
|
|
1694
1878
|
}
|
|
1695
1879
|
if ("segments" in updates) {
|
|
1696
|
-
this.
|
|
1697
|
-
this._renderSegments();
|
|
1880
|
+
this._replaceSegments(updates.segments);
|
|
1698
1881
|
}
|
|
1699
1882
|
if ("timestamp" in updates) {
|
|
1700
1883
|
this._updateTimestamp(updates.timestamp);
|
|
@@ -1723,10 +1906,12 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1723
1906
|
const el2 = segmentRenderResultToElement(runWithConfig(this._cfg, () => renderer.render(segment)), segment);
|
|
1724
1907
|
if (el2) {
|
|
1725
1908
|
this._segmentsEl.appendChild(el2);
|
|
1726
|
-
|
|
1909
|
+
this._install(segment, el2, renderer);
|
|
1727
1910
|
}
|
|
1728
1911
|
} else {
|
|
1729
|
-
|
|
1912
|
+
const el2 = unrenderedSegment(segment);
|
|
1913
|
+
this._segmentsEl.appendChild(el2);
|
|
1914
|
+
this._install(segment, el2, void 0);
|
|
1730
1915
|
}
|
|
1731
1916
|
if (this._contentEl) this._contentEl.style.display = "none";
|
|
1732
1917
|
this._reflectError();
|
|
@@ -1737,15 +1922,32 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1737
1922
|
this._renderSegments();
|
|
1738
1923
|
return;
|
|
1739
1924
|
}
|
|
1925
|
+
if (this._releasedWhileDetached) return;
|
|
1740
1926
|
const renderer = resolveSegmentRenderer(segment.type, this._cfg);
|
|
1741
1927
|
if (!renderer) return;
|
|
1928
|
+
const drawnBefore = this._drawn.get(segmentId);
|
|
1929
|
+
if (drawnBefore && drawnBefore.renderer !== renderer) {
|
|
1930
|
+
drawnBefore.segment = segment;
|
|
1931
|
+
this._redrawSegment(segment);
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1742
1934
|
if (renderer.update) {
|
|
1743
1935
|
runWithConfig(this._cfg, () => renderer.update(el2, segment));
|
|
1936
|
+
const drawn = this._drawn.get(segmentId);
|
|
1937
|
+
if (drawn) {
|
|
1938
|
+
drawn.segment = segment;
|
|
1939
|
+
const now = this._segmentsEl?.querySelector(`:scope > [data-segment-id="${cssEscape(segmentId)}"]`);
|
|
1940
|
+
if (now && now !== drawn.el) {
|
|
1941
|
+
drawn.el = now;
|
|
1942
|
+
drawn.tool = segment.type === "tool_call" ? toolRendererThatDrew(now) : null;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1744
1945
|
} else {
|
|
1745
1946
|
const newEl = segmentRenderResultToElement(runWithConfig(this._cfg, () => renderer.render(segment)), segment);
|
|
1746
1947
|
if (newEl) {
|
|
1948
|
+
this._release(segmentId);
|
|
1747
1949
|
el2.replaceWith(newEl);
|
|
1748
|
-
|
|
1950
|
+
this._install(segment, newEl, renderer);
|
|
1749
1951
|
}
|
|
1750
1952
|
}
|
|
1751
1953
|
if ("collapsed" in updates) {
|
|
@@ -2025,6 +2227,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
2025
2227
|
_renderSegments() {
|
|
2026
2228
|
if (!this._segmentsEl) return;
|
|
2027
2229
|
this._drawnWithVersion = renderingVersionOf(this._cfg);
|
|
2230
|
+
this._releaseAll();
|
|
2028
2231
|
this._segmentsEl.innerHTML = "";
|
|
2029
2232
|
for (const segment of this._segments) {
|
|
2030
2233
|
const renderer = resolveSegmentRenderer(segment.type, this._cfg);
|
|
@@ -2032,10 +2235,12 @@ class AparteChatBubble extends HTMLElement {
|
|
|
2032
2235
|
const el2 = segmentRenderResultToElement(runWithConfig(this._cfg, () => renderer.render(segment)), segment);
|
|
2033
2236
|
if (el2) {
|
|
2034
2237
|
this._segmentsEl.appendChild(el2);
|
|
2035
|
-
|
|
2238
|
+
this._install(segment, el2, renderer);
|
|
2036
2239
|
}
|
|
2037
2240
|
} else {
|
|
2038
|
-
|
|
2241
|
+
const el2 = unrenderedSegment(segment);
|
|
2242
|
+
this._segmentsEl.appendChild(el2);
|
|
2243
|
+
this._install(segment, el2, void 0);
|
|
2039
2244
|
}
|
|
2040
2245
|
}
|
|
2041
2246
|
if (this._contentEl) {
|
|
@@ -2097,18 +2302,18 @@ class AparteChatBubble extends HTMLElement {
|
|
|
2097
2302
|
const customAttachment = this._cfg.getAttachmentRenderer?.();
|
|
2098
2303
|
if (customAttachment) {
|
|
2099
2304
|
this._attachmentsEl.replaceChildren();
|
|
2100
|
-
for (const
|
|
2101
|
-
const el2 = segmentRenderResultToElement(runWithConfig(this._cfg, () => customAttachment(
|
|
2305
|
+
for (const a6 of this._attachments) {
|
|
2306
|
+
const el2 = segmentRenderResultToElement(runWithConfig(this._cfg, () => customAttachment(a6)));
|
|
2102
2307
|
if (el2) this._attachmentsEl.appendChild(el2);
|
|
2103
2308
|
}
|
|
2104
2309
|
return;
|
|
2105
2310
|
}
|
|
2106
|
-
this._attachmentsEl.innerHTML = this._attachments.map((
|
|
2107
|
-
const name = escapeHtml(
|
|
2108
|
-
if (
|
|
2109
|
-
return `<div class="aparte-thumbnail aparte-thumb aparte-thumb--image" title="${name}"><img class="aparte-thumbnail__image" src="${escapeHtml(
|
|
2311
|
+
this._attachmentsEl.innerHTML = this._attachments.map((a6) => {
|
|
2312
|
+
const name = escapeHtml(a6.name);
|
|
2313
|
+
if (a6.type.startsWith("image/") && this._isShowableUrl(a6.url)) {
|
|
2314
|
+
return `<div class="aparte-thumbnail aparte-thumb aparte-thumb--image" title="${name}"><img class="aparte-thumbnail__image" src="${escapeHtml(a6.url)}" alt="${name}" loading="lazy" /><span class="aparte-thumb__name">${name}</span></div>`;
|
|
2110
2315
|
}
|
|
2111
|
-
return `<div class="aparte-thumbnail aparte-thumb aparte-thumb--file" title="${name}"><span class="aparte-thumbnail__label">${escapeHtml(this._fileExt(
|
|
2316
|
+
return `<div class="aparte-thumbnail aparte-thumb aparte-thumb--file" title="${name}"><span class="aparte-thumbnail__label">${escapeHtml(this._fileExt(a6.name))}</span><span class="aparte-thumb__name">${name}</span></div>`;
|
|
2112
2317
|
}).join("");
|
|
2113
2318
|
if (!this._cfg.getHostHandlers().attachmentPreview) return;
|
|
2114
2319
|
this._attachmentsEl.querySelectorAll(".aparte-thumb--image").forEach((tile) => {
|
|
@@ -2294,14 +2499,14 @@ class AparteChatBubble extends HTMLElement {
|
|
|
2294
2499
|
const buttons = [];
|
|
2295
2500
|
if (this._role === "user") {
|
|
2296
2501
|
if (config.user) {
|
|
2297
|
-
for (const
|
|
2502
|
+
for (const a6 of config.user) buttons.push(this._actionButtonHtml(a6, icons, locale));
|
|
2298
2503
|
} else {
|
|
2299
2504
|
if (config.copy) buttons.push(this._actionButtonHtml("copy", icons, locale));
|
|
2300
2505
|
if (config.edit) buttons.push(this._actionButtonHtml("edit", icons, locale));
|
|
2301
2506
|
}
|
|
2302
2507
|
} else if (this._role === "assistant") {
|
|
2303
2508
|
if (config.assistant) {
|
|
2304
|
-
for (const
|
|
2509
|
+
for (const a6 of config.assistant) buttons.push(this._actionButtonHtml(a6, icons, locale));
|
|
2305
2510
|
} else {
|
|
2306
2511
|
if (config.copy) buttons.push(this._actionButtonHtml("copy", icons, locale));
|
|
2307
2512
|
if (config.retry) buttons.push(this._actionButtonHtml("retry", icons, locale));
|
|
@@ -2397,16 +2602,16 @@ class AparteChatBubble extends HTMLElement {
|
|
|
2397
2602
|
/** Append the registered custom action buttons for this bubble's role. */
|
|
2398
2603
|
_appendCustomActions() {
|
|
2399
2604
|
if (!this._actionBarEl) return;
|
|
2400
|
-
for (const
|
|
2401
|
-
const roles =
|
|
2605
|
+
for (const a6 of this._cfg.getActions("bubble")) {
|
|
2606
|
+
const roles = a6.bubble?.roles ?? ["user", "assistant"];
|
|
2402
2607
|
if (!roles.includes(this._role)) continue;
|
|
2403
2608
|
const btn = document.createElement("button");
|
|
2404
2609
|
btn.type = "button";
|
|
2405
2610
|
btn.className = "aparte-btn aparte-btn--icon aparte-btn--sm aparte-action-btn aparte-action-custom";
|
|
2406
|
-
btn.dataset["action"] = `custom:${
|
|
2407
|
-
btn.setAttribute("aria-label",
|
|
2408
|
-
btn.setAttribute("title",
|
|
2409
|
-
btn.innerHTML =
|
|
2611
|
+
btn.dataset["action"] = `custom:${a6.id}`;
|
|
2612
|
+
btn.setAttribute("aria-label", a6.label);
|
|
2613
|
+
btn.setAttribute("title", a6.label);
|
|
2614
|
+
btn.innerHTML = a6.icon.startsWith("<") ? a6.icon : this._cfg.getIcon(a6.icon) ?? a6.iconFallback ?? "";
|
|
2410
2615
|
this._actionBarEl.appendChild(btn);
|
|
2411
2616
|
}
|
|
2412
2617
|
}
|
|
@@ -2653,11 +2858,10 @@ class AparteChatBubble extends HTMLElement {
|
|
|
2653
2858
|
}
|
|
2654
2859
|
this._updateWaiting();
|
|
2655
2860
|
if (wasStreaming && !streaming) this._updateContent();
|
|
2656
|
-
if (wasStreaming && !streaming && this._segments.length > 0
|
|
2657
|
-
this._renderSegments();
|
|
2658
|
-
}
|
|
2861
|
+
if (wasStreaming && !streaming && this._segments.length > 0) this._redrawIfStale();
|
|
2659
2862
|
}
|
|
2660
2863
|
}
|
|
2864
|
+
deferHydrationOf(AparteChatBubble);
|
|
2661
2865
|
if (!customElements.get("aparte-chat-bubble")) {
|
|
2662
2866
|
customElements.define("aparte-chat-bubble", AparteChatBubble);
|
|
2663
2867
|
}
|
|
@@ -2831,6 +3035,7 @@ class AparteChatStatus extends HTMLElement {
|
|
|
2831
3035
|
container.setAttribute("aria-label", text || this._word());
|
|
2832
3036
|
}
|
|
2833
3037
|
}
|
|
3038
|
+
deferHydrationOf(AparteChatStatus);
|
|
2834
3039
|
if (!customElements.get("aparte-chat-status")) {
|
|
2835
3040
|
customElements.define("aparte-chat-status", AparteChatStatus);
|
|
2836
3041
|
}
|
|
@@ -3243,10 +3448,10 @@ class AparteChatViewport extends HTMLElement {
|
|
|
3243
3448
|
this._notifyBubble(messageId, "addSegment", stamped);
|
|
3244
3449
|
this._autoScroll();
|
|
3245
3450
|
}
|
|
3246
|
-
updateSegment(
|
|
3451
|
+
updateSegment(a6, b2, c2) {
|
|
3247
3452
|
const clientForm = c2 === void 0 && typeof b2 === "object";
|
|
3248
|
-
const messageId = clientForm ? this._activeMessageId() :
|
|
3249
|
-
const segmentId = clientForm ?
|
|
3453
|
+
const messageId = clientForm ? this._activeMessageId() : a6;
|
|
3454
|
+
const segmentId = clientForm ? a6 : b2;
|
|
3250
3455
|
const updates = clientForm ? b2 : c2;
|
|
3251
3456
|
if (!messageId) return;
|
|
3252
3457
|
const message = this._repo.getMessageById(messageId);
|
|
@@ -3259,10 +3464,10 @@ class AparteChatViewport extends HTMLElement {
|
|
|
3259
3464
|
this._notifyBubble(messageId, "updateSegment", { segmentId, updates: stamped });
|
|
3260
3465
|
}
|
|
3261
3466
|
}
|
|
3262
|
-
removeSegment(
|
|
3467
|
+
removeSegment(a6, b2) {
|
|
3263
3468
|
const clientForm = b2 === void 0;
|
|
3264
|
-
const messageId = clientForm ? this._activeMessageId() :
|
|
3265
|
-
const segmentId = clientForm ?
|
|
3469
|
+
const messageId = clientForm ? this._activeMessageId() : a6;
|
|
3470
|
+
const segmentId = clientForm ? a6 : b2;
|
|
3266
3471
|
if (!messageId || !segmentId) return;
|
|
3267
3472
|
const message = this._repo.getMessageById(messageId);
|
|
3268
3473
|
if (message?.segments) {
|
|
@@ -4399,6 +4604,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
4399
4604
|
this._settleUntil = 0;
|
|
4400
4605
|
}
|
|
4401
4606
|
}
|
|
4607
|
+
deferHydrationOf(AparteChatViewport);
|
|
4402
4608
|
if (!customElements.get("aparte-chat-viewport")) {
|
|
4403
4609
|
customElements.define("aparte-chat-viewport", AparteChatViewport);
|
|
4404
4610
|
}
|
|
@@ -4905,6 +5111,7 @@ class AparteComposer extends HTMLElement {
|
|
|
4905
5111
|
if (this._panelActive) this._evictPanel();
|
|
4906
5112
|
}
|
|
4907
5113
|
}
|
|
5114
|
+
deferHydrationOf(AparteComposer);
|
|
4908
5115
|
if (!customElements.get("aparte-composer")) {
|
|
4909
5116
|
customElements.define("aparte-composer", AparteComposer);
|
|
4910
5117
|
}
|
|
@@ -5152,6 +5359,7 @@ class AparteComposerInput extends HTMLElement {
|
|
|
5152
5359
|
}
|
|
5153
5360
|
/** Escape a value before it lands in a double-quoted HTML attribute. */
|
|
5154
5361
|
}
|
|
5362
|
+
deferHydrationOf(AparteComposerInput);
|
|
5155
5363
|
if (!customElements.get("aparte-composer-input")) {
|
|
5156
5364
|
customElements.define("aparte-composer-input", AparteComposerInput);
|
|
5157
5365
|
}
|
|
@@ -5313,6 +5521,7 @@ class AparteComposerSend extends HTMLElement {
|
|
|
5313
5521
|
return resolveConfig(this).getIcon("stop");
|
|
5314
5522
|
}
|
|
5315
5523
|
}
|
|
5524
|
+
deferHydrationOf(AparteComposerSend);
|
|
5316
5525
|
if (!customElements.get("aparte-composer-send")) {
|
|
5317
5526
|
customElements.define("aparte-composer-send", AparteComposerSend);
|
|
5318
5527
|
}
|
|
@@ -5381,6 +5590,7 @@ class AparteComposerCancel extends HTMLElement {
|
|
|
5381
5590
|
return resolveConfig(this).getIcon("stop");
|
|
5382
5591
|
}
|
|
5383
5592
|
}
|
|
5593
|
+
deferHydrationOf(AparteComposerCancel);
|
|
5384
5594
|
if (!customElements.get("aparte-composer-cancel")) {
|
|
5385
5595
|
customElements.define("aparte-composer-cancel", AparteComposerCancel);
|
|
5386
5596
|
}
|
|
@@ -5466,6 +5676,7 @@ class AparteComposerAttachments extends HTMLElement {
|
|
|
5466
5676
|
return dot > 0 ? filename.slice(dot + 1).toUpperCase().slice(0, 4) : "FILE";
|
|
5467
5677
|
}
|
|
5468
5678
|
}
|
|
5679
|
+
deferHydrationOf(AparteComposerAttachments);
|
|
5469
5680
|
if (!customElements.get("aparte-composer-attachments")) {
|
|
5470
5681
|
customElements.define("aparte-composer-attachments", AparteComposerAttachments);
|
|
5471
5682
|
}
|
|
@@ -5592,6 +5803,7 @@ class AparteComposerAddAttachment extends HTMLElement {
|
|
|
5592
5803
|
};
|
|
5593
5804
|
}
|
|
5594
5805
|
}
|
|
5806
|
+
deferHydrationOf(AparteComposerAddAttachment);
|
|
5595
5807
|
if (!customElements.get("aparte-composer-add-attachment")) {
|
|
5596
5808
|
customElements.define("aparte-composer-add-attachment", AparteComposerAddAttachment);
|
|
5597
5809
|
}
|
|
@@ -5674,6 +5886,7 @@ class AparteComposerAction extends HTMLElement {
|
|
|
5674
5886
|
return resolveConfig(this).getIcon(icon) ?? icon;
|
|
5675
5887
|
}
|
|
5676
5888
|
}
|
|
5889
|
+
deferHydrationOf(AparteComposerAction);
|
|
5677
5890
|
if (!customElements.get("aparte-composer-action")) {
|
|
5678
5891
|
customElements.define("aparte-composer-action", AparteComposerAction);
|
|
5679
5892
|
}
|
|
@@ -5712,6 +5925,7 @@ class AparteComposerToolbar extends HTMLElement {
|
|
|
5712
5925
|
else this.setAttribute("data-empty", "");
|
|
5713
5926
|
}
|
|
5714
5927
|
}
|
|
5928
|
+
deferHydrationOf(AparteComposerToolbar);
|
|
5715
5929
|
if (!customElements.get("aparte-composer-toolbar")) {
|
|
5716
5930
|
customElements.define("aparte-composer-toolbar", AparteComposerToolbar);
|
|
5717
5931
|
}
|
|
@@ -5730,6 +5944,7 @@ function filterConversationRows(root, needle) {
|
|
|
5730
5944
|
}
|
|
5731
5945
|
return matches;
|
|
5732
5946
|
}
|
|
5947
|
+
const statusOf = (value) => value === "running" || value === "waiting" ? value : null;
|
|
5733
5948
|
function rowSelector(attr, id) {
|
|
5734
5949
|
return `[${attr}="${cssEscape(id)}"]`;
|
|
5735
5950
|
}
|
|
@@ -5786,6 +6001,7 @@ class AparteConversationList extends HTMLElement {
|
|
|
5786
6001
|
this.addEventListener("pointerdown", this._onPress);
|
|
5787
6002
|
this._render();
|
|
5788
6003
|
window.addEventListener("aparte-config-change", this._onConfigChange);
|
|
6004
|
+
window.addEventListener("aparte-conversation-status", this._onStatus);
|
|
5789
6005
|
}
|
|
5790
6006
|
disconnectedCallback() {
|
|
5791
6007
|
this._closeMenu();
|
|
@@ -5798,6 +6014,7 @@ class AparteConversationList extends HTMLElement {
|
|
|
5798
6014
|
this._pressed = false;
|
|
5799
6015
|
this._renderAfterPress = false;
|
|
5800
6016
|
window.removeEventListener("aparte-config-change", this._onConfigChange);
|
|
6017
|
+
window.removeEventListener("aparte-conversation-status", this._onStatus);
|
|
5801
6018
|
}
|
|
5802
6019
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
5803
6020
|
if (oldValue === newValue) return;
|
|
@@ -5819,7 +6036,54 @@ class AparteConversationList extends HTMLElement {
|
|
|
5819
6036
|
if (detail?.config && detail.config !== resolveConfig(this)) return;
|
|
5820
6037
|
this._render();
|
|
5821
6038
|
};
|
|
6039
|
+
/**
|
|
6040
|
+
* `aparte-conversation-status`, on `window`: the controller's, a list's, or an app's own.
|
|
6041
|
+
* Kept page-wide too (an app may dispatch the event itself), then drawn on this row.
|
|
6042
|
+
*/
|
|
6043
|
+
_onStatus = (e) => {
|
|
6044
|
+
const detail = e.detail;
|
|
6045
|
+
if (typeof detail?.id !== "string") return;
|
|
6046
|
+
rememberConversationStatus(detail.id, statusOf(detail.status));
|
|
6047
|
+
const row = this.querySelector(rowSelector("data-conv-id", detail.id));
|
|
6048
|
+
if (row) this._applyStatus(row, detail.id);
|
|
6049
|
+
};
|
|
5822
6050
|
// ─── Public API ───────────────────────────────────────────────────────
|
|
6051
|
+
/**
|
|
6052
|
+
* Say what a conversation is doing: `running` while an AI answers in it, `waiting`
|
|
6053
|
+
* while its turn waits for the person, `null` for neither. The row carries it as
|
|
6054
|
+
* `data-status`, a running title wears `aparte-shimmer`, a waiting row shows its mark
|
|
6055
|
+
* (`.aparte-conv-item__status`, an empty slot otherwise), and a screen reader hears
|
|
6056
|
+
* the state with the title. The look is CSS on `data-status`: a site that wants a dot
|
|
6057
|
+
* for `running` draws it in the slot and takes the shimmer off.
|
|
6058
|
+
*
|
|
6059
|
+
* A state is about a conversation, not about this list: it is kept page-wide and every
|
|
6060
|
+
* list on the page draws it, one that connects later included. Patched in place, and
|
|
6061
|
+
* kept across re-renders and for rows not drawn yet. The conversation controller sets
|
|
6062
|
+
* it on its own through `aparte-conversation-status`.
|
|
6063
|
+
*/
|
|
6064
|
+
setConversationStatus(id, status) {
|
|
6065
|
+
tellConversationStatus(id, statusOf(status));
|
|
6066
|
+
}
|
|
6067
|
+
/** What a conversation is doing, as last told; `null` for neither. */
|
|
6068
|
+
getConversationStatus(id) {
|
|
6069
|
+
return conversationStatusOf(id);
|
|
6070
|
+
}
|
|
6071
|
+
/** Write a conversation's state onto its row — the same three things `_renderItem` draws. */
|
|
6072
|
+
_applyStatus(row, id) {
|
|
6073
|
+
const status = conversationStatusOf(id);
|
|
6074
|
+
if (status) row.setAttribute("data-status", status);
|
|
6075
|
+
else row.removeAttribute("data-status");
|
|
6076
|
+
const answering = status === "running";
|
|
6077
|
+
row.querySelector(".aparte-conv-item__title")?.classList.toggle("aparte-shimmer", answering);
|
|
6078
|
+
const heard = row.querySelector(".aparte-conv-item__state");
|
|
6079
|
+
if (heard) heard.textContent = this._statusWords(status);
|
|
6080
|
+
}
|
|
6081
|
+
/** The words a screen reader hears for a state. Literal `t('…')` reads, for the locale guard. */
|
|
6082
|
+
_statusWords(status) {
|
|
6083
|
+
if (status === "running") return this._t("conversationRunning");
|
|
6084
|
+
if (status === "waiting") return this._t("conversationWaiting");
|
|
6085
|
+
return "";
|
|
6086
|
+
}
|
|
5823
6087
|
/** Set the list of conversations to display. Triggers a re-render. */
|
|
5824
6088
|
set conversations(items) {
|
|
5825
6089
|
this._conversations = Array.isArray(items) ? items : [];
|
|
@@ -6003,7 +6267,7 @@ class AparteConversationList extends HTMLElement {
|
|
|
6003
6267
|
}
|
|
6004
6268
|
}
|
|
6005
6269
|
const fixed = ["pinned", "today", "yesterday", "week", "month"].map((k2) => buckets.get(k2)).filter((g2) => !!g2);
|
|
6006
|
-
const months = [...buckets.keys()].filter((k2) => k2.startsWith("m-")).sort((
|
|
6270
|
+
const months = [...buckets.keys()].filter((k2) => k2.startsWith("m-")).sort((a6, b2) => b2.localeCompare(a6)).map((k2) => buckets.get(k2));
|
|
6007
6271
|
const undated = buckets.get("undated");
|
|
6008
6272
|
return [...fixed, ...months, ...undated ? [undated] : []];
|
|
6009
6273
|
}
|
|
@@ -6031,14 +6295,18 @@ class AparteConversationList extends HTMLElement {
|
|
|
6031
6295
|
const title = escapeHtml(conv.title || this._t("newChat"));
|
|
6032
6296
|
const actionsLabel = escapeAttr(this._t("conversationActions"));
|
|
6033
6297
|
const moreGlyph = resolveConfig(this).getIcon("more");
|
|
6298
|
+
const status = conversationStatusOf(conv.id);
|
|
6299
|
+
const statusAttr = status ? ` data-status="${escapeAttr(status)}"` : "";
|
|
6300
|
+
const titleClass = escapeAttr(status === "running" ? "aparte-conv-item__title aparte-shimmer" : "aparte-conv-item__title");
|
|
6301
|
+
const heard = escapeHtml(this._statusWords(status));
|
|
6034
6302
|
return `
|
|
6035
|
-
<div class="${escapeAttr(classes)}" data-conv-id="${escapedId}">
|
|
6303
|
+
<div class="${escapeAttr(classes)}" data-conv-id="${escapedId}"${statusAttr}>
|
|
6036
6304
|
<button
|
|
6037
6305
|
class="aparte-conv-item__select"
|
|
6038
6306
|
type="button"
|
|
6039
6307
|
data-select-id="${escapedId}"
|
|
6040
6308
|
aria-current="${isActive ? "page" : "false"}"
|
|
6041
|
-
><span class="aparte-conv-
|
|
6309
|
+
><span class="${titleClass}">${title}</span><span class="aparte-conv-item__status" aria-hidden="true"></span><span class="aparte-conv-item__state aparte-sr-only">${heard}</span></button>
|
|
6042
6310
|
<button
|
|
6043
6311
|
class="aparte-btn aparte-btn--icon aparte-btn--sm aparte-conv-item__more"
|
|
6044
6312
|
type="button"
|
|
@@ -6418,6 +6686,7 @@ class AparteConversationList extends HTMLElement {
|
|
|
6418
6686
|
});
|
|
6419
6687
|
}
|
|
6420
6688
|
}
|
|
6689
|
+
deferHydrationOf(AparteConversationList);
|
|
6421
6690
|
if (!customElements.get("aparte-conversation-list")) customElements.define("aparte-conversation-list", AparteConversationList);
|
|
6422
6691
|
const isSuggestion = (value) => typeof value === "string" || typeof value === "object" && value !== null && typeof value.label === "string";
|
|
6423
6692
|
const parseSuggestions = (raw) => {
|
|
@@ -6645,6 +6914,7 @@ class AparteSuggestions extends HTMLElement {
|
|
|
6645
6914
|
}
|
|
6646
6915
|
}
|
|
6647
6916
|
}
|
|
6917
|
+
deferHydrationOf(AparteSuggestions);
|
|
6648
6918
|
if (!customElements.get("aparte-suggestions")) {
|
|
6649
6919
|
customElements.define("aparte-suggestions", AparteSuggestions);
|
|
6650
6920
|
}
|
|
@@ -6847,6 +7117,7 @@ class AparteContext extends HTMLElement {
|
|
|
6847
7117
|
}
|
|
6848
7118
|
}
|
|
6849
7119
|
}
|
|
7120
|
+
deferHydrationOf(AparteContext);
|
|
6850
7121
|
if (!customElements.get("aparte-context")) {
|
|
6851
7122
|
customElements.define("aparte-context", AparteContext);
|
|
6852
7123
|
}
|
|
@@ -7309,11 +7580,12 @@ class AparteScrollRail extends HTMLElement {
|
|
|
7309
7580
|
this._held = null;
|
|
7310
7581
|
}
|
|
7311
7582
|
}
|
|
7312
|
-
function sameElements(
|
|
7313
|
-
if (
|
|
7314
|
-
for (let i2 = 0; i2 <
|
|
7583
|
+
function sameElements(a6, b2) {
|
|
7584
|
+
if (a6.length !== b2.length) return false;
|
|
7585
|
+
for (let i2 = 0; i2 < a6.length; i2++) if (a6[i2] !== b2[i2]) return false;
|
|
7315
7586
|
return true;
|
|
7316
7587
|
}
|
|
7588
|
+
deferHydrationOf(AparteScrollRail);
|
|
7317
7589
|
if (!customElements.get("aparte-scroll-rail")) {
|
|
7318
7590
|
customElements.define("aparte-scroll-rail", AparteScrollRail);
|
|
7319
7591
|
}
|
|
@@ -7664,6 +7936,7 @@ function visibleFocusable(root) {
|
|
|
7664
7936
|
function nearestSidebar(control) {
|
|
7665
7937
|
return control.closest("aparte-sidebar") ?? rootOf(control).querySelector("aparte-sidebar") ?? document.querySelector("aparte-sidebar");
|
|
7666
7938
|
}
|
|
7939
|
+
deferHydrationOf(AparteSidebar);
|
|
7667
7940
|
if (!customElements.get("aparte-sidebar")) {
|
|
7668
7941
|
customElements.define("aparte-sidebar", AparteSidebar);
|
|
7669
7942
|
}
|
|
@@ -8442,6 +8715,7 @@ class AparteSplit extends HTMLElement {
|
|
|
8442
8715
|
handle.dataset["ownLabel"] = "locale";
|
|
8443
8716
|
}
|
|
8444
8717
|
}
|
|
8718
|
+
deferHydrationOf(AparteSplit);
|
|
8445
8719
|
if (!customElements.get("aparte-split")) {
|
|
8446
8720
|
customElements.define("aparte-split", AparteSplit);
|
|
8447
8721
|
}
|
|
@@ -8529,7 +8803,7 @@ export {
|
|
|
8529
8803
|
AparteSplit,
|
|
8530
8804
|
s as AparteStreamParser,
|
|
8531
8805
|
AparteSuggestions,
|
|
8532
|
-
|
|
8806
|
+
aparteGlobalConfig,
|
|
8533
8807
|
u as applyElementProps,
|
|
8534
8808
|
v as attachConfig,
|
|
8535
8809
|
buildApprovalPanel,
|
|
@@ -8545,34 +8819,40 @@ export {
|
|
|
8545
8819
|
cssEscape,
|
|
8546
8820
|
I as declineDefaultRenderers,
|
|
8547
8821
|
J as defaultSanitizer,
|
|
8548
|
-
|
|
8822
|
+
deferHydrationOf,
|
|
8823
|
+
L as defineMountedSegmentRenderer,
|
|
8824
|
+
M as defineMountedToolRenderer,
|
|
8825
|
+
N as detachConfig,
|
|
8549
8826
|
escapeAttr,
|
|
8550
8827
|
escapeHtml,
|
|
8551
|
-
|
|
8552
|
-
|
|
8828
|
+
Q as filesToAttachments,
|
|
8829
|
+
R as getAllRenderers,
|
|
8553
8830
|
getSegmentRenderer,
|
|
8554
8831
|
installDefaultRenderersOnce,
|
|
8555
8832
|
installDialogTriggersOnce,
|
|
8556
8833
|
isAwaitingReply,
|
|
8557
|
-
|
|
8834
|
+
W as isFormatAdapter,
|
|
8558
8835
|
isSafeUrl,
|
|
8559
8836
|
isSegmentSettled,
|
|
8560
|
-
|
|
8837
|
+
Z as overrideSegmentRenderer,
|
|
8838
|
+
_ as parseAparteEventStream,
|
|
8561
8839
|
parseMarkdownToSegments,
|
|
8562
8840
|
populateBubbleFromMessage,
|
|
8563
|
-
|
|
8841
|
+
a1 as readableToAsyncIterable,
|
|
8564
8842
|
registerAllComponents,
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8843
|
+
a2 as registerDefaultRenderers,
|
|
8844
|
+
a3 as registerSegmentRenderer,
|
|
8845
|
+
a4 as releaseDeferredHydration,
|
|
8846
|
+
a5 as requestUserInput,
|
|
8568
8847
|
resolveConfig,
|
|
8569
8848
|
revokeAttachmentUrls,
|
|
8570
8849
|
runWithConfig,
|
|
8571
|
-
|
|
8572
|
-
|
|
8850
|
+
a9 as segmentDuration,
|
|
8851
|
+
aa as segmentTiming,
|
|
8573
8852
|
subscribeConfigChange,
|
|
8574
|
-
|
|
8575
|
-
|
|
8853
|
+
ac as toToolCallView,
|
|
8854
|
+
ad as toolRefusal,
|
|
8855
|
+
ae as unregisterSegmentRenderer,
|
|
8576
8856
|
uuid
|
|
8577
8857
|
};
|
|
8578
8858
|
//# sourceMappingURL=index.js.map
|