@aparte/core 0.3.0-alpha.0 → 0.4.0-alpha.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/README.md +21 -0
- package/dist/client/aparte-client.d.ts.map +1 -1
- package/dist/components/bubble/aparte-chat-bubble.d.ts +11 -0
- package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
- package/dist/components/chat/aparte-chat.d.ts +13 -0
- package/dist/components/chat/aparte-chat.d.ts.map +1 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts +13 -2
- package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
- package/dist/config/aparte-config.d.ts +10 -2
- package/dist/config/aparte-config.d.ts.map +1 -1
- package/dist/config/icon-provider.d.ts +21 -21
- package/dist/config/icon-provider.d.ts.map +1 -1
- package/dist/conversations/conversation-controller.d.ts.map +1 -1
- package/dist/custom-elements.json +7604 -7360
- package/dist/host/aparte-chat-host.d.ts +47 -1
- package/dist/host/aparte-chat-host.d.ts.map +1 -1
- package/dist/{index-CW2BCAqn.js → index-BBg6dsYx.js} +164 -41
- package/dist/index-BBg6dsYx.js.map +1 -0
- package/dist/index.css +60 -6
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +122 -26
- package/dist/index.js.map +1 -1
- package/dist/index.node.d.ts +2 -0
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +16 -14
- package/dist/index.node.js.map +1 -1
- package/dist/primitives/select/aparte-optgroup.d.ts +2 -0
- package/dist/primitives/select/aparte-optgroup.d.ts.map +1 -1
- package/dist/primitives/select/aparte-select.d.ts +2 -0
- package/dist/primitives/select/aparte-select.d.ts.map +1 -1
- package/dist/types/imperative-api.d.ts +13 -0
- package/dist/types/imperative-api.d.ts.map +1 -1
- package/dist/utils/files-to-attachments.d.ts +27 -0
- package/dist/utils/files-to-attachments.d.ts.map +1 -0
- package/dist/utils/is-awaiting-reply.d.ts +22 -0
- package/dist/utils/is-awaiting-reply.d.ts.map +1 -0
- package/package.json +8 -1
- package/dist/index-CW2BCAqn.js.map +0 -1
package/dist/index.css
CHANGED
|
@@ -216,6 +216,10 @@
|
|
|
216
216
|
--aparte-status-bg: transparent;
|
|
217
217
|
--aparte-status-dot-size: 6px;
|
|
218
218
|
|
|
219
|
+
/* Waiting Indicator (the bubble's own dots, before the first token) */
|
|
220
|
+
--aparte-waiting-height: 1.5em;
|
|
221
|
+
--aparte-waiting-dot-gap: 4px;
|
|
222
|
+
|
|
219
223
|
/* Thinking Segment */
|
|
220
224
|
--aparte-thinking-border: var(--aparte-border);
|
|
221
225
|
--aparte-thinking-bg: var(--aparte-surface-2);
|
|
@@ -712,8 +716,8 @@ aparte-chat-bubble {
|
|
|
712
716
|
min-width: 0;
|
|
713
717
|
}
|
|
714
718
|
|
|
715
|
-
/* The message bubble — the surface fills ONLY the content (
|
|
716
|
-
|
|
719
|
+
/* The message bubble — the surface fills ONLY the content (segments + text).
|
|
720
|
+
The sender name and attachment strip (above) and the action bar (below) are
|
|
717
721
|
siblings in .aparte-body, so they stay OUTSIDE the coloured surface. */
|
|
718
722
|
.aparte-message-content {
|
|
719
723
|
border-radius: var(--aparte-message-content-radius, 0);
|
|
@@ -782,8 +786,11 @@ aparte-chat-bubble {
|
|
|
782
786
|
.aparte-attachments {
|
|
783
787
|
display: flex;
|
|
784
788
|
flex-wrap: wrap;
|
|
785
|
-
/*
|
|
786
|
-
|
|
789
|
+
/* The strip sits directly above the message bubble and belongs to it, so it
|
|
790
|
+
must share the bubble's edge. The user bubble hugs its text on the LEADING
|
|
791
|
+
edge (AI-chat convention, see .aparte-message-content below), so anchoring
|
|
792
|
+
the strip to the trailing edge would split one message across both sides. */
|
|
793
|
+
justify-content: flex-start;
|
|
787
794
|
gap: var(--aparte-space-3, 6px);
|
|
788
795
|
margin-bottom: var(--aparte-space-4, 8px);
|
|
789
796
|
/* Really small tiles inside the conversation. */
|
|
@@ -1320,6 +1327,47 @@ aparte-chat-status:not([visible]) {
|
|
|
1320
1327
|
align-items: center;
|
|
1321
1328
|
}
|
|
1322
1329
|
|
|
1330
|
+
/* Screen-reader-only text. Used by the bubble's waiting indicator, whose dots are
|
|
1331
|
+
decorative: the state is conveyed by aria-busy + this localized label. */
|
|
1332
|
+
.aparte-sr-only {
|
|
1333
|
+
position: absolute;
|
|
1334
|
+
width: 1px;
|
|
1335
|
+
height: 1px;
|
|
1336
|
+
padding: 0;
|
|
1337
|
+
margin: -1px;
|
|
1338
|
+
overflow: hidden;
|
|
1339
|
+
clip: rect(0, 0, 0, 0);
|
|
1340
|
+
white-space: nowrap;
|
|
1341
|
+
border: 0;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/* The waiting indicator: between "user sends" and the first token. Built into the
|
|
1345
|
+
bubble, so it needs no wiring and behaves the same in core and every wrapper.
|
|
1346
|
+
Sits in the content region and disappears as soon as there is anything to show. */
|
|
1347
|
+
.aparte-waiting {
|
|
1348
|
+
display: flex;
|
|
1349
|
+
align-items: center;
|
|
1350
|
+
min-height: var(--aparte-waiting-height, 1.5em);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
.aparte-waiting[hidden] {
|
|
1354
|
+
display: none;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.aparte-waiting .aparte-dots {
|
|
1358
|
+
gap: var(--aparte-waiting-dot-gap, 4px);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/* Staggered so it reads as "working", not as three synchronized blobs. Scoped to
|
|
1362
|
+
the waiting region — the status element keeps its single-dot pulse. */
|
|
1363
|
+
.aparte-waiting .aparte-dot:nth-child(2) {
|
|
1364
|
+
animation-delay: 0.15s;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
.aparte-waiting .aparte-dot:nth-child(3) {
|
|
1368
|
+
animation-delay: 0.3s;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1323
1371
|
.aparte-dot {
|
|
1324
1372
|
width: var(--aparte-status-dot-size, 6px);
|
|
1325
1373
|
height: var(--aparte-status-dot-size, 6px);
|
|
@@ -2409,9 +2457,15 @@ aparte-option[data-active] {
|
|
|
2409
2457
|
box-shadow: inset 0 0 0 2px var(--aparte-primary, #3b82f6);
|
|
2410
2458
|
}
|
|
2411
2459
|
|
|
2460
|
+
/* Selected option: an accent TINT plus an accent bar, keeping the theme's own
|
|
2461
|
+
text colour. A solid `--aparte-primary` fill with white text failed WCAG AA in
|
|
2462
|
+
both themes (the brass accent is mid-luminance, so white sits around 3.4:1),
|
|
2463
|
+
and an unreadable selected row is the one row that must be readable. Both
|
|
2464
|
+
tokens stay overridable for a design that wants the solid fill back. */
|
|
2412
2465
|
aparte-option[selected] {
|
|
2413
|
-
background: var(--aparte-select-option-selected, var(--aparte-primary, #3b82f6));
|
|
2414
|
-
color: var(--aparte-select-option-selected-text, #
|
|
2466
|
+
background: var(--aparte-select-option-selected, color-mix(in srgb, var(--aparte-primary, #3b82f6) 18%, transparent));
|
|
2467
|
+
color: var(--aparte-select-option-selected-text, var(--aparte-select-text, var(--aparte-text, #1e293b)));
|
|
2468
|
+
box-shadow: inset 2px 0 0 var(--aparte-primary, #3b82f6);
|
|
2415
2469
|
}
|
|
2416
2470
|
|
|
2417
2471
|
aparte-option[disabled] {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
* High-performance AI conversation engine in Vanilla TypeScript
|
|
4
4
|
* Zero-dependency Web Components for LLM streaming
|
|
5
5
|
*
|
|
6
|
+
* ⚠️ This is the **browser** entry: it defines the custom elements and imports CSS
|
|
7
|
+
* at module scope, so it needs a DOM. **Node resolves `index.node.ts` instead**
|
|
8
|
+
* (via the `node` condition in package.json) — a DOM-free entry with the client,
|
|
9
|
+
* host, transports, the chat handler and every type. This file sits first in the
|
|
10
|
+
* exports map only because of the repo-local `@aparte-workspace/source` condition,
|
|
11
|
+
* which is why reading it can look like "this package can't run in Node".
|
|
12
|
+
* See the "Node / SSR" section of the README.
|
|
13
|
+
*
|
|
6
14
|
* @packageDocumentation
|
|
7
15
|
*/
|
|
8
16
|
|
|
@@ -36,6 +44,8 @@ export type { AparteMarkdownProvider, AparteStreamingMarkdownProvider, AparteStr
|
|
|
36
44
|
export { DEFAULT_ICON_FALLBACKS, DEFAULT_SKELETON_FALLBACKS, DEFAULT_LOCALE, defaultSanitizer, isSafeUrl } from './config/index.js';
|
|
37
45
|
export { AparteClient } from './client/aparte-client.js';
|
|
38
46
|
export { applyElementProps, DEFAULT_UI_EVENTS } from './interop/element-props.js';
|
|
47
|
+
export { filesToAttachments } from './utils/files-to-attachments.js';
|
|
48
|
+
export { isAwaitingReply } from './utils/is-awaiting-reply.js';
|
|
39
49
|
export type { AparteClientOptions, AparteToolApprovalResolver, AparteCompactionSelector } from './client/aparte-client.js';
|
|
40
50
|
export { createStreamAdapter, readableToAsyncIterable } from './client/stream-adapter.js';
|
|
41
51
|
export type { AparteStreamRunEvent, AparteStreamRunEmitter, StreamAdapterTarget, CreateStreamAdapterOptions, AparteStreamRunner, AparteStreamRunOptions } from './client/stream-adapter.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A, a, b, c, e, f, g, h, i, B, C, D, j, k, l, m, n, o, q, r, s, t, u, v, w, x,
|
|
1
|
+
import { P as resolveConfig, z as getSegmentRenderer, Q as runWithConfig, M as MessageRepository, E as isAwaitingReply, J as populateBubbleFromMessage, d as AparteConfig, p as buildElicitationPanel } from "./index-BBg6dsYx.js";
|
|
2
|
+
import { A, a, b, c, e, f, g, h, i, B, C, D, j, k, l, m, n, o, q, r, s, t, u, v, w, x, y, F, G, H, I, K, L, N, O, R } from "./index-BBg6dsYx.js";
|
|
3
3
|
class AparteOption extends HTMLElement {
|
|
4
4
|
static get observedAttributes() {
|
|
5
5
|
return ["value", "disabled", "selected", "data-status"];
|
|
@@ -73,6 +73,8 @@ if (!customElements.get("aparte-option")) {
|
|
|
73
73
|
customElements.define("aparte-option", AparteOption);
|
|
74
74
|
}
|
|
75
75
|
class AparteOptgroup extends HTMLElement {
|
|
76
|
+
/** Ids for the label span the group points `aria-labelledby` at. */
|
|
77
|
+
static _labelIdSeq = 0;
|
|
76
78
|
static get observedAttributes() {
|
|
77
79
|
return ["label", "collapsible", "collapsed", "loading"];
|
|
78
80
|
}
|
|
@@ -121,10 +123,11 @@ class AparteOptgroup extends HTMLElement {
|
|
|
121
123
|
if (!existingHeader) {
|
|
122
124
|
const header = document.createElement("div");
|
|
123
125
|
header.className = "aparte-optgroup-header";
|
|
124
|
-
header.setAttribute("aria-label", this.label);
|
|
125
126
|
const labelSpan = document.createElement("span");
|
|
126
127
|
labelSpan.className = "aparte-optgroup-label";
|
|
128
|
+
labelSpan.id = `aparte-optgroup-label-${++AparteOptgroup._labelIdSeq}`;
|
|
127
129
|
labelSpan.textContent = this.label;
|
|
130
|
+
this.setAttribute("aria-labelledby", labelSpan.id);
|
|
128
131
|
header.appendChild(labelSpan);
|
|
129
132
|
if (this.collapsible) {
|
|
130
133
|
const chevron = document.createElement("span");
|
|
@@ -179,6 +182,8 @@ if (!customElements.get("aparte-optgroup")) {
|
|
|
179
182
|
}
|
|
180
183
|
class AparteSelect extends HTMLElement {
|
|
181
184
|
static _optIdSeq = 0;
|
|
185
|
+
/** Fallback ids for the listbox `aria-controls` target when the host has no id. */
|
|
186
|
+
static _listboxSeq = 0;
|
|
182
187
|
_value = "";
|
|
183
188
|
_isOpen = false;
|
|
184
189
|
_activeIndex = -1;
|
|
@@ -271,17 +276,21 @@ class AparteSelect extends HTMLElement {
|
|
|
271
276
|
trigger.append(labelSpan, chevronSpan);
|
|
272
277
|
const dropdown = document.createElement("div");
|
|
273
278
|
dropdown.className = "aparte-select-dropdown";
|
|
274
|
-
dropdown.setAttribute("role", "listbox");
|
|
275
279
|
dropdown.hidden = !this._isOpen;
|
|
276
280
|
if (searchable) {
|
|
277
281
|
const searchInput = document.createElement("input");
|
|
278
282
|
searchInput.type = "text";
|
|
279
283
|
searchInput.className = "aparte-select-search";
|
|
280
284
|
searchInput.placeholder = "Search...";
|
|
285
|
+
searchInput.setAttribute("aria-label", "Search options");
|
|
281
286
|
dropdown.appendChild(searchInput);
|
|
282
287
|
}
|
|
283
288
|
const optionsContainer = document.createElement("div");
|
|
284
289
|
optionsContainer.className = "aparte-select-options";
|
|
290
|
+
optionsContainer.setAttribute("role", "listbox");
|
|
291
|
+
optionsContainer.setAttribute("aria-label", trigger.getAttribute("aria-label") ?? placeholder);
|
|
292
|
+
optionsContainer.id = this.id ? `${this.id}-listbox` : `aparte-listbox-${++AparteSelect._listboxSeq}`;
|
|
293
|
+
trigger.setAttribute("aria-controls", optionsContainer.id);
|
|
285
294
|
slottedChildren.forEach((child) => {
|
|
286
295
|
if (child.tagName === "APARTE-OPTION" || child.tagName === "APARTE-OPTGROUP") {
|
|
287
296
|
optionsContainer.appendChild(child);
|
|
@@ -589,9 +598,15 @@ if (!customElements.get("aparte-progress-spinner")) {
|
|
|
589
598
|
}
|
|
590
599
|
class AparteChat extends HTMLElement {
|
|
591
600
|
static get observedAttributes() {
|
|
592
|
-
return ["placeholder", "disabled", "center-empty"];
|
|
601
|
+
return ["placeholder", "disabled", "center-empty", "attachments"];
|
|
593
602
|
}
|
|
594
603
|
_observer = null;
|
|
604
|
+
/**
|
|
605
|
+
* True only for the composition THIS element injected. An author-provided
|
|
606
|
+
* composer (or a `framework-managed` host) is never edited by the attachments
|
|
607
|
+
* toggle below — those own their own markup.
|
|
608
|
+
*/
|
|
609
|
+
_ownsShell = false;
|
|
595
610
|
connectedCallback() {
|
|
596
611
|
this._render();
|
|
597
612
|
this._forwardAttr("placeholder");
|
|
@@ -608,6 +623,10 @@ class AparteChat extends HTMLElement {
|
|
|
608
623
|
this._syncEmptyWatch();
|
|
609
624
|
return;
|
|
610
625
|
}
|
|
626
|
+
if (name === "attachments") {
|
|
627
|
+
this._syncAttachments();
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
611
630
|
const composer = this.querySelector("aparte-composer");
|
|
612
631
|
if (!composer) return;
|
|
613
632
|
if (newValue !== null) composer.setAttribute(name, newValue);
|
|
@@ -626,17 +645,43 @@ class AparteChat extends HTMLElement {
|
|
|
626
645
|
if (this.querySelector("aparte-chat-viewport")) return;
|
|
627
646
|
const placeholder = this.getAttribute("placeholder");
|
|
628
647
|
const composerAttrs = (placeholder !== null ? ` placeholder="${this._escapeAttr(placeholder)}"` : "") + (this.hasAttribute("disabled") ? " disabled" : "");
|
|
648
|
+
const attachments = this.hasAttribute("attachments");
|
|
629
649
|
this.innerHTML = `
|
|
630
650
|
<aparte-chat-viewport></aparte-chat-viewport>
|
|
631
651
|
<aparte-composer${composerAttrs}>
|
|
632
652
|
<div class="aparte-composer-shell">
|
|
653
|
+
${attachments ? "<aparte-composer-attachments></aparte-composer-attachments>" : ""}
|
|
633
654
|
<div class="aparte-composer-row">
|
|
655
|
+
${attachments ? "<aparte-composer-add-attachment></aparte-composer-add-attachment>" : ""}
|
|
634
656
|
<aparte-composer-input></aparte-composer-input>
|
|
635
657
|
<aparte-composer-send></aparte-composer-send>
|
|
636
658
|
</div>
|
|
637
659
|
</div>
|
|
638
660
|
</aparte-composer>
|
|
639
661
|
`;
|
|
662
|
+
this._ownsShell = true;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Add/remove the two attachment primitives on the composition we injected, so
|
|
666
|
+
* toggling the attribute after mount works like the wrappers' reactive prop
|
|
667
|
+
* (there, a re-render does it). Author-provided markup is left alone.
|
|
668
|
+
*/
|
|
669
|
+
_syncAttachments() {
|
|
670
|
+
if (!this._ownsShell) return;
|
|
671
|
+
const composer = this.composer;
|
|
672
|
+
const shell = composer?.querySelector(".aparte-composer-shell");
|
|
673
|
+
const row = shell?.querySelector(".aparte-composer-row");
|
|
674
|
+
if (!composer || !shell || !row) return;
|
|
675
|
+
const strip = shell.querySelector("aparte-composer-attachments");
|
|
676
|
+
const picker = row.querySelector("aparte-composer-add-attachment");
|
|
677
|
+
if (this.hasAttribute("attachments")) {
|
|
678
|
+
if (!strip) shell.insertBefore(document.createElement("aparte-composer-attachments"), row);
|
|
679
|
+
if (!picker) row.insertBefore(document.createElement("aparte-composer-add-attachment"), row.firstChild);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
strip?.remove();
|
|
683
|
+
picker?.remove();
|
|
684
|
+
composer.clearAttachments();
|
|
640
685
|
}
|
|
641
686
|
/** Set an attribute on the inner composer only when the shell carries it. */
|
|
642
687
|
_forwardAttr(name) {
|
|
@@ -795,11 +840,13 @@ class AparteChatBubble extends HTMLElement {
|
|
|
795
840
|
setSegments(segments) {
|
|
796
841
|
this._segments = segments;
|
|
797
842
|
this._renderSegments();
|
|
843
|
+
this._updateWaiting();
|
|
798
844
|
}
|
|
799
845
|
/** Add a segment */
|
|
800
846
|
addSegment(segment) {
|
|
801
847
|
this._segments.push(segment);
|
|
802
848
|
this._appendSegmentEl(segment);
|
|
849
|
+
this._updateWaiting();
|
|
803
850
|
}
|
|
804
851
|
/** Update a specific segment */
|
|
805
852
|
updateSegment(segmentId, updates) {
|
|
@@ -830,6 +877,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
830
877
|
}
|
|
831
878
|
const el = this._segmentsEl?.querySelector(`[data-segment-id="${cssEscape(segmentId)}"]`);
|
|
832
879
|
el?.remove();
|
|
880
|
+
this._updateWaiting();
|
|
833
881
|
}
|
|
834
882
|
/** Set attachments (chips shown above message content, user role only) */
|
|
835
883
|
setAttachments(attachments) {
|
|
@@ -976,6 +1024,10 @@ class AparteChatBubble extends HTMLElement {
|
|
|
976
1024
|
<div class="aparte-message-content">
|
|
977
1025
|
<div class="aparte-segments"></div>
|
|
978
1026
|
<div class="aparte-content"></div>
|
|
1027
|
+
<div class="aparte-waiting" hidden>
|
|
1028
|
+
<span class="aparte-dots" aria-hidden="true"><span class="aparte-dot"></span><span class="aparte-dot"></span><span class="aparte-dot"></span></span>
|
|
1029
|
+
<span class="aparte-sr-only"></span>
|
|
1030
|
+
</div>
|
|
979
1031
|
</div>
|
|
980
1032
|
<div class="aparte-footer">
|
|
981
1033
|
<div class="aparte-branch-picker" hidden>
|
|
@@ -997,6 +1049,28 @@ class AparteChatBubble extends HTMLElement {
|
|
|
997
1049
|
this._setupBranchPickerListeners();
|
|
998
1050
|
this._updateActionBar();
|
|
999
1051
|
this._renderAvatar();
|
|
1052
|
+
if (this._streaming) this._updateStreaming(true);
|
|
1053
|
+
this._updateWaiting();
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Show the built-in waiting indicator while this bubble is in flight and has
|
|
1057
|
+
* nothing to show yet — the gap between "user sends" and the first token, which
|
|
1058
|
+
* used to be a bubble with a name and an empty body.
|
|
1059
|
+
*
|
|
1060
|
+
* The dots are CSS (no per-token work, themable, honours reduced-motion); the
|
|
1061
|
+
* accessible name is `locale.typing`, next to the `aria-busy` the streaming state
|
|
1062
|
+
* already sets. A custom bubble shell without the region simply has no indicator
|
|
1063
|
+
* (same null-guarded degradation as the other region hooks).
|
|
1064
|
+
*/
|
|
1065
|
+
_updateWaiting() {
|
|
1066
|
+
const el = this.querySelector(".aparte-waiting");
|
|
1067
|
+
if (!el) return;
|
|
1068
|
+
const waiting = this._streaming && this._role !== "user" && this._segments.length === 0 && !this._content.trim();
|
|
1069
|
+
el.hidden = !waiting;
|
|
1070
|
+
if (!waiting) return;
|
|
1071
|
+
const label = this._cfg.getLocale().typing;
|
|
1072
|
+
const sr = el.querySelector(".aparte-sr-only");
|
|
1073
|
+
if (sr && sr.textContent !== label) sr.textContent = label;
|
|
1000
1074
|
}
|
|
1001
1075
|
/**
|
|
1002
1076
|
* Hand the avatar host element off to the registered AvatarProvider, if
|
|
@@ -1047,10 +1121,12 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1047
1121
|
if (!this._contentEl) return;
|
|
1048
1122
|
if (this._segments.length > 0) {
|
|
1049
1123
|
this._contentEl.style.display = "none";
|
|
1124
|
+
this._updateWaiting();
|
|
1050
1125
|
return;
|
|
1051
1126
|
}
|
|
1052
1127
|
this._contentEl.style.display = "";
|
|
1053
1128
|
this._contentEl.innerHTML = this._cfg.renderMarkdown(this._content);
|
|
1129
|
+
this._updateWaiting();
|
|
1054
1130
|
if (!this._streaming) this._highlightContentCode();
|
|
1055
1131
|
}
|
|
1056
1132
|
/**
|
|
@@ -1505,6 +1581,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1505
1581
|
message.classList.remove("aparte-message-streaming");
|
|
1506
1582
|
}
|
|
1507
1583
|
}
|
|
1584
|
+
this._updateWaiting();
|
|
1508
1585
|
if (wasStreaming && !streaming) this._highlightContentCode();
|
|
1509
1586
|
}
|
|
1510
1587
|
}
|
|
@@ -1903,7 +1980,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
1903
1980
|
bubble.setAttribute("role", message.role);
|
|
1904
1981
|
if (message.timestamp) bubble.setAttribute("timestamp", String(message.timestamp));
|
|
1905
1982
|
if (message.content) bubble.setAttribute("content", message.content);
|
|
1906
|
-
if (message
|
|
1983
|
+
if (isAwaitingReply(message)) {
|
|
1907
1984
|
bubble.setAttribute("streaming", "");
|
|
1908
1985
|
}
|
|
1909
1986
|
if (this._bottomSpacer && this._bottomSpacer.parentNode === wrapper) {
|
|
@@ -1911,6 +1988,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
1911
1988
|
} else {
|
|
1912
1989
|
wrapper.appendChild(bubble);
|
|
1913
1990
|
}
|
|
1991
|
+
populateBubbleFromMessage(bubble, message);
|
|
1914
1992
|
}
|
|
1915
1993
|
}
|
|
1916
1994
|
this._pruneRenderedBubbles();
|
|
@@ -1991,7 +2069,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
1991
2069
|
if (currentIdx === -1) return;
|
|
1992
2070
|
const targetIdx = direction === "prev" ? currentIdx - 1 : currentIdx + 1;
|
|
1993
2071
|
if (targetIdx < 0 || targetIdx >= siblings.length) return;
|
|
1994
|
-
this._isAutoScrollEnabled =
|
|
2072
|
+
this._isAutoScrollEnabled = this._isAtBottom();
|
|
1995
2073
|
this._updateScrollButton();
|
|
1996
2074
|
this._repo.switchToBranch(siblings[targetIdx]);
|
|
1997
2075
|
this._reRenderActivePath();
|
|
@@ -2181,11 +2259,14 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2181
2259
|
case "setUsage":
|
|
2182
2260
|
bubble.setUsage?.(payload);
|
|
2183
2261
|
break;
|
|
2184
|
-
case "update":
|
|
2185
|
-
|
|
2262
|
+
case "update": {
|
|
2263
|
+
const updates = payload;
|
|
2264
|
+
const renderable = ["status", "segments", "content", "attachments", "usage"];
|
|
2265
|
+
if (renderable.some((key) => key in updates)) {
|
|
2186
2266
|
bubble.updateMessage?.(payload);
|
|
2187
2267
|
}
|
|
2188
2268
|
break;
|
|
2269
|
+
}
|
|
2189
2270
|
case "complete":
|
|
2190
2271
|
bubble.updateMessage?.(payload);
|
|
2191
2272
|
break;
|
|
@@ -2222,7 +2303,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2222
2303
|
bubble.setAttribute("message-id", message.id);
|
|
2223
2304
|
bubble.setAttribute("role", message.role);
|
|
2224
2305
|
if (message.timestamp) bubble.setAttribute("timestamp", String(message.timestamp));
|
|
2225
|
-
if (message
|
|
2306
|
+
if (isAwaitingReply(message)) {
|
|
2226
2307
|
bubble.setAttribute("streaming", "");
|
|
2227
2308
|
}
|
|
2228
2309
|
wrapper.appendChild(bubble);
|
|
@@ -2230,7 +2311,6 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2230
2311
|
}
|
|
2231
2312
|
this._dispatchPathChanged(activeMessages, siblingsInfo);
|
|
2232
2313
|
this._recalculateSpacer();
|
|
2233
|
-
requestAnimationFrame(() => this._handleScroll());
|
|
2234
2314
|
}
|
|
2235
2315
|
_dispatchPathChanged(messages, siblings) {
|
|
2236
2316
|
const detail = { messages, siblings };
|
|
@@ -2400,11 +2480,15 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2400
2480
|
});
|
|
2401
2481
|
}
|
|
2402
2482
|
}
|
|
2483
|
+
/** Is the scroll surface within `_scrollThreshold` of its bottom, right now? */
|
|
2484
|
+
_isAtBottom() {
|
|
2485
|
+
if (!this._container) return true;
|
|
2486
|
+
const { scrollTop, scrollHeight, clientHeight } = this._container;
|
|
2487
|
+
return scrollHeight - scrollTop - clientHeight <= this._scrollThreshold;
|
|
2488
|
+
}
|
|
2403
2489
|
_handleScroll() {
|
|
2404
2490
|
if (!this._container) return;
|
|
2405
|
-
|
|
2406
|
-
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
2407
|
-
this._isAutoScrollEnabled = distanceFromBottom <= this._scrollThreshold;
|
|
2491
|
+
this._isAutoScrollEnabled = this._isAtBottom();
|
|
2408
2492
|
this._updateScrollButton();
|
|
2409
2493
|
}
|
|
2410
2494
|
_scrollToBottom() {
|
|
@@ -2423,11 +2507,20 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2423
2507
|
return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2424
2508
|
}
|
|
2425
2509
|
/**
|
|
2426
|
-
* Show/hide the scroll-to-bottom button
|
|
2427
|
-
*
|
|
2510
|
+
* Show/hide the scroll-to-bottom button from the **current geometry**, not from
|
|
2511
|
+
* `_isAutoScrollEnabled`.
|
|
2512
|
+
*
|
|
2513
|
+
* The two answer different questions: the flag is intent ("should new content
|
|
2514
|
+
* pull the view down"), the button is a fact ("is there anything below the
|
|
2515
|
+
* fold"). Mirroring the flag made the button lie whenever the two diverged —
|
|
2516
|
+
* `navigateBranch` deliberately disarms auto-follow, so swapping a branch while
|
|
2517
|
+
* already at the bottom of a scrollable transcript left the button offering to
|
|
2518
|
+
* scroll nowhere (reported from bonaparte, React). Re-derived on scroll, on the
|
|
2519
|
+
* post-mutation frame and after a path swap, so it converges to the truth
|
|
2520
|
+
* whatever a framework's render timing does in between.
|
|
2428
2521
|
*/
|
|
2429
2522
|
_updateScrollButton() {
|
|
2430
|
-
this._scrollBtn?.classList.toggle("aparte-scroll-btn--hidden", this.
|
|
2523
|
+
this._scrollBtn?.classList.toggle("aparte-scroll-btn--hidden", this._isAtBottom());
|
|
2431
2524
|
}
|
|
2432
2525
|
/**
|
|
2433
2526
|
* Recalculate the bottom spacer height so the last user message is always
|
|
@@ -2486,6 +2579,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2486
2579
|
this._spacerRafId = requestAnimationFrame(() => {
|
|
2487
2580
|
this._spacerRafId = null;
|
|
2488
2581
|
this._recalculateSpacer();
|
|
2582
|
+
this._updateScrollButton();
|
|
2489
2583
|
});
|
|
2490
2584
|
}
|
|
2491
2585
|
/**
|
|
@@ -3746,19 +3840,21 @@ export {
|
|
|
3746
3840
|
v as defaultSanitizer,
|
|
3747
3841
|
w as deriveArtifactKind,
|
|
3748
3842
|
x as detachConfig,
|
|
3843
|
+
y as filesToAttachments,
|
|
3749
3844
|
getSegmentRenderer,
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3845
|
+
isAwaitingReply,
|
|
3846
|
+
F as isFormatAdapter,
|
|
3847
|
+
G as isSafeUrl,
|
|
3848
|
+
H as parseAparteEventStream,
|
|
3849
|
+
I as parseMarkdownToSegments,
|
|
3754
3850
|
populateBubbleFromMessage,
|
|
3755
|
-
|
|
3851
|
+
K as readableToAsyncIterable,
|
|
3756
3852
|
registerAllComponents,
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3853
|
+
L as registerDefaultRenderers,
|
|
3854
|
+
N as registerSegmentRenderer,
|
|
3855
|
+
O as requestUserInput,
|
|
3760
3856
|
resolveConfig,
|
|
3761
3857
|
runWithConfig,
|
|
3762
|
-
|
|
3858
|
+
R as unregisterSegmentRenderer
|
|
3763
3859
|
};
|
|
3764
3860
|
//# sourceMappingURL=index.js.map
|