@aparte/core 0.8.0 → 0.10.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.map +1 -1
- package/dist/client/stream-adapter.d.ts.map +1 -1
- package/dist/client/xml-artifact-feed.d.ts.map +1 -1
- package/dist/components/bubble/aparte-chat-bubble.d.ts +24 -3
- package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
- package/dist/components/bubble/bubble-sync.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 +11 -0
- package/dist/components/composer/aparte-composer-add-attachment.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-cancel.d.ts +8 -0
- 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 +32 -0
- package/dist/components/composer/aparte-composer-send.d.ts.map +1 -1
- package/dist/components/elicitation/aparte-elicitation.d.ts +10 -0
- package/dist/components/elicitation/aparte-elicitation.d.ts.map +1 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts +13 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
- package/dist/config/aparte-config.d.ts +34 -1
- package/dist/config/aparte-config.d.ts.map +1 -1
- package/dist/config/config-subscribe.d.ts +4 -0
- package/dist/config/config-subscribe.d.ts.map +1 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/locale.d.ts +98 -0
- package/dist/config/locale.d.ts.map +1 -1
- package/dist/config/sanitize.d.ts.map +1 -1
- package/dist/custom-elements.json +7590 -6959
- package/dist/elicitation/panel.d.ts +13 -0
- package/dist/elicitation/panel.d.ts.map +1 -1
- package/dist/host/aparte-chat-host.d.ts +3 -1
- package/dist/host/aparte-chat-host.d.ts.map +1 -1
- package/dist/{index-BieFbKeW.js → index-BnKBciDG.js} +642 -248
- package/dist/index-BnKBciDG.js.map +1 -0
- package/dist/index.css +147 -310
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +281 -92
- package/dist/index.js.map +1 -1
- package/dist/index.node.d.ts +3 -1
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +60 -55
- package/dist/index.node.js.map +1 -1
- package/dist/parsers/aparte-stream-parser.d.ts +25 -0
- package/dist/parsers/aparte-stream-parser.d.ts.map +1 -1
- package/dist/renderers/highlight-stream.d.ts +17 -0
- package/dist/renderers/highlight-stream.d.ts.map +1 -0
- package/dist/renderers/segment-renderers.d.ts.map +1 -1
- package/dist/renderers/segments/artifact/binary-file.d.ts.map +1 -1
- package/dist/renderers/segments/artifact/card.d.ts.map +1 -1
- package/dist/renderers/segments/artifact/shared.d.ts +19 -2
- package/dist/renderers/segments/artifact/shared.d.ts.map +1 -1
- package/dist/renderers/segments/code.d.ts.map +1 -1
- package/dist/renderers/segments/error.d.ts.map +1 -1
- package/dist/renderers/segments/pipeline-waiting.d.ts.map +1 -1
- package/dist/renderers/segments/thinking.d.ts.map +1 -1
- package/dist/renderers/segments/tool-call.d.ts.map +1 -1
- package/dist/types/event-map.d.ts +1 -2
- package/dist/types/event-map.d.ts.map +1 -1
- package/dist/types/events.d.ts +0 -16
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/models.d.ts +1 -6
- package/dist/types/models.d.ts.map +1 -1
- package/dist/types/segments.d.ts +103 -51
- package/dist/types/segments.d.ts.map +1 -1
- package/dist/utils/segments.d.ts +244 -0
- package/dist/utils/segments.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/index-BieFbKeW.js.map +0 -1
- package/dist/renderers/segments/file-tree.d.ts +0 -3
- package/dist/renderers/segments/file-tree.d.ts.map +0 -1
- package/dist/renderers/segments/terminal.d.ts +0 -3
- package/dist/renderers/segments/terminal.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p,
|
|
1
|
+
import { I as escapeAttr, D as cssEscape, $ as runWithConfig, Z as resolveConfig, a5 as mergeSegmentUpdate, J as escapeHtml, M as getSegmentRenderer, N as installDefaultRenderersOnce, r as AparteMessageRepository, a6 as stampSegmentActivity, a7 as stampSegmentOnInsert, a8 as stampSegmentOnUpdate, a9 as renumberSegments, aa as openSegmentIds, ab as isTerminalStatus, ac as adoptMessageSegments, O as isAwaitingReply, U as populateBubbleFromMessage, a4 as uuid, _ as revokeAttachmentUrls, a2 as subscribeConfigChange, w as buildElicitationPanel } from "./index-BnKBciDG.js";
|
|
2
|
+
import { A, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, u, v, x, y, z, B, C, E, F, G, H, K, L, P, Q, R, S, T, V, W, X, Y, a0, a1, a3 } from "./index-BnKBciDG.js";
|
|
3
3
|
class AparteOption extends HTMLElement {
|
|
4
4
|
static get observedAttributes() {
|
|
5
5
|
return ["value", "disabled", "selected", "data-status"];
|
|
@@ -804,7 +804,38 @@ class AparteChatBubble extends HTMLElement {
|
|
|
804
804
|
this._updateName();
|
|
805
805
|
this._updateLocalizedLabels();
|
|
806
806
|
this._updateWaiting();
|
|
807
|
+
this._renderAvatar();
|
|
808
|
+
this._relabelSegments();
|
|
809
|
+
this._updateTimestamp(this.getAttribute("timestamp"));
|
|
807
810
|
};
|
|
811
|
+
/**
|
|
812
|
+
* Ask every rendered segment to re-read its config-derived text.
|
|
813
|
+
*
|
|
814
|
+
* Not `_renderSegments()`, which wipes the container and rebuilds: that destroys a
|
|
815
|
+
* mounted artifact preview, reverts a reasoning block the reader expanded by
|
|
816
|
+
* clicking `<summary>` (the DOM's real state is never written back to `collapsed`),
|
|
817
|
+
* resets scroll inside long terminal panes, drops focus from an Approve/Reject
|
|
818
|
+
* gate, and throws away the incremental Markdown parser's buffered lookahead
|
|
819
|
+
* mid-stream — for a change that added no content. It also fires container-wide
|
|
820
|
+
* childList mutations, which is what the viewport's observer reads as "scroll to
|
|
821
|
+
* the bottom".
|
|
822
|
+
*
|
|
823
|
+
* `relabel` is the narrow alternative, bound by the same no-child-node rule as
|
|
824
|
+
* `update()`. A renderer that has no config-derived text does not implement it,
|
|
825
|
+
* and this loop simply skips it.
|
|
826
|
+
*/
|
|
827
|
+
_relabelSegments() {
|
|
828
|
+
if (!this._segmentsEl) return;
|
|
829
|
+
for (const segment of this._segments) {
|
|
830
|
+
const renderer = resolveSegmentRenderer(segment.type, this._cfg);
|
|
831
|
+
if (!renderer?.relabel) continue;
|
|
832
|
+
const el = this._segmentsEl.querySelector(
|
|
833
|
+
`:scope > [data-segment-id="${cssEscape(segment.id)}"]`
|
|
834
|
+
);
|
|
835
|
+
if (!el) continue;
|
|
836
|
+
runWithConfig(this._cfg, () => renderer.relabel(el, segment));
|
|
837
|
+
}
|
|
838
|
+
}
|
|
808
839
|
/**
|
|
809
840
|
* Re-apply the locale strings written straight into the markup by `_render()` —
|
|
810
841
|
* the accessible names a screen reader reads, which nothing else refreshes.
|
|
@@ -892,7 +923,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
892
923
|
}
|
|
893
924
|
/** Set segments for rich content */
|
|
894
925
|
setSegments(segments) {
|
|
895
|
-
this._segments = segments;
|
|
926
|
+
this._segments = [...segments];
|
|
896
927
|
this._renderSegments();
|
|
897
928
|
this._updateWaiting();
|
|
898
929
|
}
|
|
@@ -906,7 +937,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
906
937
|
updateSegment(segmentId, updates) {
|
|
907
938
|
const index = this._segments.findIndex((s2) => s2.id === segmentId);
|
|
908
939
|
if (index !== -1) {
|
|
909
|
-
const updated =
|
|
940
|
+
const updated = mergeSegmentUpdate(this._segments[index], updates);
|
|
910
941
|
this._segments[index] = updated;
|
|
911
942
|
this._applySegmentUpdate(segmentId, updated, updates);
|
|
912
943
|
}
|
|
@@ -1135,9 +1166,12 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1135
1166
|
* (same null-guarded degradation as the other region hooks).
|
|
1136
1167
|
*/
|
|
1137
1168
|
_updateWaiting() {
|
|
1169
|
+
const empty = this._segments.length === 0 && !this._content.trim();
|
|
1170
|
+
const waiting = this._streaming && this._role !== "user" && empty;
|
|
1171
|
+
const box = this.querySelector(".aparte-message-content");
|
|
1172
|
+
if (box) box.hidden = empty && !waiting;
|
|
1138
1173
|
const el = this.querySelector(".aparte-waiting");
|
|
1139
1174
|
if (!el) return;
|
|
1140
|
-
const waiting = this._streaming && this._role !== "user" && this._segments.length === 0 && !this._content.trim();
|
|
1141
1175
|
el.hidden = !waiting;
|
|
1142
1176
|
if (!waiting) return;
|
|
1143
1177
|
const label = this._cfg.getLocale().typing;
|
|
@@ -1145,9 +1179,13 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1145
1179
|
if (sr && sr.textContent !== label) sr.textContent = label;
|
|
1146
1180
|
}
|
|
1147
1181
|
/**
|
|
1148
|
-
* Hand the avatar host element off to the registered AvatarProvider, if
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1182
|
+
* Hand the avatar host element off to the registered AvatarProvider, if any.
|
|
1183
|
+
*
|
|
1184
|
+
* With no provider the slot is left exactly as the shell rendered it — which for
|
|
1185
|
+
* the default shell means EMPTY, and hidden by `.aparte-avatar:empty`. This used
|
|
1186
|
+
* to claim it "falls back to the default initial rendered by `_render()`"; there
|
|
1187
|
+
* is no such initial, and believing there was is what made `_updateRole` write
|
|
1188
|
+
* one.
|
|
1151
1189
|
*/
|
|
1152
1190
|
_renderAvatar() {
|
|
1153
1191
|
const avatar = this.querySelector(".aparte-avatar");
|
|
@@ -1175,7 +1213,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1175
1213
|
}
|
|
1176
1214
|
if (avatar) {
|
|
1177
1215
|
avatar.setAttribute("data-role", this._role);
|
|
1178
|
-
avatar.textContent = this._getAvatarInitial();
|
|
1216
|
+
if (avatar.textContent) avatar.textContent = this._getAvatarInitial();
|
|
1179
1217
|
}
|
|
1180
1218
|
if (nameEl) {
|
|
1181
1219
|
nameEl.textContent = this._getDisplayName();
|
|
@@ -1186,7 +1224,9 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1186
1224
|
_updateName() {
|
|
1187
1225
|
const avatar = this.querySelector(".aparte-avatar");
|
|
1188
1226
|
const nameEl = this.querySelector(".aparte-name");
|
|
1189
|
-
if (avatar && !this._cfg.getAvatarProvider())
|
|
1227
|
+
if (avatar && avatar.textContent && !this._cfg.getAvatarProvider()) {
|
|
1228
|
+
avatar.textContent = this._getAvatarInitial();
|
|
1229
|
+
}
|
|
1190
1230
|
if (nameEl) nameEl.textContent = this._getDisplayName();
|
|
1191
1231
|
}
|
|
1192
1232
|
_updateContent() {
|
|
@@ -1271,7 +1311,7 @@ class AparteChatBubble extends HTMLElement {
|
|
|
1271
1311
|
if (!timestampEl || !value) return;
|
|
1272
1312
|
try {
|
|
1273
1313
|
const date = new Date(isNaN(Number(value)) ? value : Number(value));
|
|
1274
|
-
timestampEl.textContent = date.toLocaleTimeString(void 0, {
|
|
1314
|
+
timestampEl.textContent = date.toLocaleTimeString(this._cfg.getLocale().tag || void 0, {
|
|
1275
1315
|
hour: "2-digit",
|
|
1276
1316
|
minute: "2-digit"
|
|
1277
1317
|
});
|
|
@@ -1978,7 +2018,13 @@ class AparteChatViewport extends HTMLElement {
|
|
|
1978
2018
|
if (segment && "content" in segment) {
|
|
1979
2019
|
message.segments[index] = {
|
|
1980
2020
|
...segment,
|
|
1981
|
-
content: segment.content + chunk
|
|
2021
|
+
content: segment.content + chunk,
|
|
2022
|
+
// Content arriving IS the segment's activity, so this is what
|
|
2023
|
+
// `endedAt` measures. Without it a thinking block's end would be
|
|
2024
|
+
// whenever someone happened to notice it had stopped — the end of
|
|
2025
|
+
// the turn, or the start of the next segment — and both of those
|
|
2026
|
+
// silently fold the waiting that followed into the duration.
|
|
2027
|
+
...stampSegmentActivity(segment)
|
|
1982
2028
|
};
|
|
1983
2029
|
}
|
|
1984
2030
|
this.dispatchEvent(new CustomEvent("aparte-segment-update", {
|
|
@@ -2017,8 +2063,16 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2017
2063
|
if (!message.segments) {
|
|
2018
2064
|
message.segments = [];
|
|
2019
2065
|
}
|
|
2020
|
-
|
|
2021
|
-
|
|
2066
|
+
const stamped = stampSegmentOnInsert(
|
|
2067
|
+
message.segments,
|
|
2068
|
+
segment,
|
|
2069
|
+
messageId,
|
|
2070
|
+
// THIS chat's defaults, not the page's: two chats on one page can be
|
|
2071
|
+
// configured differently, and the config seam is per instance.
|
|
2072
|
+
resolveConfig(this).getSegmentDefaults(segment.type)
|
|
2073
|
+
);
|
|
2074
|
+
message.segments.push(stamped);
|
|
2075
|
+
this._notifyBubble(messageId, "addSegment", stamped);
|
|
2022
2076
|
this._autoScroll();
|
|
2023
2077
|
}
|
|
2024
2078
|
updateSegment(a2, b2, c2) {
|
|
@@ -2031,11 +2085,10 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2031
2085
|
if (!message?.segments) return;
|
|
2032
2086
|
const segmentIndex = message.segments.findIndex((s2) => s2.id === segmentId);
|
|
2033
2087
|
if (segmentIndex !== -1) {
|
|
2034
|
-
message.segments[segmentIndex]
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
};
|
|
2038
|
-
this._notifyBubble(messageId, "updateSegment", { segmentId, updates });
|
|
2088
|
+
const current = message.segments[segmentIndex];
|
|
2089
|
+
const stamped = stampSegmentOnUpdate(current, updates);
|
|
2090
|
+
message.segments[segmentIndex] = mergeSegmentUpdate(current, stamped);
|
|
2091
|
+
this._notifyBubble(messageId, "updateSegment", { segmentId, updates: stamped });
|
|
2039
2092
|
}
|
|
2040
2093
|
}
|
|
2041
2094
|
removeSegment(a2, b2) {
|
|
@@ -2046,7 +2099,10 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2046
2099
|
const message = this._repo.getMessageById(messageId);
|
|
2047
2100
|
if (message?.segments) {
|
|
2048
2101
|
const idx = message.segments.findIndex((s2) => s2.id === segmentId);
|
|
2049
|
-
if (idx !== -1)
|
|
2102
|
+
if (idx !== -1) {
|
|
2103
|
+
message.segments.splice(idx, 1);
|
|
2104
|
+
renumberSegments(message.segments);
|
|
2105
|
+
}
|
|
2050
2106
|
}
|
|
2051
2107
|
this._notifyBubble(messageId, "removeSegment", segmentId);
|
|
2052
2108
|
}
|
|
@@ -2086,13 +2142,26 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2086
2142
|
if (message) {
|
|
2087
2143
|
message.isStreaming = false;
|
|
2088
2144
|
message.status = "completed";
|
|
2089
|
-
|
|
2090
|
-
s2.isStreaming = false;
|
|
2091
|
-
});
|
|
2145
|
+
this._settleSegments(messageId, message);
|
|
2092
2146
|
this._notifyBubble(messageId, "complete", { status: "completed" });
|
|
2093
2147
|
this._recalculateSpacer();
|
|
2094
2148
|
}
|
|
2095
2149
|
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Close a finished message's still-open segments, one `updateSegment` each.
|
|
2152
|
+
*
|
|
2153
|
+
* Deliberately NOT a loop that writes `isStreaming` onto the objects: that path
|
|
2154
|
+
* stamps the model and tells the bubble nothing, so a renderer never learns its
|
|
2155
|
+
* segment settled — no `endedAt` in the rendered label, and no final Markdown
|
|
2156
|
+
* flush either. Going through `updateSegment` reuses the one path that does
|
|
2157
|
+
* both.
|
|
2158
|
+
*/
|
|
2159
|
+
_settleSegments(messageId, message) {
|
|
2160
|
+
if (!message.segments) return;
|
|
2161
|
+
for (const id of openSegmentIds(message.segments)) {
|
|
2162
|
+
this.updateSegment(messageId, id, { isStreaming: false });
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2096
2165
|
/**
|
|
2097
2166
|
* Atomic update for a message by ID
|
|
2098
2167
|
* Supports updating content, status, segments, and other metadata
|
|
@@ -2103,6 +2172,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2103
2172
|
Object.assign(message, updates);
|
|
2104
2173
|
if (updates.status) {
|
|
2105
2174
|
message.isStreaming = updates.status === "streaming" || updates.status === "pending";
|
|
2175
|
+
if (isTerminalStatus(updates.status)) this._settleSegments(messageId, message);
|
|
2106
2176
|
}
|
|
2107
2177
|
this._notifyBubble(messageId, "update", updates);
|
|
2108
2178
|
this._autoScroll();
|
|
@@ -2117,7 +2187,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2117
2187
|
* which both records the message and creates its bubble element.
|
|
2118
2188
|
*/
|
|
2119
2189
|
addMessage(message) {
|
|
2120
|
-
this._repo.addOrUpdateMessage(this._repo.headId, { ...message });
|
|
2190
|
+
this._repo.addOrUpdateMessage(this._repo.headId, adoptMessageSegments({ ...message }));
|
|
2121
2191
|
this._pruneRenderedBubbles();
|
|
2122
2192
|
this._autoScroll();
|
|
2123
2193
|
}
|
|
@@ -2128,8 +2198,20 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2128
2198
|
* When `_frameworkManagedDOM` is true, only the internal repo is updated —
|
|
2129
2199
|
* the framework owns the DOM and will create the bubble element itself.
|
|
2130
2200
|
*/
|
|
2131
|
-
appendMessage(message) {
|
|
2132
|
-
|
|
2201
|
+
appendMessage(message, options) {
|
|
2202
|
+
const stored = options?.historical ? adoptMessageSegments(message) : message.segments?.length ? { ...message, segments: message.segments.reduce(
|
|
2203
|
+
(acc, segment) => {
|
|
2204
|
+
acc.push(stampSegmentOnInsert(
|
|
2205
|
+
acc,
|
|
2206
|
+
segment,
|
|
2207
|
+
message.id,
|
|
2208
|
+
resolveConfig(this).getSegmentDefaults(segment.type)
|
|
2209
|
+
));
|
|
2210
|
+
return acc;
|
|
2211
|
+
},
|
|
2212
|
+
[]
|
|
2213
|
+
) } : { ...message };
|
|
2214
|
+
this._repo.addOrUpdateMessage(this._repo.headId, stored);
|
|
2133
2215
|
if (!this._frameworkManagedDOM) {
|
|
2134
2216
|
const wrapper = this.querySelector(".aparte-messages-wrapper");
|
|
2135
2217
|
if (wrapper) {
|
|
@@ -2146,7 +2228,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2146
2228
|
} else {
|
|
2147
2229
|
wrapper.appendChild(bubble);
|
|
2148
2230
|
}
|
|
2149
|
-
populateBubbleFromMessage(bubble,
|
|
2231
|
+
populateBubbleFromMessage(bubble, stored);
|
|
2150
2232
|
}
|
|
2151
2233
|
}
|
|
2152
2234
|
this._pruneRenderedBubbles();
|
|
@@ -2296,7 +2378,13 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2296
2378
|
*/
|
|
2297
2379
|
importTree(tree) {
|
|
2298
2380
|
this.clearAll({ revokeAttachments: false });
|
|
2299
|
-
this._repo.import(
|
|
2381
|
+
this._repo.import({
|
|
2382
|
+
...tree,
|
|
2383
|
+
messages: tree.messages.map((entry) => ({
|
|
2384
|
+
...entry,
|
|
2385
|
+
message: adoptMessageSegments(entry.message)
|
|
2386
|
+
}))
|
|
2387
|
+
});
|
|
2300
2388
|
this._reRenderActivePath();
|
|
2301
2389
|
}
|
|
2302
2390
|
/**
|
|
@@ -2344,7 +2432,7 @@ class AparteChatViewport extends HTMLElement {
|
|
|
2344
2432
|
setMessages(messages) {
|
|
2345
2433
|
this.clearAll({ revokeAttachments: false });
|
|
2346
2434
|
for (const m2 of messages) {
|
|
2347
|
-
this.appendMessage(m2);
|
|
2435
|
+
this.appendMessage(m2, { historical: true });
|
|
2348
2436
|
}
|
|
2349
2437
|
}
|
|
2350
2438
|
/**
|
|
@@ -3205,6 +3293,7 @@ class AparteComposerInput extends HTMLElement {
|
|
|
3205
3293
|
this._render();
|
|
3206
3294
|
this._connectToRoot();
|
|
3207
3295
|
this._scheduleInitialReflow();
|
|
3296
|
+
this._unsubscribes.push(subscribeConfigChange(this, () => this._updatePlaceholder()));
|
|
3208
3297
|
}
|
|
3209
3298
|
disconnectedCallback() {
|
|
3210
3299
|
this._editor?.removeEventListener("input", this._onInput);
|
|
@@ -3408,6 +3497,16 @@ if (!customElements.get("aparte-composer-input")) {
|
|
|
3408
3497
|
class AparteComposerSend extends HTMLElement {
|
|
3409
3498
|
_button = null;
|
|
3410
3499
|
_unsubscribes = [];
|
|
3500
|
+
/**
|
|
3501
|
+
* The last `panel-change` payload.
|
|
3502
|
+
*
|
|
3503
|
+
* This button has four meanings — send, stop, submit an answer, advance to the
|
|
3504
|
+
* next question — and three of them are decided by state it does not own: the
|
|
3505
|
+
* root's `streaming`, and this payload. It was read straight out of the event's
|
|
3506
|
+
* arguments and thrown away, so nothing could recompute the button's chrome
|
|
3507
|
+
* afterwards; a config change had no way to know which of the four to write.
|
|
3508
|
+
*/
|
|
3509
|
+
_panel = null;
|
|
3411
3510
|
// Bound handler
|
|
3412
3511
|
_onClick = this._handleClick.bind(this);
|
|
3413
3512
|
connectedCallback() {
|
|
@@ -3457,27 +3556,56 @@ class AparteComposerSend extends HTMLElement {
|
|
|
3457
3556
|
root._on("attachments-change", () => this._syncState())
|
|
3458
3557
|
);
|
|
3459
3558
|
this._unsubscribes.push(
|
|
3460
|
-
root._on("panel-change", (
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
this._button.disabled = !submitEnabled;
|
|
3464
|
-
const cfg = resolveConfig(this);
|
|
3465
|
-
const advancing = mode === "advance";
|
|
3466
|
-
this._button.innerHTML = advancing ? cfg.getIcon("nextBranch") : this._getSubmitIcon();
|
|
3467
|
-
const label = advancing ? cfg.t("elicitationNext") || "Next" : cfg.t("submitButton") || "Submit";
|
|
3468
|
-
this._button.setAttribute("aria-label", label);
|
|
3469
|
-
this._button.setAttribute("title", label);
|
|
3470
|
-
this._button.classList.remove("is-streaming");
|
|
3471
|
-
} else {
|
|
3472
|
-
const root2 = this._getRoot();
|
|
3473
|
-
if (root2?.streaming) {
|
|
3474
|
-
this._syncStreamingState(true);
|
|
3475
|
-
} else {
|
|
3476
|
-
this._syncState();
|
|
3477
|
-
}
|
|
3478
|
-
}
|
|
3559
|
+
root._on("panel-change", (payload) => {
|
|
3560
|
+
this._panel = payload;
|
|
3561
|
+
this._refreshChrome();
|
|
3479
3562
|
})
|
|
3480
3563
|
);
|
|
3564
|
+
this._unsubscribes.push(subscribeConfigChange(this, () => this._refreshChrome()));
|
|
3565
|
+
}
|
|
3566
|
+
/**
|
|
3567
|
+
* Write the chrome for the mode the button is IN, deciding before writing.
|
|
3568
|
+
*
|
|
3569
|
+
* Never `_render()`: it returns early once the button exists, and its own
|
|
3570
|
+
* disabled/icon computation consults neither `root.streaming` nor the panel — so
|
|
3571
|
+
* rebuilding mid-turn would put a paper plane back while a reply was still
|
|
3572
|
+
* streaming, and rebuilding with the question panel open would silently drop out
|
|
3573
|
+
* of answer mode. It would also take the focus off the one control in this
|
|
3574
|
+
* composer most likely to be holding it.
|
|
3575
|
+
*/
|
|
3576
|
+
_refreshChrome() {
|
|
3577
|
+
if (!this._button) return;
|
|
3578
|
+
if (this._panel?.active) {
|
|
3579
|
+
this._syncPanelState();
|
|
3580
|
+
return;
|
|
3581
|
+
}
|
|
3582
|
+
if (this._getRoot()?.streaming) {
|
|
3583
|
+
this._syncStreamingState(true);
|
|
3584
|
+
return;
|
|
3585
|
+
}
|
|
3586
|
+
this._syncState();
|
|
3587
|
+
}
|
|
3588
|
+
/**
|
|
3589
|
+
* Panel open: this one button now means "answer", and WHICH answer depends on
|
|
3590
|
+
* where you are in the form.
|
|
3591
|
+
*
|
|
3592
|
+
* The icon has to move with the meaning: it drew a paper plane while the label
|
|
3593
|
+
* already said "Submit", so it read as "send a message" while it meant "answer
|
|
3594
|
+
* this question". And a check on a form with three questions left was just as
|
|
3595
|
+
* wrong — hence a chevron while there is more ahead. The visual is what a user
|
|
3596
|
+
* reads.
|
|
3597
|
+
*/
|
|
3598
|
+
_syncPanelState() {
|
|
3599
|
+
const panel = this._panel;
|
|
3600
|
+
if (!this._button || !panel?.active) return;
|
|
3601
|
+
const cfg = resolveConfig(this);
|
|
3602
|
+
const advancing = panel.mode === "advance";
|
|
3603
|
+
this._button.disabled = !panel.submitEnabled;
|
|
3604
|
+
this._button.innerHTML = advancing ? cfg.getIcon("nextBranch") : this._getSubmitIcon();
|
|
3605
|
+
const label = advancing ? cfg.t("elicitationNext") || "Next" : cfg.t("submitButton") || "Submit";
|
|
3606
|
+
this._button.setAttribute("aria-label", label);
|
|
3607
|
+
this._button.setAttribute("title", label);
|
|
3608
|
+
this._button.classList.remove("is-streaming");
|
|
3481
3609
|
}
|
|
3482
3610
|
_handleClick(e2) {
|
|
3483
3611
|
e2.preventDefault();
|
|
@@ -3500,8 +3628,9 @@ class AparteComposerSend extends HTMLElement {
|
|
|
3500
3628
|
if (streaming) {
|
|
3501
3629
|
this._button.disabled = false;
|
|
3502
3630
|
this._button.innerHTML = this._getStopIcon();
|
|
3503
|
-
this.
|
|
3504
|
-
this._button.setAttribute("
|
|
3631
|
+
const label = resolveConfig(this).t("stopButton") || "Stop";
|
|
3632
|
+
this._button.setAttribute("aria-label", label);
|
|
3633
|
+
this._button.setAttribute("title", label);
|
|
3505
3634
|
this._button.classList.add("is-streaming");
|
|
3506
3635
|
} else {
|
|
3507
3636
|
this._syncState();
|
|
@@ -3532,6 +3661,7 @@ class AparteComposerCancel extends HTMLElement {
|
|
|
3532
3661
|
connectedCallback() {
|
|
3533
3662
|
this._render();
|
|
3534
3663
|
this._connectToRoot();
|
|
3664
|
+
this._unsubscribes.push(subscribeConfigChange(this, () => this._refreshChrome()));
|
|
3535
3665
|
}
|
|
3536
3666
|
disconnectedCallback() {
|
|
3537
3667
|
this._button?.removeEventListener("click", this._onClick);
|
|
@@ -3569,6 +3699,20 @@ class AparteComposerCancel extends HTMLElement {
|
|
|
3569
3699
|
e2.preventDefault();
|
|
3570
3700
|
this._getRoot()?.cancel();
|
|
3571
3701
|
}
|
|
3702
|
+
/**
|
|
3703
|
+
* Re-read the accessible name and the icon in place.
|
|
3704
|
+
*
|
|
3705
|
+
* `hidden` is NOT touched: `_render()` always renders this button hidden and only
|
|
3706
|
+
* the root's `streaming-change` listener ever un-hides it, so a rebuild would make
|
|
3707
|
+
* the stop button vanish in the middle of a turn.
|
|
3708
|
+
*/
|
|
3709
|
+
_refreshChrome() {
|
|
3710
|
+
if (!this._button) return;
|
|
3711
|
+
const label = resolveConfig(this).t("stopButton") || "Stop";
|
|
3712
|
+
this._button.setAttribute("aria-label", label);
|
|
3713
|
+
this._button.setAttribute("title", label);
|
|
3714
|
+
this._button.innerHTML = this._getStopIcon();
|
|
3715
|
+
}
|
|
3572
3716
|
_getStopIcon() {
|
|
3573
3717
|
return resolveConfig(this).getIcon("stop");
|
|
3574
3718
|
}
|
|
@@ -3674,6 +3818,7 @@ class AparteComposerAddAttachment extends HTMLElement {
|
|
|
3674
3818
|
this._render();
|
|
3675
3819
|
this._connectToRoot();
|
|
3676
3820
|
this._setupDragDrop();
|
|
3821
|
+
this._unsubscribes.push(subscribeConfigChange(this, () => this._refreshChrome()));
|
|
3677
3822
|
}
|
|
3678
3823
|
disconnectedCallback() {
|
|
3679
3824
|
this._button?.removeEventListener("click", this._onClick);
|
|
@@ -3690,6 +3835,24 @@ class AparteComposerAddAttachment extends HTMLElement {
|
|
|
3690
3835
|
_getRoot() {
|
|
3691
3836
|
return this.closest("aparte-composer");
|
|
3692
3837
|
}
|
|
3838
|
+
/**
|
|
3839
|
+
* Re-read the label and the icon on the button that already exists.
|
|
3840
|
+
*
|
|
3841
|
+
* Deliberately not a re-render: `_render()` returns early once the button is
|
|
3842
|
+
* there, and its `disabled` computation ignores `root.streaming` while the
|
|
3843
|
+
* streaming listener sets `disabled` directly — so rebuilding would silently
|
|
3844
|
+
* re-enable the attach button mid-turn, and drop focus if the user were on it.
|
|
3845
|
+
* The native file input and the drag listeners live outside this element
|
|
3846
|
+
* (on `document.body` and on the composer root), so they are untouched either way.
|
|
3847
|
+
*/
|
|
3848
|
+
_refreshChrome() {
|
|
3849
|
+
if (!this._button) return;
|
|
3850
|
+
const cfg = resolveConfig(this);
|
|
3851
|
+
const label = cfg.t("actionUpload") || "Attach file";
|
|
3852
|
+
this._button.setAttribute("aria-label", label);
|
|
3853
|
+
this._button.setAttribute("title", label);
|
|
3854
|
+
this._button.innerHTML = cfg.getIcon("paperclip") || this._defaultIcon();
|
|
3855
|
+
}
|
|
3693
3856
|
_render() {
|
|
3694
3857
|
if (this.querySelector(".aparte-caa-button")) return;
|
|
3695
3858
|
const label = resolveConfig(this).t("actionUpload") || "Attach file";
|
|
@@ -3783,6 +3946,9 @@ class AparteComposerAction extends HTMLElement {
|
|
|
3783
3946
|
connectedCallback() {
|
|
3784
3947
|
this._render();
|
|
3785
3948
|
this._connectToRoot();
|
|
3949
|
+
this._unsubscribes.push(subscribeConfigChange(this, () => {
|
|
3950
|
+
if (this._button) this._button.innerHTML = this._resolveIcon(this.getAttribute("icon") ?? "");
|
|
3951
|
+
}));
|
|
3786
3952
|
}
|
|
3787
3953
|
disconnectedCallback() {
|
|
3788
3954
|
this._button?.removeEventListener("click", this._onClick);
|
|
@@ -4064,8 +4230,10 @@ class AparteElicitation extends HTMLElement {
|
|
|
4064
4230
|
if (evtTargetId && own && evtTargetId !== own) return;
|
|
4065
4231
|
this._cancelPending();
|
|
4066
4232
|
};
|
|
4233
|
+
_unsubscribeConfig = null;
|
|
4067
4234
|
connectedCallback() {
|
|
4068
4235
|
this.style.display = "none";
|
|
4236
|
+
this._unsubscribeConfig = subscribeConfigChange(this, () => this._relabelPending());
|
|
4069
4237
|
resolveConfig(this).setElicitationPresenter(this._present);
|
|
4070
4238
|
window.addEventListener("aparte-message-aborted", this._onTurnEnd);
|
|
4071
4239
|
window.addEventListener("aparte-message-error", this._onTurnEnd);
|
|
@@ -4091,8 +4259,24 @@ class AparteElicitation extends HTMLElement {
|
|
|
4091
4259
|
if (cfg.getElicitationPresenter() === this._present) cfg.setElicitationPresenter(null);
|
|
4092
4260
|
window.removeEventListener("aparte-message-aborted", this._onTurnEnd);
|
|
4093
4261
|
window.removeEventListener("aparte-message-error", this._onTurnEnd);
|
|
4262
|
+
this._unsubscribeConfig?.();
|
|
4263
|
+
this._unsubscribeConfig = null;
|
|
4094
4264
|
this._cancelPending();
|
|
4095
4265
|
}
|
|
4266
|
+
/**
|
|
4267
|
+
* Re-apply the open question's strings, in place.
|
|
4268
|
+
*
|
|
4269
|
+
* Two owners, and both have to move or the panel goes bilingual: the panel's own
|
|
4270
|
+
* defaults (`relabel`), and the Skip button, which this file builds and this file
|
|
4271
|
+
* therefore has to re-text. The composer's one button is a third, and it already
|
|
4272
|
+
* follows — `aparte-composer-send` remembers the panel state it was given.
|
|
4273
|
+
*/
|
|
4274
|
+
_relabelPending() {
|
|
4275
|
+
if (!this._pending) return;
|
|
4276
|
+
const cfg = resolveConfig(this);
|
|
4277
|
+
runWithConfig(cfg, () => this._pending.panel.relabel());
|
|
4278
|
+
this._pending.skip.textContent = cfg.t("elicitationSkip");
|
|
4279
|
+
}
|
|
4096
4280
|
_present = (request) => {
|
|
4097
4281
|
if (this._pending) return Promise.resolve({ action: "cancel" });
|
|
4098
4282
|
const composer = this._getComposer();
|
|
@@ -4123,7 +4307,7 @@ class AparteElicitation extends HTMLElement {
|
|
|
4123
4307
|
skip.textContent = cfg.t("elicitationSkip");
|
|
4124
4308
|
skip.addEventListener("click", () => settle({ action: "decline" }));
|
|
4125
4309
|
panel.dismiss.appendChild(skip);
|
|
4126
|
-
this._pending = { settle, composer };
|
|
4310
|
+
this._pending = { settle, composer, panel, skip };
|
|
4127
4311
|
composer.showPanel(panel.el, {
|
|
4128
4312
|
submitEnabled: panel.canProceed(),
|
|
4129
4313
|
mode: panel.mode(),
|
|
@@ -4207,21 +4391,22 @@ function registerAllComponents() {
|
|
|
4207
4391
|
}
|
|
4208
4392
|
}
|
|
4209
4393
|
export {
|
|
4210
|
-
A as
|
|
4211
|
-
a as
|
|
4212
|
-
b as
|
|
4213
|
-
c as
|
|
4214
|
-
d as
|
|
4215
|
-
e as
|
|
4216
|
-
f as
|
|
4217
|
-
g as
|
|
4218
|
-
h as
|
|
4394
|
+
A as APARTE_CONFIG_CHANGE,
|
|
4395
|
+
a as APARTE_CONVERSATION_SCHEMA_VERSION,
|
|
4396
|
+
b as APARTE_DEFAULT_BUBBLE_ACTIONS,
|
|
4397
|
+
c as APARTE_DEFAULT_HOST_HANDLERS,
|
|
4398
|
+
d as APARTE_DEFAULT_ICON_FALLBACKS,
|
|
4399
|
+
e as APARTE_DEFAULT_LOCALE,
|
|
4400
|
+
f as APARTE_DEFAULT_SKELETON_FALLBACKS,
|
|
4401
|
+
g as APARTE_DEFAULT_UI_EVENTS,
|
|
4402
|
+
h as APARTE_HOST_ATTR,
|
|
4403
|
+
i as AparteBackendTransport,
|
|
4219
4404
|
AparteChat,
|
|
4220
4405
|
AparteChatBubble,
|
|
4221
|
-
|
|
4406
|
+
j as AparteChatHost,
|
|
4222
4407
|
AparteChatStatus,
|
|
4223
4408
|
AparteChatViewport,
|
|
4224
|
-
|
|
4409
|
+
k as AparteClient,
|
|
4225
4410
|
AparteComposer,
|
|
4226
4411
|
AparteComposerAction,
|
|
4227
4412
|
AparteComposerAddAttachment,
|
|
@@ -4230,55 +4415,59 @@ export {
|
|
|
4230
4415
|
AparteComposerInput,
|
|
4231
4416
|
AparteComposerSend,
|
|
4232
4417
|
AparteComposerToolbar,
|
|
4233
|
-
|
|
4234
|
-
|
|
4418
|
+
l as AparteConfig,
|
|
4419
|
+
m as AparteConversationController,
|
|
4235
4420
|
AparteConversationList,
|
|
4236
|
-
|
|
4237
|
-
|
|
4421
|
+
n as AparteConversationManager,
|
|
4422
|
+
o as AparteDirectTransport,
|
|
4238
4423
|
AparteElicitation,
|
|
4239
|
-
|
|
4240
|
-
|
|
4424
|
+
p as AparteError,
|
|
4425
|
+
q as AparteErrorCode,
|
|
4241
4426
|
AparteMessageRepository,
|
|
4242
4427
|
AparteOptgroup,
|
|
4243
4428
|
AparteOption,
|
|
4244
4429
|
AparteProgressSpinner,
|
|
4245
4430
|
AparteSelect,
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4431
|
+
s as AparteStreamParser,
|
|
4432
|
+
t as aparteGlobalConfig,
|
|
4433
|
+
u as applyElementProps,
|
|
4434
|
+
v as attachConfig,
|
|
4250
4435
|
buildElicitationPanel,
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4436
|
+
x as collectRendererStyles,
|
|
4437
|
+
y as contentToText,
|
|
4438
|
+
z as contextConfig,
|
|
4439
|
+
B as createAparteChatHandler,
|
|
4440
|
+
C as createStreamAdapter,
|
|
4256
4441
|
cssEscape,
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4442
|
+
E as declineDefaultRenderers,
|
|
4443
|
+
F as defaultSanitizer,
|
|
4444
|
+
G as deriveArtifactKind,
|
|
4445
|
+
H as detachConfig,
|
|
4261
4446
|
escapeAttr,
|
|
4262
4447
|
escapeHtml,
|
|
4263
|
-
|
|
4264
|
-
|
|
4448
|
+
K as filesToAttachments,
|
|
4449
|
+
L as getAllRenderers,
|
|
4265
4450
|
getSegmentRenderer,
|
|
4266
4451
|
installDefaultRenderersOnce,
|
|
4267
4452
|
isAwaitingReply,
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4453
|
+
P as isFormatAdapter,
|
|
4454
|
+
Q as isSafeUrl,
|
|
4455
|
+
R as isSegmentSettled,
|
|
4456
|
+
S as parseAparteEventStream,
|
|
4457
|
+
T as parseMarkdownToSegments,
|
|
4272
4458
|
populateBubbleFromMessage,
|
|
4273
|
-
|
|
4459
|
+
V as readableToAsyncIterable,
|
|
4274
4460
|
registerAllComponents,
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4461
|
+
W as registerDefaultRenderers,
|
|
4462
|
+
X as registerSegmentRenderer,
|
|
4463
|
+
Y as requestUserInput,
|
|
4278
4464
|
resolveConfig,
|
|
4279
4465
|
revokeAttachmentUrls,
|
|
4280
4466
|
runWithConfig,
|
|
4281
|
-
|
|
4467
|
+
a0 as segmentDuration,
|
|
4468
|
+
a1 as segmentTiming,
|
|
4469
|
+
subscribeConfigChange,
|
|
4470
|
+
a3 as unregisterSegmentRenderer,
|
|
4282
4471
|
uuid
|
|
4283
4472
|
};
|
|
4284
4473
|
//# sourceMappingURL=index.js.map
|