@aparte/core 0.2.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 +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 +16 -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 +3658 -3385
- package/dist/host/aparte-chat-host.d.ts +47 -1
- package/dist/host/aparte-chat-host.d.ts.map +1 -1
- package/dist/{index-D89k-n2Q.js → index-BBg6dsYx.js} +193 -43
- 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 +123 -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 +27 -0
- package/dist/types/imperative-api.d.ts.map +1 -1
- package/dist/types/model-provider.d.ts +9 -3
- package/dist/types/model-provider.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-D89k-n2Q.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;
|
|
@@ -261,6 +266,7 @@ class AparteSelect extends HTMLElement {
|
|
|
261
266
|
trigger.setAttribute("role", "combobox");
|
|
262
267
|
trigger.setAttribute("aria-haspopup", "listbox");
|
|
263
268
|
trigger.setAttribute("aria-expanded", "false");
|
|
269
|
+
trigger.setAttribute("aria-label", this.getAttribute("aria-label") || placeholder);
|
|
264
270
|
const labelSpan = document.createElement("span");
|
|
265
271
|
labelSpan.className = "aparte-select-label";
|
|
266
272
|
labelSpan.textContent = placeholder;
|
|
@@ -270,17 +276,21 @@ class AparteSelect extends HTMLElement {
|
|
|
270
276
|
trigger.append(labelSpan, chevronSpan);
|
|
271
277
|
const dropdown = document.createElement("div");
|
|
272
278
|
dropdown.className = "aparte-select-dropdown";
|
|
273
|
-
dropdown.setAttribute("role", "listbox");
|
|
274
279
|
dropdown.hidden = !this._isOpen;
|
|
275
280
|
if (searchable) {
|
|
276
281
|
const searchInput = document.createElement("input");
|
|
277
282
|
searchInput.type = "text";
|
|
278
283
|
searchInput.className = "aparte-select-search";
|
|
279
284
|
searchInput.placeholder = "Search...";
|
|
285
|
+
searchInput.setAttribute("aria-label", "Search options");
|
|
280
286
|
dropdown.appendChild(searchInput);
|
|
281
287
|
}
|
|
282
288
|
const optionsContainer = document.createElement("div");
|
|
283
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);
|
|
284
294
|
slottedChildren.forEach((child) => {
|
|
285
295
|
if (child.tagName === "APARTE-OPTION" || child.tagName === "APARTE-OPTGROUP") {
|
|
286
296
|
optionsContainer.appendChild(child);
|
|
@@ -588,9 +598,15 @@ if (!customElements.get("aparte-progress-spinner")) {
|
|
|
588
598
|
}
|
|
589
599
|
class AparteChat extends HTMLElement {
|
|
590
600
|
static get observedAttributes() {
|
|
591
|
-
return ["placeholder", "disabled", "center-empty"];
|
|
601
|
+
return ["placeholder", "disabled", "center-empty", "attachments"];
|
|
592
602
|
}
|
|
593
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;
|
|
594
610
|
connectedCallback() {
|
|
595
611
|
this._render();
|
|
596
612
|
this._forwardAttr("placeholder");
|
|
@@ -607,6 +623,10 @@ class AparteChat extends HTMLElement {
|
|
|
607
623
|
this._syncEmptyWatch();
|
|
608
624
|
return;
|
|
609
625
|
}
|
|
626
|
+
if (name === "attachments") {
|
|
627
|
+
this._syncAttachments();
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
610
630
|
const composer = this.querySelector("aparte-composer");
|
|
611
631
|
if (!composer) return;
|
|
612
632
|
if (newValue !== null) composer.setAttribute(name, newValue);
|
|
@@ -625,17 +645,43 @@ class AparteChat extends HTMLElement {
|
|
|
625
645
|
if (this.querySelector("aparte-chat-viewport")) return;
|
|
626
646
|
const placeholder = this.getAttribute("placeholder");
|
|
627
647
|
const composerAttrs = (placeholder !== null ? ` placeholder="${this._escapeAttr(placeholder)}"` : "") + (this.hasAttribute("disabled") ? " disabled" : "");
|
|
648
|
+
const attachments = this.hasAttribute("attachments");
|
|
628
649
|
this.innerHTML = `
|
|
629
650
|
<aparte-chat-viewport></aparte-chat-viewport>
|
|
630
651
|
<aparte-composer${composerAttrs}>
|
|
631
652
|
<div class="aparte-composer-shell">
|
|
653
|
+
${attachments ? "<aparte-composer-attachments></aparte-composer-attachments>" : ""}
|
|
632
654
|
<div class="aparte-composer-row">
|
|
655
|
+
${attachments ? "<aparte-composer-add-attachment></aparte-composer-add-attachment>" : ""}
|
|
633
656
|
<aparte-composer-input></aparte-composer-input>
|
|
634
657
|
<aparte-composer-send></aparte-composer-send>
|
|
635
658
|
</div>
|
|
636
659
|
</div>
|
|
637
660
|
</aparte-composer>
|
|
638
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();
|
|
639
685
|
}
|
|
640
686
|
/** Set an attribute on the inner composer only when the shell carries it. */
|
|
641
687
|
_forwardAttr(name) {
|
|
@@ -794,11 +840,13 @@ class AparteChatBubble extends HTMLElement {
|
|
|
794
840
|
setSegments(segments) {
|
|
795
841
|
this._segments = segments;
|
|
796
842
|
this._renderSegments();
|
|
843
|
+
this._updateWaiting();
|
|
797
844
|
}
|
|
798
845
|
/** Add a segment */
|
|
799
846
|
addSegment(segment) {
|
|
800
847
|
this._segments.push(segment);
|
|
801
848
|
this._appendSegmentEl(segment);
|
|
849
|
+
this._updateWaiting();
|
|
802
850
|
}
|
|
803
851
|
/** Update a specific segment */
|
|
804
852
|
updateSegment(segmentId, updates) {
|
|
@@ -829,6 +877,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
829
877
|
}
|
|
830
878
|
const el = this._segmentsEl?.querySelector(`[data-segment-id="${cssEscape(segmentId)}"]`);
|
|
831
879
|
el?.remove();
|
|
880
|
+
this._updateWaiting();
|
|
832
881
|
}
|
|
833
882
|
/** Set attachments (chips shown above message content, user role only) */
|
|
834
883
|
setAttachments(attachments) {
|
|
@@ -975,6 +1024,10 @@ class AparteChatBubble extends HTMLElement {
|
|
|
975
1024
|
<div class="aparte-message-content">
|
|
976
1025
|
<div class="aparte-segments"></div>
|
|
977
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>
|
|
978
1031
|
</div>
|
|
979
1032
|
<div class="aparte-footer">
|
|
980
1033
|
<div class="aparte-branch-picker" hidden>
|
|
@@ -996,6 +1049,28 @@ class AparteChatBubble extends HTMLElement {
|
|
|
996
1049
|
this._setupBranchPickerListeners();
|
|
997
1050
|
this._updateActionBar();
|
|
998
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;
|
|
999
1074
|
}
|
|
1000
1075
|
/**
|
|
1001
1076
|
* Hand the avatar host element off to the registered AvatarProvider, if
|
|
@@ -1046,10 +1121,12 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1046
1121
|
if (!this._contentEl) return;
|
|
1047
1122
|
if (this._segments.length > 0) {
|
|
1048
1123
|
this._contentEl.style.display = "none";
|
|
1124
|
+
this._updateWaiting();
|
|
1049
1125
|
return;
|
|
1050
1126
|
}
|
|
1051
1127
|
this._contentEl.style.display = "";
|
|
1052
1128
|
this._contentEl.innerHTML = this._cfg.renderMarkdown(this._content);
|
|
1129
|
+
this._updateWaiting();
|
|
1053
1130
|
if (!this._streaming) this._highlightContentCode();
|
|
1054
1131
|
}
|
|
1055
1132
|
/**
|
|
@@ -1504,6 +1581,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1504
1581
|
message.classList.remove("aparte-message-streaming");
|
|
1505
1582
|
}
|
|
1506
1583
|
}
|
|
1584
|
+
this._updateWaiting();
|
|
1507
1585
|
if (wasStreaming && !streaming) this._highlightContentCode();
|
|
1508
1586
|
}
|
|
1509
1587
|
}
|
|
@@ -1902,7 +1980,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
1902
1980
|
bubble.setAttribute("role", message.role);
|
|
1903
1981
|
if (message.timestamp) bubble.setAttribute("timestamp", String(message.timestamp));
|
|
1904
1982
|
if (message.content) bubble.setAttribute("content", message.content);
|
|
1905
|
-
if (message
|
|
1983
|
+
if (isAwaitingReply(message)) {
|
|
1906
1984
|
bubble.setAttribute("streaming", "");
|
|
1907
1985
|
}
|
|
1908
1986
|
if (this._bottomSpacer && this._bottomSpacer.parentNode === wrapper) {
|
|
@@ -1910,6 +1988,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
1910
1988
|
} else {
|
|
1911
1989
|
wrapper.appendChild(bubble);
|
|
1912
1990
|
}
|
|
1991
|
+
populateBubbleFromMessage(bubble, message);
|
|
1913
1992
|
}
|
|
1914
1993
|
}
|
|
1915
1994
|
this._pruneRenderedBubbles();
|
|
@@ -1990,7 +2069,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
1990
2069
|
if (currentIdx === -1) return;
|
|
1991
2070
|
const targetIdx = direction === "prev" ? currentIdx - 1 : currentIdx + 1;
|
|
1992
2071
|
if (targetIdx < 0 || targetIdx >= siblings.length) return;
|
|
1993
|
-
this._isAutoScrollEnabled =
|
|
2072
|
+
this._isAutoScrollEnabled = this._isAtBottom();
|
|
1994
2073
|
this._updateScrollButton();
|
|
1995
2074
|
this._repo.switchToBranch(siblings[targetIdx]);
|
|
1996
2075
|
this._reRenderActivePath();
|
|
@@ -2180,11 +2259,14 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2180
2259
|
case "setUsage":
|
|
2181
2260
|
bubble.setUsage?.(payload);
|
|
2182
2261
|
break;
|
|
2183
|
-
case "update":
|
|
2184
|
-
|
|
2262
|
+
case "update": {
|
|
2263
|
+
const updates = payload;
|
|
2264
|
+
const renderable = ["status", "segments", "content", "attachments", "usage"];
|
|
2265
|
+
if (renderable.some((key) => key in updates)) {
|
|
2185
2266
|
bubble.updateMessage?.(payload);
|
|
2186
2267
|
}
|
|
2187
2268
|
break;
|
|
2269
|
+
}
|
|
2188
2270
|
case "complete":
|
|
2189
2271
|
bubble.updateMessage?.(payload);
|
|
2190
2272
|
break;
|
|
@@ -2221,7 +2303,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2221
2303
|
bubble.setAttribute("message-id", message.id);
|
|
2222
2304
|
bubble.setAttribute("role", message.role);
|
|
2223
2305
|
if (message.timestamp) bubble.setAttribute("timestamp", String(message.timestamp));
|
|
2224
|
-
if (message
|
|
2306
|
+
if (isAwaitingReply(message)) {
|
|
2225
2307
|
bubble.setAttribute("streaming", "");
|
|
2226
2308
|
}
|
|
2227
2309
|
wrapper.appendChild(bubble);
|
|
@@ -2229,7 +2311,6 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2229
2311
|
}
|
|
2230
2312
|
this._dispatchPathChanged(activeMessages, siblingsInfo);
|
|
2231
2313
|
this._recalculateSpacer();
|
|
2232
|
-
requestAnimationFrame(() => this._handleScroll());
|
|
2233
2314
|
}
|
|
2234
2315
|
_dispatchPathChanged(messages, siblings) {
|
|
2235
2316
|
const detail = { messages, siblings };
|
|
@@ -2399,11 +2480,15 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2399
2480
|
});
|
|
2400
2481
|
}
|
|
2401
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
|
+
}
|
|
2402
2489
|
_handleScroll() {
|
|
2403
2490
|
if (!this._container) return;
|
|
2404
|
-
|
|
2405
|
-
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
2406
|
-
this._isAutoScrollEnabled = distanceFromBottom <= this._scrollThreshold;
|
|
2491
|
+
this._isAutoScrollEnabled = this._isAtBottom();
|
|
2407
2492
|
this._updateScrollButton();
|
|
2408
2493
|
}
|
|
2409
2494
|
_scrollToBottom() {
|
|
@@ -2422,11 +2507,20 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2422
2507
|
return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2423
2508
|
}
|
|
2424
2509
|
/**
|
|
2425
|
-
* Show/hide the scroll-to-bottom button
|
|
2426
|
-
*
|
|
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.
|
|
2427
2521
|
*/
|
|
2428
2522
|
_updateScrollButton() {
|
|
2429
|
-
this._scrollBtn?.classList.toggle("aparte-scroll-btn--hidden", this.
|
|
2523
|
+
this._scrollBtn?.classList.toggle("aparte-scroll-btn--hidden", this._isAtBottom());
|
|
2430
2524
|
}
|
|
2431
2525
|
/**
|
|
2432
2526
|
* Recalculate the bottom spacer height so the last user message is always
|
|
@@ -2485,6 +2579,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2485
2579
|
this._spacerRafId = requestAnimationFrame(() => {
|
|
2486
2580
|
this._spacerRafId = null;
|
|
2487
2581
|
this._recalculateSpacer();
|
|
2582
|
+
this._updateScrollButton();
|
|
2488
2583
|
});
|
|
2489
2584
|
}
|
|
2490
2585
|
/**
|
|
@@ -3745,19 +3840,21 @@ export {
|
|
|
3745
3840
|
v as defaultSanitizer,
|
|
3746
3841
|
w as deriveArtifactKind,
|
|
3747
3842
|
x as detachConfig,
|
|
3843
|
+
y as filesToAttachments,
|
|
3748
3844
|
getSegmentRenderer,
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3845
|
+
isAwaitingReply,
|
|
3846
|
+
F as isFormatAdapter,
|
|
3847
|
+
G as isSafeUrl,
|
|
3848
|
+
H as parseAparteEventStream,
|
|
3849
|
+
I as parseMarkdownToSegments,
|
|
3753
3850
|
populateBubbleFromMessage,
|
|
3754
|
-
|
|
3851
|
+
K as readableToAsyncIterable,
|
|
3755
3852
|
registerAllComponents,
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3853
|
+
L as registerDefaultRenderers,
|
|
3854
|
+
N as registerSegmentRenderer,
|
|
3855
|
+
O as requestUserInput,
|
|
3759
3856
|
resolveConfig,
|
|
3760
3857
|
runWithConfig,
|
|
3761
|
-
|
|
3858
|
+
R as unregisterSegmentRenderer
|
|
3762
3859
|
};
|
|
3763
3860
|
//# sourceMappingURL=index.js.map
|