@aparte/core 0.12.0 → 0.13.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 +25 -0
- package/dist/client/aparte-client.d.ts.map +1 -1
- package/dist/components/bubble/aparte-chat-bubble.d.ts +117 -0
- package/dist/components/bubble/aparte-chat-bubble.d.ts.map +1 -1
- package/dist/components/chat/aparte-chat.d.ts +70 -15
- package/dist/components/chat/aparte-chat.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-action.d.ts +48 -7
- package/dist/components/composer/aparte-composer-action.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-add-attachment.d.ts +37 -4
- package/dist/components/composer/aparte-composer-add-attachment.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-attachments.d.ts +35 -3
- package/dist/components/composer/aparte-composer-attachments.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-cancel.d.ts +25 -2
- package/dist/components/composer/aparte-composer-cancel.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-input.d.ts +54 -5
- package/dist/components/composer/aparte-composer-input.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-send.d.ts +47 -4
- package/dist/components/composer/aparte-composer-send.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-toolbar.d.ts +15 -4
- package/dist/components/composer/aparte-composer-toolbar.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer.d.ts +139 -7
- package/dist/components/composer/aparte-composer.d.ts.map +1 -1
- package/dist/components/composer/index.d.ts +1 -1
- package/dist/components/composer/index.d.ts.map +1 -1
- package/dist/components/conversation-list/aparte-conversation-list.d.ts +50 -8
- package/dist/components/conversation-list/aparte-conversation-list.d.ts.map +1 -1
- package/dist/components/elicitation/aparte-elicitation.d.ts +45 -0
- 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/status/aparte-chat-status.d.ts +47 -5
- package/dist/components/status/aparte-chat-status.d.ts.map +1 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts +78 -8
- package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
- package/dist/config/aparte-config.d.ts +22 -0
- package/dist/config/aparte-config.d.ts.map +1 -1
- package/dist/config/icon-provider.d.ts +17 -15
- package/dist/config/icon-provider.d.ts.map +1 -1
- package/dist/config/sanitize.d.ts.map +1 -1
- package/dist/custom-elements.json +5400 -3278
- package/dist/elicitation/approval-panel.d.ts.map +1 -1
- package/dist/elicitation/panel.d.ts +23 -2
- package/dist/elicitation/panel.d.ts.map +1 -1
- package/dist/generated/element-attributes.d.ts +21 -15
- package/dist/generated/element-attributes.d.ts.map +1 -1
- package/dist/glyphs-QXfbL3n_.js +81 -0
- package/dist/glyphs-QXfbL3n_.js.map +1 -0
- package/dist/host/aparte-chat-host.d.ts +22 -21
- package/dist/host/aparte-chat-host.d.ts.map +1 -1
- package/dist/icons/extended.d.ts +153 -0
- package/dist/icons/extended.d.ts.map +1 -0
- package/dist/icons/glyphs.d.ts +98 -0
- package/dist/icons/glyphs.d.ts.map +1 -0
- package/dist/icons/index.d.ts +24 -0
- package/dist/icons/index.d.ts.map +1 -0
- package/dist/icons.d.ts +14 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +157 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.css +5082 -2119
- package/dist/index.d.ts +37 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +167 -67
- package/dist/index.js.map +1 -1
- package/dist/index.node.d.ts +2 -2
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +1 -1
- package/dist/index.node.js.map +1 -1
- package/dist/{is-awaiting-reply-CxWRISBC.js → is-awaiting-reply-DrO_oR32.js} +369 -148
- package/dist/is-awaiting-reply-DrO_oR32.js.map +1 -0
- package/dist/primitives/icon/aparte-icon.d.ts +49 -0
- package/dist/primitives/icon/aparte-icon.d.ts.map +1 -0
- package/dist/primitives/icon/index.d.ts +2 -0
- package/dist/primitives/icon/index.d.ts.map +1 -0
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/progress-spinner/aparte-progress-spinner.d.ts +20 -5
- package/dist/primitives/progress-spinner/aparte-progress-spinner.d.ts.map +1 -1
- package/dist/primitives/select/aparte-optgroup.d.ts +30 -7
- package/dist/primitives/select/aparte-optgroup.d.ts.map +1 -1
- package/dist/primitives/select/aparte-option.d.ts +29 -3
- package/dist/primitives/select/aparte-option.d.ts.map +1 -1
- package/dist/primitives/select/aparte-select.d.ts +36 -11
- package/dist/primitives/select/aparte-select.d.ts.map +1 -1
- package/dist/renderers/segments/artifact/card.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/element-map.d.ts +2 -0
- package/dist/types/element-map.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/segments.d.ts +92 -7
- package/dist/types/segments.d.ts.map +1 -1
- package/dist/types/tools.d.ts +2 -0
- package/dist/types/tools.d.ts.map +1 -1
- package/package.json +7 -1
- package/dist/is-awaiting-reply-CxWRISBC.js.map +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { A as APARTE_ICON_GLYPHS } from "./glyphs-QXfbL3n_.js";
|
|
1
2
|
function contentToText(content) {
|
|
2
3
|
if (typeof content === "string") return content;
|
|
3
4
|
return content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
@@ -35,29 +36,7 @@ class AparteError extends Error {
|
|
|
35
36
|
return new AparteError(message, defaultCode, void 0, error, status);
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
|
-
const APARTE_DEFAULT_ICON_FALLBACKS =
|
|
39
|
-
copy: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,
|
|
40
|
-
check: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`,
|
|
41
|
-
send: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>`,
|
|
42
|
-
loading: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="aparte-icon-spin"><circle cx="12" cy="12" r="10" stroke-opacity="0.25"/><path d="M12 2a10 10 0 0 1 10 10"/></svg>`,
|
|
43
|
-
error: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>`,
|
|
44
|
-
expand: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>`,
|
|
45
|
-
collapse: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg>`,
|
|
46
|
-
terminal: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>`,
|
|
47
|
-
paperclip: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>`,
|
|
48
|
-
image: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`,
|
|
49
|
-
file: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>`,
|
|
50
|
-
scrollDown: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>`,
|
|
51
|
-
retry: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>`,
|
|
52
|
-
edit: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`,
|
|
53
|
-
thumbUp: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 10v12"/><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"/></svg>`,
|
|
54
|
-
thumbDown: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 14V2"/><path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z"/></svg>`,
|
|
55
|
-
prevBranch: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>`,
|
|
56
|
-
nextBranch: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>`,
|
|
57
|
-
tool: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>`,
|
|
58
|
-
close: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`,
|
|
59
|
-
stop: `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><rect x="2" y="2" width="12" height="12" rx="2"/></svg>`
|
|
60
|
-
};
|
|
39
|
+
const APARTE_DEFAULT_ICON_FALLBACKS = APARTE_ICON_GLYPHS;
|
|
61
40
|
const APARTE_DEFAULT_LOCALE = {
|
|
62
41
|
inputPlaceholder: "Type a message...",
|
|
63
42
|
sendButton: "Send",
|
|
@@ -122,6 +101,14 @@ const APARTE_DEFAULT_LOCALE = {
|
|
|
122
101
|
unarchiveConversation: "Unarchive conversation",
|
|
123
102
|
direction: "ltr"
|
|
124
103
|
};
|
|
104
|
+
const APARTE_DEFAULT_SKELETON_FALLBACKS = {
|
|
105
|
+
message: '<div class="aparte-skeleton-fallback">Loading...</div>',
|
|
106
|
+
code: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--code">Loading code...</div>',
|
|
107
|
+
thinking: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--tight">Thinking...</div>',
|
|
108
|
+
input: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--snug">...</div>',
|
|
109
|
+
list: '<div class="aparte-skeleton-fallback">Loading items...</div>',
|
|
110
|
+
text: '<div class="aparte-skeleton-fallback aparte-skeleton-fallback--tight">...</div>'
|
|
111
|
+
};
|
|
125
112
|
function isFormatAdapter(p) {
|
|
126
113
|
return typeof p.buildRequest === "function" && typeof p.parseStream === "function" && typeof p.defaultEndpoint === "string" && (typeof p.authHeaders === "function" || typeof p.authQuery === "function");
|
|
127
114
|
}
|
|
@@ -959,6 +946,7 @@ const SAFE_STYLE_PROPS = /* @__PURE__ */ new Set([
|
|
|
959
946
|
"white-space"
|
|
960
947
|
]);
|
|
961
948
|
function isSafeCustomProperty(prop) {
|
|
949
|
+
if (prop.includes("\\")) return false;
|
|
962
950
|
return prop.startsWith("--") && !prop.startsWith("--aparte-");
|
|
963
951
|
}
|
|
964
952
|
function scrubStyle(value) {
|
|
@@ -1873,6 +1861,24 @@ class AparteConfig {
|
|
|
1873
1861
|
getTools() {
|
|
1874
1862
|
return Array.from(this._tools.values()).map((e) => e.tool);
|
|
1875
1863
|
}
|
|
1864
|
+
/**
|
|
1865
|
+
* The registered tools' own `systemPrompt`s, joined, or `null` if none set one.
|
|
1866
|
+
*
|
|
1867
|
+
* `AparteTool.systemPrompt` has always been documented as "injected automatically when
|
|
1868
|
+
* this tool is registered", on the type and in the tools guide — and nothing read it.
|
|
1869
|
+
* A field that is documented and dead is worse than one that does not exist: a caller
|
|
1870
|
+
* writes it, sees the tool work (the model has the schema either way), and never learns
|
|
1871
|
+
* that the sentence explaining WHEN to reach for it was dropped. `@aparte/plugin-ask-user`
|
|
1872
|
+
* sets one, so a shipped plugin was losing its instructions.
|
|
1873
|
+
*
|
|
1874
|
+
* Registration order, blank line between, because two tools' instructions are two
|
|
1875
|
+
* paragraphs and not one run-on sentence. The app's own template stays a separate
|
|
1876
|
+
* message — see `resolveSystemPrompt`, which is about the app and not about the tools.
|
|
1877
|
+
*/
|
|
1878
|
+
resolveToolSystemPrompts() {
|
|
1879
|
+
const prompts = Array.from(this._tools.values()).map((entry) => entry.tool.systemPrompt?.trim()).filter((prompt) => Boolean(prompt));
|
|
1880
|
+
return prompts.length ? prompts.join("\n\n") : null;
|
|
1881
|
+
}
|
|
1876
1882
|
/** Get the handler for a tool by name */
|
|
1877
1883
|
getToolHandler(name) {
|
|
1878
1884
|
return this._tools.get(name)?.handler;
|
|
@@ -2122,16 +2128,15 @@ class AparteConfig {
|
|
|
2122
2128
|
_defaultHighlightRenderer(code) {
|
|
2123
2129
|
return `<pre><code>${escapeHtml(code)}</code></pre>`;
|
|
2124
2130
|
}
|
|
2131
|
+
/**
|
|
2132
|
+
* ONE owner. This used to hold a second, hand-written copy of
|
|
2133
|
+
* `APARTE_DEFAULT_SKELETON_FALLBACKS`, and the two had already drifted — `message`
|
|
2134
|
+
* said "Loading message…" here and "Loading…" there, `text` the reverse. A duplicate
|
|
2135
|
+
* that disagrees with its original is worse than either: whichever one you read, you
|
|
2136
|
+
* cannot tell which one ships.
|
|
2137
|
+
*/
|
|
2125
2138
|
_defaultSkeletonRenderer(type) {
|
|
2126
|
-
|
|
2127
|
-
message: '<div class="aparte-skeleton-fallback" style="padding:16px;color:#9ca3af;">Loading message...</div>',
|
|
2128
|
-
code: '<div class="aparte-skeleton-fallback" style="padding:16px;background:#1e293b;color:#64748b;border-radius:8px;">Loading code...</div>',
|
|
2129
|
-
text: '<div class="aparte-skeleton-fallback" style="padding:8px;color:#9ca3af;">Loading...</div>',
|
|
2130
|
-
thinking: '<div class="aparte-skeleton-fallback" style="padding:8px;color:#9ca3af;">Thinking...</div>',
|
|
2131
|
-
input: '<div class="aparte-skeleton-fallback" style="padding:12px;color:#9ca3af;">...</div>',
|
|
2132
|
-
list: '<div class="aparte-skeleton-fallback" style="padding:16px;color:#9ca3af;">Loading items...</div>'
|
|
2133
|
-
};
|
|
2134
|
-
return fallbacks[type] || fallbacks.text;
|
|
2139
|
+
return APARTE_DEFAULT_SKELETON_FALLBACKS[type] ?? APARTE_DEFAULT_SKELETON_FALLBACKS.text;
|
|
2135
2140
|
}
|
|
2136
2141
|
}
|
|
2137
2142
|
const GLOBAL_CONFIG_KEY = Symbol.for("aparte.globalConfig.1");
|
|
@@ -2195,14 +2200,6 @@ function subscribeConfigChange(el2, handler) {
|
|
|
2195
2200
|
window.addEventListener(APARTE_CONFIG_CHANGE, listener);
|
|
2196
2201
|
return () => window.removeEventListener(APARTE_CONFIG_CHANGE, listener);
|
|
2197
2202
|
}
|
|
2198
|
-
const APARTE_DEFAULT_SKELETON_FALLBACKS = {
|
|
2199
|
-
message: '<div class="aparte-skeleton-fallback" style="padding:16px;color:#9ca3af;">Loading...</div>',
|
|
2200
|
-
code: '<div class="aparte-skeleton-fallback" style="padding:16px;background:#1e293b;color:#64748b;border-radius:8px;">Loading code...</div>',
|
|
2201
|
-
thinking: '<div class="aparte-skeleton-fallback" style="padding:8px;color:#9ca3af;">Thinking...</div>',
|
|
2202
|
-
input: '<div class="aparte-skeleton-fallback" style="padding:12px;color:#9ca3af;">...</div>',
|
|
2203
|
-
list: '<div class="aparte-skeleton-fallback" style="padding:16px;color:#9ca3af;">Loading items...</div>',
|
|
2204
|
-
text: '<div class="aparte-skeleton-fallback" style="padding:8px;color:#9ca3af;">...</div>'
|
|
2205
|
-
};
|
|
2206
2203
|
function writeStreamedMarkdown(host, contentEl, content, streaming) {
|
|
2207
2204
|
if (streaming) {
|
|
2208
2205
|
if (host._aparteSmd === void 0) {
|
|
@@ -2275,7 +2272,21 @@ const thinkingRenderer = {
|
|
|
2275
2272
|
* new class of risk. With no Markdown provider registered it degrades to the
|
|
2276
2273
|
* zero-dependency default (escape + `<br>`), which is what it used to be.
|
|
2277
2274
|
*/
|
|
2278
|
-
|
|
2275
|
+
/*
|
|
2276
|
+
* WEARS THE ACCORDION RECIPE. A reasoning block is a disclosure — `<details>`, a
|
|
2277
|
+
* `<summary>` you press, a panel, a chevron that turns — which is exactly what
|
|
2278
|
+
* `surface/accordion.css` already draws, and this renderer used to draw a second
|
|
2279
|
+
* time under four classes of its own. Two costs, both visible: it looked unrelated
|
|
2280
|
+
* to every other disclosure in the library, and its chevron was the CHARACTER `▼`,
|
|
2281
|
+
* a fourth way to draw a chevron here and the only one that is not a glyph — so it
|
|
2282
|
+
* could not take `--aparte-icon-size`, could not be replaced through the icon
|
|
2283
|
+
* provider, and rendered in whatever the platform font felt like.
|
|
2284
|
+
*
|
|
2285
|
+
* What stays thinking's own is the left rail and the quieter tone, which is the
|
|
2286
|
+
* only part that is genuinely about reasoning rather than about disclosure.
|
|
2287
|
+
* `.aparte-thinking-label` stays too: `relabel` below queries it.
|
|
2288
|
+
*/
|
|
2289
|
+
render: (segment) => `<details class="aparte-segment aparte-segment-thinking aparte-accordion__item" data-segment-id="${escapeHtml(segment.id)}" ${segment.collapsed === false ? "open" : ""}><summary class="aparte-accordion__header aparte-thinking-header"><span class="aparte-thinking-label">${escapeHtml(segment.label || contextConfig().t("thinking"))}</span>${contextConfig().getIcon("expand")}</summary><div class="aparte-accordion__panel aparte-thinking-content">${contextConfig().renderMarkdown(segment.content)}</div></details>`,
|
|
2279
2290
|
/**
|
|
2280
2291
|
* The default label is the only config-derived text here — and `segment.label`
|
|
2281
2292
|
* still wins, exactly as in `render`, because that string is the app's.
|
|
@@ -2349,7 +2360,7 @@ const codeRenderer = {
|
|
|
2349
2360
|
<div class="aparte-code-header">
|
|
2350
2361
|
${segment.filename ? `<span class="aparte-code-filename">${escapeHtml(segment.filename)}</span>` : `<span class="aparte-code-header-filler"></span>`}
|
|
2351
2362
|
<span class="aparte-code-language">${escapeHtml(segment.language || "")}</span>
|
|
2352
|
-
<button class="aparte-code-copy" data-action="copy" title="${escapeAttr(contextConfig().t("copy"))}">
|
|
2363
|
+
<button class="aparte-btn aparte-btn--icon aparte-btn--sm aparte-code-copy" data-action="copy" title="${escapeAttr(contextConfig().t("copy"))}">
|
|
2353
2364
|
${contextConfig().getIcon("copy")}
|
|
2354
2365
|
</button>
|
|
2355
2366
|
</div>
|
|
@@ -2418,7 +2429,7 @@ const errorRenderer = {
|
|
|
2418
2429
|
return out;
|
|
2419
2430
|
}
|
|
2420
2431
|
return `
|
|
2421
|
-
<div class="aparte-segment aparte-segment-error" data-segment-id="${escapeHtml(segment.id)}">
|
|
2432
|
+
<div class="aparte-segment aparte-alert aparte-alert--danger aparte-segment-error" data-segment-id="${escapeHtml(segment.id)}">
|
|
2422
2433
|
<div class="aparte-error-icon-wrapper">
|
|
2423
2434
|
${contextConfig().getIcon("error") || "⚠"}
|
|
2424
2435
|
</div>
|
|
@@ -2517,7 +2528,7 @@ function pillMarkup(segment, name, status) {
|
|
|
2517
2528
|
<span class="aparte-tool-icon">${contextConfig().getIcon("tool")}</span>
|
|
2518
2529
|
<span class="aparte-tool-name">${escapeHtml(name)}</span>
|
|
2519
2530
|
</span>
|
|
2520
|
-
<span class="aparte-tool-spinner" aria-hidden="true"${status === "pending" ? "" : " hidden"}></span>
|
|
2531
|
+
<span class="aparte-spinner aparte-tool-spinner" aria-hidden="true"${status === "pending" ? "" : " hidden"}></span>
|
|
2521
2532
|
<span class="aparte-tool-state">${badge}</span>`;
|
|
2522
2533
|
}
|
|
2523
2534
|
const toolCallRenderer = {
|
|
@@ -2532,6 +2543,7 @@ const toolCallRenderer = {
|
|
|
2532
2543
|
if (out) return out;
|
|
2533
2544
|
}
|
|
2534
2545
|
const pill = pillMarkup(segment, name, status);
|
|
2546
|
+
const toggle = contextConfig().getIcon("expand");
|
|
2535
2547
|
const input = describeInput(segment);
|
|
2536
2548
|
const result = segment.result;
|
|
2537
2549
|
if (!input && !result) {
|
|
@@ -2543,7 +2555,7 @@ const toolCallRenderer = {
|
|
|
2543
2555
|
const cfg = contextConfig();
|
|
2544
2556
|
return `
|
|
2545
2557
|
<details class="aparte-segment aparte-segment-tool-call" data-segment-id="${escapeHtml(segment.id)}" data-status="${escapeAttr(status)}" data-tool-call-id="${escapeAttr(toolCallId)}">
|
|
2546
|
-
<summary class="aparte-tool-summary"><span class="aparte-tool-toggle"
|
|
2558
|
+
<summary class="aparte-tool-summary"><span class="aparte-tool-toggle">${toggle}</span>${pill}</summary>
|
|
2547
2559
|
<div class="aparte-tool-detail">
|
|
2548
2560
|
${input ? `
|
|
2549
2561
|
<div class="aparte-tool-part" data-part="input">
|
|
@@ -2618,7 +2630,7 @@ const toolCallRenderer = {
|
|
|
2618
2630
|
const customRenderer = contextConfig().getToolRenderer(segment.toolCall?.name);
|
|
2619
2631
|
customRenderer?.setup?.(element, segment);
|
|
2620
2632
|
},
|
|
2621
|
-
// CSS lives in styles/
|
|
2633
|
+
// CSS lives in styles/segment/ — see that folder for why a
|
|
2622
2634
|
// built-in's rules belong there. `getStyles` stays for a CONSUMER's renderer, which
|
|
2623
2635
|
// has no other way onto the page.
|
|
2624
2636
|
getStyles: () => ""
|
|
@@ -2629,9 +2641,9 @@ const pipelineWaitingRenderer = {
|
|
|
2629
2641
|
render: (segment) => {
|
|
2630
2642
|
return `
|
|
2631
2643
|
<div class="aparte-segment aparte-segment-pipeline-waiting" data-segment-id="${escapeHtml(segment.id)}" aria-label="${escapeAttr(waitingLabel())}" role="status">
|
|
2632
|
-
<span class="aparte-pw-dot"></span>
|
|
2633
|
-
<span class="aparte-pw-dot"></span>
|
|
2634
|
-
<span class="aparte-pw-dot"></span>
|
|
2644
|
+
<span class="aparte-dot aparte-pw-dot"></span>
|
|
2645
|
+
<span class="aparte-dot aparte-pw-dot"></span>
|
|
2646
|
+
<span class="aparte-dot aparte-pw-dot"></span>
|
|
2635
2647
|
</div>`;
|
|
2636
2648
|
},
|
|
2637
2649
|
update: () => {
|
|
@@ -2656,7 +2668,7 @@ const pipelineWaitingRenderer = {
|
|
|
2656
2668
|
});
|
|
2657
2669
|
observer.observe(parent, { childList: true });
|
|
2658
2670
|
},
|
|
2659
|
-
// CSS lives in styles/
|
|
2671
|
+
// CSS lives in styles/segment/ — see that folder for why a
|
|
2660
2672
|
// built-in's rules belong there. `getStyles` stays for a CONSUMER's renderer, which
|
|
2661
2673
|
// has no other way onto the page.
|
|
2662
2674
|
getStyles: () => ""
|
|
@@ -2862,7 +2874,7 @@ function renderBinaryFileArtifact(segment, kind) {
|
|
|
2862
2874
|
if (cached && !isStreaming) {
|
|
2863
2875
|
const previewBody = cached.previewHtml ? contextConfig().sanitizeHtml(cached.previewHtml) : `<div class="aparte-art-file__preview-empty">Preview not available for ${escapeHtml(kind)} yet</div>`;
|
|
2864
2876
|
return `
|
|
2865
|
-
<div class="aparte-segment aparte-segment-artifact-file"
|
|
2877
|
+
<div class="aparte-segment aparte-card aparte-segment-artifact-file"
|
|
2866
2878
|
data-segment-id="${escapeHtml(segment.id)}"
|
|
2867
2879
|
data-artifact-type="${escapeHtml(kind)}"
|
|
2868
2880
|
data-state="ready">
|
|
@@ -2873,7 +2885,7 @@ function renderBinaryFileArtifact(segment, kind) {
|
|
|
2873
2885
|
<div class="aparte-art-file__meta-sub" data-role="file-sub">${escapeHtml(formatBytes(cached.bytes))} · ${escapeHtml(kind.toUpperCase())}</div>
|
|
2874
2886
|
</div>
|
|
2875
2887
|
<div class="aparte-art-file__actions">
|
|
2876
|
-
${canRedownload ? `<button type="button" class="aparte-art-file__btn aparte-art-file__btn--primary" data-action="download">${downloadLabel}</button>` : ""}
|
|
2888
|
+
${canRedownload ? `<button type="button" class="aparte-btn aparte-btn--primary aparte-btn--solid aparte-art-file__btn aparte-art-file__btn--primary" data-action="download">${downloadLabel}</button>` : ""}
|
|
2877
2889
|
</div>
|
|
2878
2890
|
</div>
|
|
2879
2891
|
<div class="aparte-art-file__body">
|
|
@@ -2888,7 +2900,7 @@ function renderBinaryFileArtifact(segment, kind) {
|
|
|
2888
2900
|
const cleanContent = stripCodeFences(segment.content || "");
|
|
2889
2901
|
const subText = isStreaming ? contextConfig().t("generating") : contextConfig().t("rebuildingPreview");
|
|
2890
2902
|
return `
|
|
2891
|
-
<div class="aparte-segment aparte-segment-artifact-file"
|
|
2903
|
+
<div class="aparte-segment aparte-card aparte-segment-artifact-file"
|
|
2892
2904
|
data-segment-id="${escapeHtml(segment.id)}"
|
|
2893
2905
|
data-artifact-type="${escapeHtml(kind)}"
|
|
2894
2906
|
data-state="${isStreaming ? "streaming" : "compiling"}">
|
|
@@ -2899,7 +2911,7 @@ function renderBinaryFileArtifact(segment, kind) {
|
|
|
2899
2911
|
<div class="aparte-art-file__meta-sub" data-role="file-sub">${escapeHtml(subText)}</div>
|
|
2900
2912
|
</div>
|
|
2901
2913
|
<div class="aparte-art-file__actions">
|
|
2902
|
-
${canRedownload ? `<button type="button" class="aparte-art-file__btn aparte-art-file__btn--primary" data-action="download" disabled>${downloadLabel}</button>` : ""}
|
|
2914
|
+
${canRedownload ? `<button type="button" class="aparte-btn aparte-btn--primary aparte-btn--solid aparte-art-file__btn aparte-art-file__btn--primary" data-action="download" disabled>${downloadLabel}</button>` : ""}
|
|
2903
2915
|
</div>
|
|
2904
2916
|
</div>
|
|
2905
2917
|
<div class="aparte-art-file__body">
|
|
@@ -3057,7 +3069,7 @@ function showSandboxError(element, phase, errorMsg) {
|
|
|
3057
3069
|
body.innerHTML = `
|
|
3058
3070
|
<div class="aparte-art-file__error">
|
|
3059
3071
|
<div class="aparte-art-file__error-title">${escapeHtml(contextConfig().t("sandboxError"))}</div>
|
|
3060
|
-
<div class="aparte-art-file__error-msg">${escapeHtml(short)}</div>
|
|
3072
|
+
<div class="aparte-output aparte-art-file__error-msg">${escapeHtml(short)}</div>
|
|
3061
3073
|
<div class="aparte-art-file__error-hint">${escapeHtml(contextConfig().t("sandboxErrorHint"))}</div>
|
|
3062
3074
|
</div>
|
|
3063
3075
|
`;
|
|
@@ -3082,8 +3094,9 @@ const artifactRenderer = {
|
|
|
3082
3094
|
const codeLabel = cfg.t("code");
|
|
3083
3095
|
const isBinary = BINARY_FILE_KINDS.has(kind);
|
|
3084
3096
|
const cleanContent = stripCodeFences(segment.content || "");
|
|
3097
|
+
const cardId = escapeAttr(segment.id);
|
|
3085
3098
|
return `
|
|
3086
|
-
<div class="aparte-segment aparte-segment-artifact-card"
|
|
3099
|
+
<div class="aparte-segment aparte-card aparte-segment-artifact-card"
|
|
3087
3100
|
data-segment-id="${escapeHtml(segment.id)}"
|
|
3088
3101
|
data-artifact-type="${escapeHtml(kind)}"
|
|
3089
3102
|
data-streaming="${isStreaming ? "true" : "false"}"
|
|
@@ -3092,31 +3105,31 @@ const artifactRenderer = {
|
|
|
3092
3105
|
data-binary="${isBinary ? "true" : "false"}">
|
|
3093
3106
|
<header class="aparte-art-card__header">
|
|
3094
3107
|
<div class="aparte-art-card__title-block">
|
|
3095
|
-
<span class="aparte-art-card__kind" data-kind="${escapeHtml(kind)}">${escapeHtml(displayLang)}</span>
|
|
3108
|
+
<span class="aparte-badge aparte-badge--outline aparte-art-card__kind" data-kind="${escapeHtml(kind)}">${escapeHtml(displayLang)}</span>
|
|
3096
3109
|
<span class="aparte-art-card__title">${escapeHtml(title)}</span>
|
|
3097
|
-
${isStreaming ?
|
|
3110
|
+
${isStreaming ? `<span class="aparte-dot aparte-art-card__pulse" role="img" aria-label="${escapeAttr(cfg.t("generating"))}"></span>` : ""}
|
|
3098
3111
|
</div>
|
|
3099
3112
|
<div class="aparte-art-card__actions">
|
|
3100
|
-
<button type="button" class="aparte-art-card__btn" data-action="copy" title="${escapeAttr(contextConfig().t("copy"))}" aria-label="${escapeAttr(contextConfig().t("copy"))}">
|
|
3113
|
+
<button type="button" class="aparte-btn aparte-btn--icon aparte-art-card__btn" data-action="copy" title="${escapeAttr(contextConfig().t("copy"))}" aria-label="${escapeAttr(contextConfig().t("copy"))}">
|
|
3101
3114
|
${contextConfig().getIcon("copy")}
|
|
3102
3115
|
</button>
|
|
3103
|
-
<button type="button" class="aparte-art-card__btn" data-action="download" title="${escapeAttr(downloadLabel)}" aria-label="${escapeAttr(downloadLabel)}" ${isStreaming ? "disabled" : ""}>
|
|
3104
|
-
|
|
3116
|
+
<button type="button" class="aparte-btn aparte-btn--icon aparte-art-card__btn" data-action="download" title="${escapeAttr(downloadLabel)}" aria-label="${escapeAttr(downloadLabel)}" ${isStreaming ? "disabled" : ""}>
|
|
3117
|
+
${contextConfig().getIcon("download")}
|
|
3105
3118
|
</button>
|
|
3106
3119
|
</div>
|
|
3107
3120
|
</header>
|
|
3108
|
-
<nav class="aparte-art-card__tabs" role="tablist">
|
|
3109
|
-
<button type="button" role="tab"
|
|
3110
|
-
${previewable ? `<button type="button" role="tab"
|
|
3121
|
+
<nav class="aparte-tabs aparte-tabs--underline aparte-art-card__tabs" role="tablist">
|
|
3122
|
+
<button type="button" class="aparte-tabs__tab" role="tab" id="aparte-art-${cardId}-tab-code" aria-controls="aparte-art-${cardId}-pane-code" aria-selected="true" tabindex="0" data-tab-target="code">${escapeHtml(codeLabel)}</button>
|
|
3123
|
+
${previewable ? `<button type="button" class="aparte-tabs__tab" role="tab" id="aparte-art-${cardId}-tab-preview" aria-controls="aparte-art-${cardId}-pane-preview" aria-selected="false" tabindex="-1" data-tab-target="preview" ${isStreaming ? "disabled" : ""}>${escapeHtml(previewLabel)}</button>` : ""}
|
|
3111
3124
|
</nav>
|
|
3112
3125
|
<div class="aparte-art-card__body">
|
|
3113
|
-
<div class="aparte-art-card__pane" data-pane="code">
|
|
3126
|
+
<div class="aparte-art-card__pane" role="tabpanel" id="aparte-art-${cardId}-pane-code" aria-labelledby="aparte-art-${cardId}-tab-code" tabindex="0" data-pane="code">
|
|
3114
3127
|
<div class="aparte-code-content-wrapper">
|
|
3115
3128
|
<pre><code class="language-${escapeHtml(displayLang)}">${escapeHtml(cleanContent)}</code></pre>
|
|
3116
3129
|
</div>
|
|
3117
3130
|
</div>
|
|
3118
3131
|
${previewable ? `
|
|
3119
|
-
<div class="aparte-art-card__pane" data-pane="preview">
|
|
3132
|
+
<div class="aparte-art-card__pane" role="tabpanel" id="aparte-art-${cardId}-pane-preview" aria-labelledby="aparte-art-${cardId}-tab-preview" tabindex="0" data-pane="preview">
|
|
3120
3133
|
<div class="aparte-art-card__pending">${escapeHtml(cfg.t("previewPending"))}</div>
|
|
3121
3134
|
</div>
|
|
3122
3135
|
` : ""}
|
|
@@ -3171,17 +3184,38 @@ const artifactRenderer = {
|
|
|
3171
3184
|
}).catch(() => {
|
|
3172
3185
|
});
|
|
3173
3186
|
}
|
|
3174
|
-
element.querySelectorAll("[data-tab-target]")
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3187
|
+
const tabs = [...element.querySelectorAll("[data-tab-target]")];
|
|
3188
|
+
const select = (btn, focus) => {
|
|
3189
|
+
const target = btn.getAttribute("data-tab-target");
|
|
3190
|
+
if (!target || btn.disabled) return;
|
|
3191
|
+
if (target === "preview") mountPreviewFrame(element, segment);
|
|
3192
|
+
element.setAttribute("data-tab", target);
|
|
3193
|
+
for (const b of tabs) {
|
|
3194
|
+
const on = b === btn;
|
|
3195
|
+
b.setAttribute("aria-selected", on ? "true" : "false");
|
|
3196
|
+
b.tabIndex = on ? 0 : -1;
|
|
3197
|
+
}
|
|
3198
|
+
if (focus) btn.focus();
|
|
3199
|
+
};
|
|
3200
|
+
for (const [i, btn] of tabs.entries()) {
|
|
3201
|
+
btn.addEventListener("click", () => select(btn, false));
|
|
3202
|
+
btn.addEventListener("keydown", (e) => {
|
|
3203
|
+
const step = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
|
|
3204
|
+
let next = -1;
|
|
3205
|
+
if (step) next = (i + step + tabs.length) % tabs.length;
|
|
3206
|
+
else if (e.key === "Home") next = 0;
|
|
3207
|
+
else if (e.key === "End") next = tabs.length - 1;
|
|
3208
|
+
if (next < 0) return;
|
|
3209
|
+
e.preventDefault();
|
|
3210
|
+
for (let n = 0; n < tabs.length; n++) {
|
|
3211
|
+
const candidate = tabs[(next + n * (step || 1) + tabs.length) % tabs.length];
|
|
3212
|
+
if (candidate && !candidate.disabled) {
|
|
3213
|
+
select(candidate, true);
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3183
3217
|
});
|
|
3184
|
-
}
|
|
3218
|
+
}
|
|
3185
3219
|
const copyBtn = element.querySelector('[data-action="copy"]');
|
|
3186
3220
|
if (copyBtn) {
|
|
3187
3221
|
copyBtn.addEventListener("click", () => {
|
|
@@ -3266,7 +3300,7 @@ const artifactRenderer = {
|
|
|
3266
3300
|
});
|
|
3267
3301
|
}
|
|
3268
3302
|
},
|
|
3269
|
-
// CSS lives in styles/
|
|
3303
|
+
// CSS lives in styles/segment/ — see that folder for why a
|
|
3270
3304
|
// built-in's rules belong there. `getStyles` stays for a CONSUMER's renderer, which
|
|
3271
3305
|
// has no other way onto the page.
|
|
3272
3306
|
getStyles: () => ""
|
|
@@ -4120,6 +4154,7 @@ class AparteConversationController {
|
|
|
4120
4154
|
function cssEscape(value) {
|
|
4121
4155
|
return value.replace(/["\\]/g, "\\$&");
|
|
4122
4156
|
}
|
|
4157
|
+
const MAX_SIBLING_RETRIES = 6;
|
|
4123
4158
|
class AparteChatHost {
|
|
4124
4159
|
binding;
|
|
4125
4160
|
options;
|
|
@@ -4129,6 +4164,8 @@ class AparteChatHost {
|
|
|
4129
4164
|
_renderedIds = /* @__PURE__ */ new Set();
|
|
4130
4165
|
/** Sibling info from the last path-change, applied after the next render. */
|
|
4131
4166
|
_pendingSiblings;
|
|
4167
|
+
/** How many render passes the sibling write has waited for — see `_applyPendingSiblings`. */
|
|
4168
|
+
_siblingRetries = 0;
|
|
4132
4169
|
/** Abort handle for an in-flight {@link streamTokens} loop. */
|
|
4133
4170
|
_streamAbort;
|
|
4134
4171
|
/** Segment chunks written to the bubble, awaiting one coalesced state sync. */
|
|
@@ -4630,17 +4667,48 @@ class AparteChatHost {
|
|
|
4630
4667
|
}
|
|
4631
4668
|
}
|
|
4632
4669
|
}
|
|
4670
|
+
/**
|
|
4671
|
+
* Write the branch counts onto their bubbles — and RESCHEDULE for the ones that are
|
|
4672
|
+
* not on the page yet, instead of dropping them.
|
|
4673
|
+
*
|
|
4674
|
+
* This used to `continue` past a missing bubble and then clear `_pendingSiblings`
|
|
4675
|
+
* unconditionally, which lost the fork for good. That is the "1 / 1" defect: the
|
|
4676
|
+
* bubble kept its default of one sibling, the picker hid itself, and the other version
|
|
4677
|
+
* became unreachable for the life of the page.
|
|
4678
|
+
*
|
|
4679
|
+
* It needed a framework that renders LATE, which is every framework here. React's
|
|
4680
|
+
* `afterRender` is `requestAnimationFrame(() => cb())` — a bet that the next paint
|
|
4681
|
+
* lands after React's commit, and it does not always; the repo has lost that same bet
|
|
4682
|
+
* before (`25f356b`, "a bet on rAF phase"). The fix is here rather than in the
|
|
4683
|
+
* wrapper's rAF call, because ANY binding whose `afterRender` can precede its commit
|
|
4684
|
+
* hits this, and one fix at the host covers all four.
|
|
4685
|
+
*
|
|
4686
|
+
* Bounded, because a sibling can legitimately never arrive — a message that left the
|
|
4687
|
+
* path has no bubble and never will, and an unbounded retry would hold a callback for
|
|
4688
|
+
* the page's lifetime. Six passes is far more than the one tick the race needs.
|
|
4689
|
+
*/
|
|
4633
4690
|
_applyPendingSiblings() {
|
|
4634
4691
|
if (!this._pendingSiblings) return;
|
|
4635
4692
|
const msgs = this.binding.getMessages();
|
|
4693
|
+
const unresolved = [];
|
|
4636
4694
|
for (const sib of this._pendingSiblings) {
|
|
4637
4695
|
if (sib.count <= 1) continue;
|
|
4638
4696
|
const bubble = this._bubbleById(sib.id);
|
|
4639
|
-
if (!bubble)
|
|
4697
|
+
if (!bubble) {
|
|
4698
|
+
unresolved.push(sib);
|
|
4699
|
+
continue;
|
|
4700
|
+
}
|
|
4640
4701
|
const message = msgs.find((m) => m.id === sib.id);
|
|
4641
4702
|
if (message) populateBubbleFromMessage(bubble, message, sib);
|
|
4642
4703
|
}
|
|
4643
4704
|
this._pendingSiblings = void 0;
|
|
4705
|
+
if (unresolved.length && this._siblingRetries < MAX_SIBLING_RETRIES) {
|
|
4706
|
+
this._siblingRetries += 1;
|
|
4707
|
+
this._pendingSiblings = unresolved;
|
|
4708
|
+
this.binding.afterRender(() => this._applyPendingSiblings());
|
|
4709
|
+
return;
|
|
4710
|
+
}
|
|
4711
|
+
this._siblingRetries = 0;
|
|
4644
4712
|
}
|
|
4645
4713
|
// ── internals ──────────────────────────────────────────────────────────
|
|
4646
4714
|
/** Orphan-stream guard: a stream is in flight but the last msg isn't it. */
|
|
@@ -5234,6 +5302,7 @@ function readableToAsyncIterable(stream, signal) {
|
|
|
5234
5302
|
}
|
|
5235
5303
|
};
|
|
5236
5304
|
}
|
|
5305
|
+
let steppedPanelSeq = 0;
|
|
5237
5306
|
let _relabels = null;
|
|
5238
5307
|
function onRelabel(fn) {
|
|
5239
5308
|
_relabels?.push(fn);
|
|
@@ -5244,6 +5313,18 @@ function el$1(tag, className, text) {
|
|
|
5244
5313
|
if (text != null) node.textContent = text;
|
|
5245
5314
|
return node;
|
|
5246
5315
|
}
|
|
5316
|
+
function optionBody(label, description) {
|
|
5317
|
+
const body = el$1("span", "aparte-elic-option-body");
|
|
5318
|
+
body.appendChild(el$1("span", "aparte-elic-option-title", label));
|
|
5319
|
+
if (description) body.appendChild(el$1("span", "aparte-elic-option-desc", description));
|
|
5320
|
+
return body;
|
|
5321
|
+
}
|
|
5322
|
+
function commandOption(label, description, recommended) {
|
|
5323
|
+
const button = el$1("button", "aparte-btn aparte-btn--block aparte-btn--surface aparte-elic-option aparte-elic-option--command" + (recommended ? " aparte-elic-option--recommended" : ""));
|
|
5324
|
+
button.type = "button";
|
|
5325
|
+
button.appendChild(optionBody(label, description));
|
|
5326
|
+
return button;
|
|
5327
|
+
}
|
|
5247
5328
|
function fieldHeader(parent, field, skipTitle) {
|
|
5248
5329
|
let titleId;
|
|
5249
5330
|
if (field.title && field.title.trim() !== skipTitle?.trim()) {
|
|
@@ -5256,61 +5337,87 @@ function fieldHeader(parent, field, skipTitle) {
|
|
|
5256
5337
|
return titleId;
|
|
5257
5338
|
}
|
|
5258
5339
|
function labelGroup(list, opts) {
|
|
5259
|
-
list.setAttribute("role", opts.multiple ? "group" : "radiogroup");
|
|
5340
|
+
list.setAttribute("role", opts.multiple || opts.commands ? "group" : "radiogroup");
|
|
5260
5341
|
if (opts.titleId) list.setAttribute("aria-labelledby", opts.titleId);
|
|
5261
5342
|
else if (opts.fallbackLabel) list.setAttribute("aria-label", opts.fallbackLabel);
|
|
5262
5343
|
}
|
|
5263
|
-
function buildEnumField(field, onChange, fallbackLabel) {
|
|
5344
|
+
function buildEnumField(field, onChange, fallbackLabel, settle) {
|
|
5264
5345
|
const wrap = el$1("div", "aparte-elic-field aparte-elic-enum");
|
|
5265
5346
|
const titleId = fieldHeader(wrap, field, fallbackLabel);
|
|
5266
5347
|
const list = el$1("div", "aparte-elic-options");
|
|
5267
|
-
labelGroup(list, { multiple: field.multiple, titleId, fallbackLabel });
|
|
5268
5348
|
const name = `elic-${uuid()}`;
|
|
5269
5349
|
const type = field.multiple ? "checkbox" : "radio";
|
|
5350
|
+
const controlRecipe = type === "checkbox" ? "aparte-checkbox" : "aparte-radio";
|
|
5270
5351
|
const allowOther = field.allowOther ?? contextConfig().getElicitationOptions().allowOther;
|
|
5271
5352
|
const defaults = new Set(Array.isArray(field.default) ? field.default : field.default != null ? [field.default] : []);
|
|
5353
|
+
const settling = settle != null && !field.multiple && defaults.size === 0;
|
|
5354
|
+
labelGroup(list, { multiple: field.multiple, commands: settling, titleId, fallbackLabel });
|
|
5272
5355
|
const buildOption = (value, label, description, recommended) => {
|
|
5273
|
-
|
|
5274
|
-
|
|
5356
|
+
if (settling) {
|
|
5357
|
+
const button = commandOption(label, description, recommended);
|
|
5358
|
+
button.addEventListener("click", () => settle?.(value));
|
|
5359
|
+
return button;
|
|
5360
|
+
}
|
|
5361
|
+
const row = el$1("label", "aparte-field-choice aparte-elic-option" + (recommended ? " aparte-elic-option--recommended" : ""));
|
|
5362
|
+
const control = el$1("input", controlRecipe + " aparte-elic-control");
|
|
5275
5363
|
control.type = type;
|
|
5276
5364
|
control.name = name;
|
|
5277
5365
|
control.value = value;
|
|
5278
5366
|
if (defaults.has(value)) control.checked = true;
|
|
5279
|
-
|
|
5280
|
-
body.appendChild(el$1("span", "aparte-elic-option-title", label));
|
|
5281
|
-
if (description) body.appendChild(el$1("span", "aparte-elic-option-desc", description));
|
|
5282
|
-
row.append(control, body);
|
|
5367
|
+
row.append(control, optionBody(label, description));
|
|
5283
5368
|
return row;
|
|
5284
5369
|
};
|
|
5285
5370
|
for (const opt of field.options) {
|
|
5286
5371
|
list.appendChild(buildOption(opt.value, opt.label ?? opt.value, opt.description, opt.recommended));
|
|
5287
5372
|
}
|
|
5288
5373
|
let otherText = null;
|
|
5374
|
+
let otherOpen = false;
|
|
5289
5375
|
if (allowOther) {
|
|
5290
|
-
const row = el$1("label", "aparte-elic-option aparte-elic-option--other");
|
|
5291
|
-
const control = el$1("input", "aparte-elic-control");
|
|
5292
|
-
control.type = type;
|
|
5293
|
-
control.name = name;
|
|
5294
|
-
control.value = "__other__";
|
|
5295
|
-
const body = el$1("span", "aparte-elic-option-body");
|
|
5296
5376
|
const t = contextConfig();
|
|
5297
|
-
|
|
5298
|
-
body.appendChild(otherTitle);
|
|
5299
|
-
otherText = el$1("input", "aparte-elic-other-input");
|
|
5377
|
+
otherText = el$1("input", "aparte-field aparte-elic-other-input");
|
|
5300
5378
|
otherText.type = "text";
|
|
5301
5379
|
otherText.placeholder = t.t("elicitationOtherPlaceholder");
|
|
5302
5380
|
otherText.style.display = "none";
|
|
5303
5381
|
otherText.setAttribute("aria-label", t.t("elicitationOtherLabel"));
|
|
5304
5382
|
const otherInput = otherText;
|
|
5305
|
-
|
|
5306
|
-
const
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5383
|
+
if (settling) {
|
|
5384
|
+
const button = commandOption(t.t("elicitationOther"));
|
|
5385
|
+
button.classList.add("aparte-elic-option--other");
|
|
5386
|
+
button.setAttribute("aria-expanded", "false");
|
|
5387
|
+
button.addEventListener("click", () => {
|
|
5388
|
+
otherOpen = true;
|
|
5389
|
+
button.setAttribute("aria-expanded", "true");
|
|
5390
|
+
otherInput.style.display = "";
|
|
5391
|
+
otherInput.focus();
|
|
5392
|
+
onChange();
|
|
5393
|
+
});
|
|
5394
|
+
const otherTitle = button.querySelector(".aparte-elic-option-title");
|
|
5395
|
+
onRelabel(() => {
|
|
5396
|
+
const now = contextConfig();
|
|
5397
|
+
otherTitle.textContent = now.t("elicitationOther");
|
|
5398
|
+
otherInput.placeholder = now.t("elicitationOtherPlaceholder");
|
|
5399
|
+
otherInput.setAttribute("aria-label", now.t("elicitationOtherLabel"));
|
|
5400
|
+
});
|
|
5401
|
+
list.append(button, otherInput);
|
|
5402
|
+
} else {
|
|
5403
|
+
const row = el$1("label", "aparte-field-choice aparte-elic-option aparte-elic-option--other");
|
|
5404
|
+
const control = el$1("input", controlRecipe + " aparte-elic-control");
|
|
5405
|
+
control.type = type;
|
|
5406
|
+
control.name = name;
|
|
5407
|
+
control.value = "__other__";
|
|
5408
|
+
const body = el$1("span", "aparte-elic-option-body");
|
|
5409
|
+
const otherTitle = el$1("span", "aparte-elic-option-title", t.t("elicitationOther"));
|
|
5410
|
+
body.appendChild(otherTitle);
|
|
5411
|
+
onRelabel(() => {
|
|
5412
|
+
const now = contextConfig();
|
|
5413
|
+
otherTitle.textContent = now.t("elicitationOther");
|
|
5414
|
+
otherInput.placeholder = now.t("elicitationOtherPlaceholder");
|
|
5415
|
+
otherInput.setAttribute("aria-label", now.t("elicitationOtherLabel"));
|
|
5416
|
+
});
|
|
5417
|
+
body.appendChild(otherText);
|
|
5418
|
+
row.append(control, body);
|
|
5419
|
+
list.appendChild(row);
|
|
5420
|
+
}
|
|
5314
5421
|
}
|
|
5315
5422
|
wrap.appendChild(list);
|
|
5316
5423
|
const controls = () => Array.from(list.querySelectorAll(".aparte-elic-control"));
|
|
@@ -5324,8 +5431,18 @@ function buildEnumField(field, onChange, fallbackLabel) {
|
|
|
5324
5431
|
onChange();
|
|
5325
5432
|
});
|
|
5326
5433
|
otherText?.addEventListener("input", onChange);
|
|
5434
|
+
const typedOther = () => otherText?.value.trim() ?? "";
|
|
5435
|
+
if (settling) {
|
|
5436
|
+
return {
|
|
5437
|
+
el: wrap,
|
|
5438
|
+
getValue: () => typedOther(),
|
|
5439
|
+
isComplete: () => otherOpen && typedOther() !== "",
|
|
5440
|
+
focus: () => list.querySelector(".aparte-elic-option--command")?.focus(),
|
|
5441
|
+
offersSubmit: () => otherOpen
|
|
5442
|
+
};
|
|
5443
|
+
}
|
|
5327
5444
|
const getValue = () => {
|
|
5328
|
-
const otherVal = other()?.checked &&
|
|
5445
|
+
const otherVal = other()?.checked && typedOther() ? typedOther() : "";
|
|
5329
5446
|
if (field.multiple) {
|
|
5330
5447
|
const vals = controls().filter((c) => c.checked && c.value !== "__other__").map((c) => c.value);
|
|
5331
5448
|
if (otherVal) vals.push(otherVal);
|
|
@@ -5345,15 +5462,42 @@ function buildEnumField(field, onChange, fallbackLabel) {
|
|
|
5345
5462
|
focus: () => controls()[0]?.focus()
|
|
5346
5463
|
};
|
|
5347
5464
|
}
|
|
5348
|
-
function buildBooleanField(field, onChange, fallbackLabel) {
|
|
5465
|
+
function buildBooleanField(field, onChange, fallbackLabel, settle) {
|
|
5349
5466
|
const wrap = el$1("div", "aparte-elic-field aparte-elic-boolean");
|
|
5350
5467
|
const titleId = fieldHeader(wrap, field, fallbackLabel);
|
|
5351
5468
|
const list = el$1("div", "aparte-elic-options");
|
|
5352
|
-
labelGroup(list, { titleId, fallbackLabel });
|
|
5353
5469
|
const name = `elic-${uuid()}`;
|
|
5470
|
+
const settling = settle != null && field.default == null;
|
|
5471
|
+
labelGroup(list, { commands: settling, titleId, fallbackLabel });
|
|
5472
|
+
if (settling) {
|
|
5473
|
+
const t2 = contextConfig();
|
|
5474
|
+
const mkCommand = (val, label, defaulted) => {
|
|
5475
|
+
const button = commandOption(label);
|
|
5476
|
+
button.addEventListener("click", () => settle?.(val));
|
|
5477
|
+
if (defaulted) {
|
|
5478
|
+
const title = button.querySelector(".aparte-elic-option-title");
|
|
5479
|
+
onRelabel(() => {
|
|
5480
|
+
title.textContent = contextConfig().t(defaulted);
|
|
5481
|
+
});
|
|
5482
|
+
}
|
|
5483
|
+
list.appendChild(button);
|
|
5484
|
+
};
|
|
5485
|
+
mkCommand(true, field.trueLabel ?? t2.t("elicitationYes"), field.trueLabel ? void 0 : "elicitationYes");
|
|
5486
|
+
mkCommand(false, field.falseLabel ?? t2.t("elicitationNo"), field.falseLabel ? void 0 : "elicitationNo");
|
|
5487
|
+
wrap.appendChild(list);
|
|
5488
|
+
return {
|
|
5489
|
+
el: wrap,
|
|
5490
|
+
// Never read: the click settles, so nothing downstream asks this shape for
|
|
5491
|
+
// a value. `false` rather than a throw keeps the field total.
|
|
5492
|
+
getValue: () => false,
|
|
5493
|
+
isComplete: () => false,
|
|
5494
|
+
focus: () => list.querySelector(".aparte-elic-option--command")?.focus(),
|
|
5495
|
+
offersSubmit: () => false
|
|
5496
|
+
};
|
|
5497
|
+
}
|
|
5354
5498
|
const mk = (val, label, defaulted) => {
|
|
5355
|
-
const row = el$1("label", "aparte-elic-option");
|
|
5356
|
-
const control = el$1("input", "aparte-elic-control");
|
|
5499
|
+
const row = el$1("label", "aparte-field-choice aparte-elic-option");
|
|
5500
|
+
const control = el$1("input", "aparte-radio aparte-elic-control");
|
|
5357
5501
|
control.type = "radio";
|
|
5358
5502
|
control.name = name;
|
|
5359
5503
|
control.value = val;
|
|
@@ -5383,7 +5527,7 @@ function buildBooleanField(field, onChange, fallbackLabel) {
|
|
|
5383
5527
|
function buildStringField(field, onChange, fallbackLabel) {
|
|
5384
5528
|
const wrap = el$1("div", "aparte-elic-field aparte-elic-string");
|
|
5385
5529
|
fieldHeader(wrap, field, fallbackLabel);
|
|
5386
|
-
const input = field.multiline ? el$1("textarea", "aparte-elic-text") : el$1("input", "aparte-elic-text");
|
|
5530
|
+
const input = field.multiline ? el$1("textarea", "aparte-field aparte-field--textarea aparte-elic-text") : el$1("input", "aparte-field aparte-elic-text");
|
|
5387
5531
|
if (!field.multiline) input.type = "text";
|
|
5388
5532
|
if (field.placeholder) input.setAttribute("placeholder", field.placeholder);
|
|
5389
5533
|
if (field.default) input.value = field.default;
|
|
@@ -5411,7 +5555,7 @@ function buildStringField(field, onChange, fallbackLabel) {
|
|
|
5411
5555
|
focus: () => input.focus()
|
|
5412
5556
|
};
|
|
5413
5557
|
}
|
|
5414
|
-
function buildField(field, onChange, fallbackLabel, key) {
|
|
5558
|
+
function buildField(field, onChange, fallbackLabel, key, settle) {
|
|
5415
5559
|
const custom = contextConfig().getElicitationFieldRenderer()?.(field, { key, notifyChange: onChange });
|
|
5416
5560
|
if (custom) {
|
|
5417
5561
|
return {
|
|
@@ -5423,9 +5567,10 @@ function buildField(field, onChange, fallbackLabel, key) {
|
|
|
5423
5567
|
}
|
|
5424
5568
|
switch (field.type) {
|
|
5425
5569
|
case "enum":
|
|
5426
|
-
return buildEnumField(field, onChange, fallbackLabel);
|
|
5570
|
+
return buildEnumField(field, onChange, fallbackLabel, settle);
|
|
5427
5571
|
case "boolean":
|
|
5428
|
-
return buildBooleanField(field, onChange, fallbackLabel);
|
|
5572
|
+
return buildBooleanField(field, onChange, fallbackLabel, settle);
|
|
5573
|
+
// No `settle`: text is written, not chosen, so it always needs a commit.
|
|
5429
5574
|
case "string":
|
|
5430
5575
|
return buildStringField(field, onChange, fallbackLabel);
|
|
5431
5576
|
}
|
|
@@ -5447,6 +5592,16 @@ function buildPanel(message, schema, onChange) {
|
|
|
5447
5592
|
const panel = el$1("div", "aparte-elic-panel");
|
|
5448
5593
|
const asked = typeof message === "function" ? message() : message;
|
|
5449
5594
|
if (asked) panel.appendChild(el$1("p", "aparte-elic-message", asked));
|
|
5595
|
+
let settleCb = null;
|
|
5596
|
+
let settled = false;
|
|
5597
|
+
const settle = (content) => {
|
|
5598
|
+
if (settled) return;
|
|
5599
|
+
settled = true;
|
|
5600
|
+
settleCb?.(content);
|
|
5601
|
+
};
|
|
5602
|
+
const onSettle = (cb) => {
|
|
5603
|
+
settleCb = cb;
|
|
5604
|
+
};
|
|
5450
5605
|
const body = el$1("div", "aparte-elic-body");
|
|
5451
5606
|
panel.appendChild(body);
|
|
5452
5607
|
const dismiss = el$1("div", "aparte-elic-dismiss");
|
|
@@ -5487,15 +5642,20 @@ function buildPanel(message, schema, onChange) {
|
|
|
5487
5642
|
mode: () => "submit",
|
|
5488
5643
|
canProceed: isComplete,
|
|
5489
5644
|
proceed: () => {
|
|
5490
|
-
}
|
|
5645
|
+
},
|
|
5646
|
+
// A form never settles from inside: its questions are collected and
|
|
5647
|
+
// submitted together, and answering the last one on the click would be
|
|
5648
|
+
// WCAG's F36 word for word. Registered so the shape is total; never fired.
|
|
5649
|
+
onSettle
|
|
5491
5650
|
};
|
|
5492
5651
|
if (fields.length < 2 || cfg.getElicitationOptions().layout !== "stepped") return api;
|
|
5493
5652
|
panel.classList.add("aparte-elic-panel--stepped");
|
|
5494
5653
|
let current = 0;
|
|
5495
|
-
const
|
|
5654
|
+
const scope = `aparte-elic-${++steppedPanelSeq}`;
|
|
5655
|
+
const steps = el$1("div", "aparte-tabs aparte-tabs--underline aparte-elic-steps");
|
|
5496
5656
|
steps.setAttribute("role", "tablist");
|
|
5497
5657
|
const chips = fields.map((f, i) => {
|
|
5498
|
-
const chip = el$1("button", "aparte-elic-step");
|
|
5658
|
+
const chip = el$1("button", "aparte-tabs__tab aparte-elic-step");
|
|
5499
5659
|
chip.type = "button";
|
|
5500
5660
|
chip.setAttribute("role", "tab");
|
|
5501
5661
|
chip.textContent = f.header?.trim() || String(i + 1);
|
|
@@ -5503,6 +5663,22 @@ function buildPanel(message, schema, onChange) {
|
|
|
5503
5663
|
show(i);
|
|
5504
5664
|
f.field.focus();
|
|
5505
5665
|
});
|
|
5666
|
+
chip.id = `${scope}-tab-${i}`;
|
|
5667
|
+
f.field.el.id = `${scope}-panel-${i}`;
|
|
5668
|
+
f.field.el.setAttribute("role", "tabpanel");
|
|
5669
|
+
f.field.el.setAttribute("aria-labelledby", chip.id);
|
|
5670
|
+
chip.setAttribute("aria-controls", f.field.el.id);
|
|
5671
|
+
chip.addEventListener("keydown", (e) => {
|
|
5672
|
+
const step = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
|
|
5673
|
+
let next = -1;
|
|
5674
|
+
if (step) next = (i + step + fields.length) % fields.length;
|
|
5675
|
+
else if (e.key === "Home") next = 0;
|
|
5676
|
+
else if (e.key === "End") next = fields.length - 1;
|
|
5677
|
+
if (next < 0) return;
|
|
5678
|
+
e.preventDefault();
|
|
5679
|
+
show(next);
|
|
5680
|
+
chips[next]?.focus();
|
|
5681
|
+
});
|
|
5506
5682
|
steps.appendChild(chip);
|
|
5507
5683
|
return chip;
|
|
5508
5684
|
});
|
|
@@ -5510,6 +5686,7 @@ function buildPanel(message, schema, onChange) {
|
|
|
5510
5686
|
syncNav = () => {
|
|
5511
5687
|
chips.forEach((chip, i) => {
|
|
5512
5688
|
chip.setAttribute("aria-selected", String(i === current));
|
|
5689
|
+
chip.tabIndex = i === current ? 0 : -1;
|
|
5513
5690
|
chip.toggleAttribute("data-answered", fields[i].field.isComplete());
|
|
5514
5691
|
});
|
|
5515
5692
|
};
|
|
@@ -5530,7 +5707,7 @@ function buildPanel(message, schema, onChange) {
|
|
|
5530
5707
|
}
|
|
5531
5708
|
};
|
|
5532
5709
|
}
|
|
5533
|
-
const field = buildField(schema, onChange, asked);
|
|
5710
|
+
const field = buildField(schema, onChange, asked, void 0, settle);
|
|
5534
5711
|
body.appendChild(field.el);
|
|
5535
5712
|
return {
|
|
5536
5713
|
el: panel,
|
|
@@ -5538,10 +5715,11 @@ function buildPanel(message, schema, onChange) {
|
|
|
5538
5715
|
getContent: () => field.getValue(),
|
|
5539
5716
|
isComplete: () => field.isComplete(),
|
|
5540
5717
|
focus: () => field.focus(),
|
|
5541
|
-
mode: () => "submit",
|
|
5718
|
+
mode: () => field.offersSubmit?.() ?? true ? "submit" : "none",
|
|
5542
5719
|
canProceed: () => field.isComplete(),
|
|
5543
5720
|
proceed: () => {
|
|
5544
|
-
}
|
|
5721
|
+
},
|
|
5722
|
+
onSettle
|
|
5545
5723
|
};
|
|
5546
5724
|
}
|
|
5547
5725
|
function el(tag, className, text) {
|
|
@@ -5571,7 +5749,8 @@ function buildApprovalPanel(message, options, onChange) {
|
|
|
5571
5749
|
const labelled = [];
|
|
5572
5750
|
const textOf = (option) => typeof option.label === "function" ? option.label() : option.label;
|
|
5573
5751
|
for (const option of options) {
|
|
5574
|
-
const
|
|
5752
|
+
const tone = option.tone ?? "affirm";
|
|
5753
|
+
const button = el("button", `aparte-btn aparte-btn--block aparte-btn--surface aparte-approval-option aparte-approval-option--${tone}`);
|
|
5575
5754
|
button.type = "button";
|
|
5576
5755
|
button.textContent = textOf(option);
|
|
5577
5756
|
button.addEventListener("click", () => fire({ option: option.value }));
|
|
@@ -5675,6 +5854,27 @@ class AparteClient {
|
|
|
5675
5854
|
}
|
|
5676
5855
|
this._setupListeners();
|
|
5677
5856
|
}
|
|
5857
|
+
/**
|
|
5858
|
+
* What every fresh user action does before it starts a turn.
|
|
5859
|
+
*
|
|
5860
|
+
* Clearing the abort flag has to happen at the START of the action rather than inside
|
|
5861
|
+
* `_streamTurn`, which runs after the auth and file-reading awaits — a reset there
|
|
5862
|
+
* erased an abort that arrived while the user was still waiting for a large attachment
|
|
5863
|
+
* to be read.
|
|
5864
|
+
*
|
|
5865
|
+
* Cancelling the previous turn's tool controllers used to happen only on `aparte-send`.
|
|
5866
|
+
* Retry and edit reset the flag and left them running, so a tool handler from the turn
|
|
5867
|
+
* being superseded stayed alive — with its timeout still counting — while its reply was
|
|
5868
|
+
* already off the active path. A cold audit found the asymmetry; three callers, one
|
|
5869
|
+
* rule, no third copy to fall out of step.
|
|
5870
|
+
*/
|
|
5871
|
+
_beginUserTurn() {
|
|
5872
|
+
this._isAborted = false;
|
|
5873
|
+
for (const controller of this._activeToolControllers) {
|
|
5874
|
+
controller.abort();
|
|
5875
|
+
}
|
|
5876
|
+
this._activeToolControllers.clear();
|
|
5877
|
+
}
|
|
5678
5878
|
/**
|
|
5679
5879
|
* Sets up the event listeners.
|
|
5680
5880
|
* This is called once in the constructor.
|
|
@@ -5685,11 +5885,7 @@ class AparteClient {
|
|
|
5685
5885
|
const event = e;
|
|
5686
5886
|
if (event.type !== "aparte-send") return;
|
|
5687
5887
|
if (!this._isForThisInstance(event)) return;
|
|
5688
|
-
this.
|
|
5689
|
-
for (const controller of this._activeToolControllers) {
|
|
5690
|
-
controller.abort();
|
|
5691
|
-
}
|
|
5692
|
-
this._activeToolControllers.clear();
|
|
5888
|
+
this._beginUserTurn();
|
|
5693
5889
|
void this._handleSend(event);
|
|
5694
5890
|
};
|
|
5695
5891
|
}
|
|
@@ -6039,7 +6235,7 @@ ${summary}`,
|
|
|
6039
6235
|
}
|
|
6040
6236
|
async _handleRetry(event) {
|
|
6041
6237
|
const { messageId, targetId } = event.detail ?? {};
|
|
6042
|
-
this.
|
|
6238
|
+
this._beginUserTurn();
|
|
6043
6239
|
if (!messageId) return;
|
|
6044
6240
|
const targetElement = this._resolveTarget(targetId);
|
|
6045
6241
|
if (!targetElement) {
|
|
@@ -6066,9 +6262,7 @@ ${summary}`,
|
|
|
6066
6262
|
if (!provider) return;
|
|
6067
6263
|
const authConfig = await this._resolveAuth(providerId);
|
|
6068
6264
|
const chatMessages = this._messagesToChatMessages(historyMessages);
|
|
6069
|
-
const systemMessages =
|
|
6070
|
-
const userSystemPrompt = this._config.resolveSystemPrompt();
|
|
6071
|
-
if (userSystemPrompt) systemMessages.push({ role: "system", content: userSystemPrompt });
|
|
6265
|
+
const systemMessages = this._systemMessages();
|
|
6072
6266
|
await this._streamTurn(
|
|
6073
6267
|
targetElement,
|
|
6074
6268
|
newMessageId,
|
|
@@ -6079,13 +6273,30 @@ ${summary}`,
|
|
|
6079
6273
|
{ temperature: 0.4 }
|
|
6080
6274
|
);
|
|
6081
6275
|
}
|
|
6276
|
+
/**
|
|
6277
|
+
* The system messages a turn opens with: the app's own template, then the registered
|
|
6278
|
+
* tools' `systemPrompt`s.
|
|
6279
|
+
*
|
|
6280
|
+
* A helper rather than three copies. The two lines it replaces were written out at each
|
|
6281
|
+
* of the three turn entry points (send, retry, edit), which is exactly how the tool half
|
|
6282
|
+
* would have been added to two of them and forgotten in the third — the same shape as
|
|
6283
|
+
* the abort-flag asymmetry `_beginUserTurn` exists to prevent.
|
|
6284
|
+
*/
|
|
6285
|
+
_systemMessages() {
|
|
6286
|
+
const messages = [];
|
|
6287
|
+
const appPrompt = this._config.resolveSystemPrompt();
|
|
6288
|
+
if (appPrompt) messages.push({ role: "system", content: appPrompt });
|
|
6289
|
+
const toolPrompts = this._config.resolveToolSystemPrompts();
|
|
6290
|
+
if (toolPrompts) messages.push({ role: "system", content: toolPrompts });
|
|
6291
|
+
return messages;
|
|
6292
|
+
}
|
|
6082
6293
|
/**
|
|
6083
6294
|
* Handle aparte-edit — update the user message in place, truncate all subsequent
|
|
6084
6295
|
* messages, then re-stream a fresh assistant response.
|
|
6085
6296
|
*/
|
|
6086
6297
|
async _handleEdit(event) {
|
|
6087
6298
|
const { messageId, content: newContent, targetId } = event.detail ?? {};
|
|
6088
|
-
this.
|
|
6299
|
+
this._beginUserTurn();
|
|
6089
6300
|
if (!messageId || newContent === void 0) return;
|
|
6090
6301
|
const targetElement = this._resolveTarget(targetId);
|
|
6091
6302
|
if (!targetElement) {
|
|
@@ -6105,9 +6316,7 @@ ${summary}`,
|
|
|
6105
6316
|
}
|
|
6106
6317
|
const historyMessages = editIdx >= 0 ? allMessages.slice(0, editIdx + 1) : allMessages;
|
|
6107
6318
|
const chatMessages = this._messagesToChatMessages(historyMessages);
|
|
6108
|
-
const systemMessages =
|
|
6109
|
-
const userSystemPrompt = this._config.resolveSystemPrompt();
|
|
6110
|
-
if (userSystemPrompt) systemMessages.push({ role: "system", content: userSystemPrompt });
|
|
6319
|
+
const systemMessages = this._systemMessages();
|
|
6111
6320
|
const config = this._config.getModelConfig();
|
|
6112
6321
|
const providerId = config.defaultProvider;
|
|
6113
6322
|
if (!providerId) return;
|
|
@@ -6276,11 +6485,7 @@ ${summary}`,
|
|
|
6276
6485
|
* Build the initial messages array, prepending system prompts and conversation history.
|
|
6277
6486
|
*/
|
|
6278
6487
|
_buildMessages(userContent, target, parts) {
|
|
6279
|
-
const messages =
|
|
6280
|
-
const userSystemPrompt = this._config.resolveSystemPrompt();
|
|
6281
|
-
if (userSystemPrompt) {
|
|
6282
|
-
messages.push({ role: "system", content: userSystemPrompt });
|
|
6283
|
-
}
|
|
6488
|
+
const messages = this._systemMessages();
|
|
6284
6489
|
const historyOption = this.options.history ?? "viewport";
|
|
6285
6490
|
const viewportMessages = target?.getMessages?.() ?? [];
|
|
6286
6491
|
if (historyOption === "viewport") {
|
|
@@ -6736,6 +6941,21 @@ ${content}
|
|
|
6736
6941
|
...wholeParser.parse(response).segments,
|
|
6737
6942
|
...wholeParser.finalize()
|
|
6738
6943
|
];
|
|
6944
|
+
const stringHint = baseRequest._meta?.artifactHint;
|
|
6945
|
+
if (stringHint) {
|
|
6946
|
+
const codeIdx = parsed.findIndex((s) => s.type === "code");
|
|
6947
|
+
if (codeIdx !== -1) {
|
|
6948
|
+
const codeSeg = parsed[codeIdx];
|
|
6949
|
+
parsed[codeIdx] = {
|
|
6950
|
+
id: codeSeg.id,
|
|
6951
|
+
type: "artifact",
|
|
6952
|
+
mimeType: stringHint.mimeType,
|
|
6953
|
+
artifactType: stringHint.kind,
|
|
6954
|
+
title: codeSeg.filename ?? stringHint.kind,
|
|
6955
|
+
content: codeSeg.content
|
|
6956
|
+
};
|
|
6957
|
+
}
|
|
6958
|
+
}
|
|
6739
6959
|
if (parsed.length > 0) {
|
|
6740
6960
|
for (const segment of parsed) targetElement.addSegment?.(segment);
|
|
6741
6961
|
this._updateMessage(targetElement, messageId, { status: "completed" });
|
|
@@ -7072,7 +7292,7 @@ ${content}
|
|
|
7072
7292
|
details: error.code
|
|
7073
7293
|
};
|
|
7074
7294
|
if (target.updateMessage) {
|
|
7075
|
-
const rendered = target.getMessages?.()?.find((m) => m.id === messageId)?.segments ?? [];
|
|
7295
|
+
const rendered = target.getMessage?.(messageId)?.segments ?? target.getMessages?.()?.find((m) => m.id === messageId)?.segments ?? [];
|
|
7076
7296
|
target.updateMessage(messageId, {
|
|
7077
7297
|
status: "error",
|
|
7078
7298
|
segments: [...rendered, errorSegment]
|
|
@@ -7429,13 +7649,14 @@ export {
|
|
|
7429
7649
|
unregisterSegmentRenderer as a5,
|
|
7430
7650
|
uuid as a6,
|
|
7431
7651
|
mergeSegmentUpdate as a7,
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7652
|
+
writeStreamedMarkdown as a8,
|
|
7653
|
+
stampSegmentActivity as a9,
|
|
7654
|
+
stampSegmentOnInsert as aa,
|
|
7655
|
+
stampSegmentOnUpdate as ab,
|
|
7656
|
+
renumberSegments as ac,
|
|
7657
|
+
openSegmentIds as ad,
|
|
7658
|
+
isTerminalStatus as ae,
|
|
7659
|
+
adoptMessageSegments as af,
|
|
7439
7660
|
APARTE_DEFAULT_BUBBLE_ACTIONS as b,
|
|
7440
7661
|
APARTE_DEFAULT_HOST_HANDLERS as c,
|
|
7441
7662
|
APARTE_DEFAULT_ICON_FALLBACKS as d,
|
|
@@ -7462,4 +7683,4 @@ export {
|
|
|
7462
7683
|
buildElicitationPanel as y,
|
|
7463
7684
|
collectRendererStyles as z
|
|
7464
7685
|
};
|
|
7465
|
-
//# sourceMappingURL=is-awaiting-reply-
|
|
7686
|
+
//# sourceMappingURL=is-awaiting-reply-DrO_oR32.js.map
|