@aparte/core 0.19.1 → 0.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/client/aparte-client.d.ts +10 -2
- package/dist/client/aparte-client.d.ts.map +1 -1
- package/dist/client/stream-adapter.d.ts.map +1 -1
- package/dist/components/bubble/aparte-chat-bubble.d.ts +1 -1
- package/dist/components/bubble/bubble-sync.d.ts +1 -1
- package/dist/components/composer/aparte-composer-action.d.ts +1 -1
- package/dist/components/composer/aparte-composer-add-attachment.d.ts +1 -1
- package/dist/components/composer/aparte-composer-cancel.d.ts +1 -1
- package/dist/components/composer/aparte-composer-input.d.ts +3 -2
- package/dist/components/composer/aparte-composer-input.d.ts.map +1 -1
- package/dist/components/composer/aparte-composer-send.d.ts +1 -1
- package/dist/components/composer/aparte-composer.d.ts +2 -0
- package/dist/components/composer/aparte-composer.d.ts.map +1 -1
- package/dist/components/context/aparte-context.d.ts +5 -4
- package/dist/components/context/aparte-context.d.ts.map +1 -1
- package/dist/components/conversation-list/aparte-conversation-list.d.ts +2 -0
- package/dist/components/conversation-list/aparte-conversation-list.d.ts.map +1 -1
- package/dist/components/elicitation/aparte-elicitation.d.ts.map +1 -1
- package/dist/components/status/aparte-chat-status.d.ts +5 -3
- package/dist/components/status/aparte-chat-status.d.ts.map +1 -1
- package/dist/components/suggestions/aparte-suggestions.d.ts.map +1 -1
- package/dist/components/viewport/aparte-chat-viewport.d.ts +7 -0
- package/dist/components/viewport/aparte-chat-viewport.d.ts.map +1 -1
- package/dist/config/aparte-config.d.ts +86 -79
- package/dist/config/aparte-config.d.ts.map +1 -1
- package/dist/config/locale.d.ts +4 -0
- package/dist/config/locale.d.ts.map +1 -1
- package/dist/conversations/conversation-manager.d.ts +3 -3
- package/dist/conversations/conversation-manager.d.ts.map +1 -1
- package/dist/conversations/index.d.ts +1 -1
- package/dist/conversations/index.d.ts.map +1 -1
- package/dist/custom-elements.json +11129 -10952
- package/dist/{dialog-triggers-CEazY76J.js → dialog-triggers-ChnPFG9M.js} +113 -90
- package/dist/dialog-triggers-ChnPFG9M.js.map +1 -0
- package/dist/elicitation/approval-panel.d.ts.map +1 -1
- package/dist/generated/element-attributes.d.ts +5 -5
- package/dist/generated/element-attributes.d.ts.map +1 -1
- package/dist/host/aparte-chat-host.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +96 -13
- 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/parsers/aparte-stream-parser.d.ts.map +1 -1
- package/dist/renderers/segments/thinking.d.ts.map +1 -1
- package/dist/transport/agent-handler.d.ts +2 -0
- package/dist/transport/agent-handler.d.ts.map +1 -1
- package/dist/transport/backend-transport.d.ts +1 -1
- package/dist/types/imperative-api.d.ts +3 -1
- package/dist/types/imperative-api.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/segments.d.ts +8 -0
- package/dist/types/segments.d.ts.map +1 -1
- package/dist/types/tools.d.ts +1 -1
- package/package.json +3 -3
- package/dist/dialog-triggers-CEazY76J.js.map +0 -1
|
@@ -84,7 +84,7 @@ const APARTE_DEFAULT_LOCALE = {
|
|
|
84
84
|
copy: "Copy",
|
|
85
85
|
copied: "Copied!",
|
|
86
86
|
retry: "Retry",
|
|
87
|
-
thinking: "Thinking
|
|
87
|
+
thinking: "Thinking",
|
|
88
88
|
elicitationOther: "Other…",
|
|
89
89
|
elicitationOtherPlaceholder: "Type your answer…",
|
|
90
90
|
elicitationOtherLabel: "Custom answer",
|
|
@@ -124,7 +124,7 @@ const APARTE_DEFAULT_LOCALE = {
|
|
|
124
124
|
code: "Code",
|
|
125
125
|
generating: "Generating…",
|
|
126
126
|
rebuildingPreview: "Rebuilding preview…",
|
|
127
|
-
typing: "Typing
|
|
127
|
+
typing: "Typing",
|
|
128
128
|
error: "Error",
|
|
129
129
|
running: "Running...",
|
|
130
130
|
run: "Run",
|
|
@@ -148,6 +148,7 @@ const APARTE_DEFAULT_LOCALE = {
|
|
|
148
148
|
messageInfo: "Details",
|
|
149
149
|
newChat: "New Chat",
|
|
150
150
|
loadingConversations: "Loading conversations",
|
|
151
|
+
conversationList: "Conversations",
|
|
151
152
|
loadingConversation: "Loading the conversation",
|
|
152
153
|
deleteConversation: "Delete",
|
|
153
154
|
archiveConversation: "Archive",
|
|
@@ -698,8 +699,7 @@ class AparteStreamParser {
|
|
|
698
699
|
return {
|
|
699
700
|
id: this._options.autoGenerateIds ? this._generateId() : "",
|
|
700
701
|
type: "thinking",
|
|
701
|
-
content
|
|
702
|
-
label: "Thinking"
|
|
702
|
+
content
|
|
703
703
|
};
|
|
704
704
|
}
|
|
705
705
|
_isPartialMatch(buffer, pattern) {
|
|
@@ -1062,8 +1062,7 @@ function createStreamAdapter(opts) {
|
|
|
1062
1062
|
id: `think-${uuid()}`,
|
|
1063
1063
|
type: "thinking",
|
|
1064
1064
|
content: thinkingContent,
|
|
1065
|
-
collapsed: true
|
|
1066
|
-
label: "Thinking"
|
|
1065
|
+
collapsed: true
|
|
1067
1066
|
};
|
|
1068
1067
|
thinkingId = seg.id;
|
|
1069
1068
|
streaming.add(seg.id);
|
|
@@ -2054,7 +2053,7 @@ class AparteConfig {
|
|
|
2054
2053
|
* @param sanitizer A sanitizer function (e.g. a DOMPurify wrapper) for
|
|
2055
2054
|
* hardened coverage, or `null` to DISABLE sanitization. Disabling exposes
|
|
2056
2055
|
* you to XSS from LLM-authored content — only do so for content you fully
|
|
2057
|
-
* trust and have already sanitized upstream.
|
|
2056
|
+
* trust and have already sanitized upstream. `null` prints a console warning, once.
|
|
2058
2057
|
* @example aparteGlobalConfig.setHtmlSanitizer((html) => DOMPurify.sanitize(html));
|
|
2059
2058
|
*/
|
|
2060
2059
|
setHtmlSanitizer(sanitizer) {
|
|
@@ -2275,26 +2274,16 @@ class AparteConfig {
|
|
|
2275
2274
|
this._keyProvider = provider;
|
|
2276
2275
|
}
|
|
2277
2276
|
/**
|
|
2278
|
-
* Add a key source
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
2281
|
-
*
|
|
2282
|
-
*
|
|
2283
|
-
*
|
|
2284
|
-
*
|
|
2285
|
-
*
|
|
2286
|
-
*
|
|
2287
|
-
*
|
|
2288
|
-
* The direction matters (decision #9): the client registers a source ON the
|
|
2289
|
-
* config, so the capability is never hostage to the client — a page that only
|
|
2290
|
-
* ever calls `setKeyProvider` is unchanged, and a page that constructs no
|
|
2291
|
-
* client at all still lists models.
|
|
2292
|
-
*
|
|
2293
|
-
* Every call is its own source, even when the same function is handed in
|
|
2294
|
-
* twice: two panes sharing one module-level resolver are two mounts, and the
|
|
2295
|
-
* first one to unmount must not take the second one's key with it. A `Set`
|
|
2296
|
-
* keyed by the function itself made them one entry, which either teardown
|
|
2297
|
-
* removed.
|
|
2277
|
+
* Add a key source beside the one {@link AparteConfig.setKeyProvider | setKeyProvider()}
|
|
2278
|
+
* holds, and return the function that removes it. The sources are asked in the order they
|
|
2279
|
+
* were added, then `setKeyProvider`'s, and the first answer wins. `AparteClient`'s
|
|
2280
|
+
* `keyResolver` is registered this way, so the model selector can list a cloud provider's
|
|
2281
|
+
* models too. Each call is a source of its own, even with the same function.
|
|
2282
|
+
* @example
|
|
2283
|
+
* const release = aparteGlobalConfig.registerKeyProvider((providerId) =>
|
|
2284
|
+
* providerId === 'openrouter' ? localStorage.getItem('openrouter-key') : undefined);
|
|
2285
|
+
* // when what owns the key goes away:
|
|
2286
|
+
* release();
|
|
2298
2287
|
*/
|
|
2299
2288
|
registerKeyProvider(provider) {
|
|
2300
2289
|
const entry = (providerId) => provider(providerId);
|
|
@@ -2330,9 +2319,10 @@ class AparteConfig {
|
|
|
2330
2319
|
return void 0;
|
|
2331
2320
|
}
|
|
2332
2321
|
/**
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
2322
|
+
* Fetch a provider's models, with the key and endpoint its key sources answer, so a
|
|
2323
|
+
* component that lists models never handles a key. The list is kept, and
|
|
2324
|
+
* `getCurrentModel()` reads the selected model's capabilities from it. Answers `[]` for an
|
|
2325
|
+
* unknown provider, one with no `fetchModels`, or a fetch that fails (with a console warning).
|
|
2336
2326
|
*/
|
|
2337
2327
|
async refreshProviderModels(providerId) {
|
|
2338
2328
|
const provider = this._aiProviders.get(providerId);
|
|
@@ -2351,7 +2341,13 @@ class AparteConfig {
|
|
|
2351
2341
|
// AI Provider Management (BYORK - Bring Your Own Key)
|
|
2352
2342
|
// ─────────────────────────────────────────────────────────────────────────
|
|
2353
2343
|
/**
|
|
2354
|
-
* Register one or more AI providers (e.g., OpenRouter, Gemini, Anthropic)
|
|
2344
|
+
* Register one or more AI providers (e.g., OpenRouter, Gemini, Anthropic).
|
|
2345
|
+
*
|
|
2346
|
+
* When this leaves exactly one provider registered, offering exactly one model it knows
|
|
2347
|
+
* without a request (a scripted or an in-browser provider), and no model is selected,
|
|
2348
|
+
* that model is selected. A provider that lists its models over the network selects
|
|
2349
|
+
* nothing: name one with {@link AparteConfig.setModelConfig | setModelConfig()}, or let the
|
|
2350
|
+
* person pick in `<aparte-model-selector>`.
|
|
2355
2351
|
* @param providers AparteAIProvider implementations
|
|
2356
2352
|
* @example
|
|
2357
2353
|
* aparteGlobalConfig.registerAIProvider(OpenRouterProvider);
|
|
@@ -2469,7 +2465,8 @@ class AparteConfig {
|
|
|
2469
2465
|
/**
|
|
2470
2466
|
* Restore previously saved model preference via the registered provider.
|
|
2471
2467
|
* Should be called once at app startup, before any component mounts.
|
|
2472
|
-
* No-op if no provider is registered or nothing was saved.
|
|
2468
|
+
* No-op if no provider is registered or nothing was saved. The saved selection is
|
|
2469
|
+
* applied without being saved again.
|
|
2473
2470
|
*/
|
|
2474
2471
|
restoreModelPreference() {
|
|
2475
2472
|
if (!this._modelPreferenceProvider) return null;
|
|
@@ -2482,7 +2479,8 @@ class AparteConfig {
|
|
|
2482
2479
|
return null;
|
|
2483
2480
|
}
|
|
2484
2481
|
/**
|
|
2485
|
-
*
|
|
2482
|
+
* Select the default provider and model, merged over the current selection. With a
|
|
2483
|
+
* model-preference provider registered and both set, the selection is saved through it.
|
|
2486
2484
|
*/
|
|
2487
2485
|
setModelConfig(config) {
|
|
2488
2486
|
this._modelConfig = { ...this._modelConfig, ...config };
|
|
@@ -2526,12 +2524,6 @@ class AparteConfig {
|
|
|
2526
2524
|
hasSelectedModel() {
|
|
2527
2525
|
return !!(this._modelConfig.defaultProvider && this._modelConfig.defaultModel);
|
|
2528
2526
|
}
|
|
2529
|
-
/**
|
|
2530
|
-
* Opt-in UX: when enabled, `<aparte-composer>` blocks sending and greys out
|
|
2531
|
-
* until {@link hasSelectedModel} is true (e.g. while the model selector is
|
|
2532
|
-
* still fetching its list). Off by default so single-model / backend setups
|
|
2533
|
-
* that never select a model are unaffected.
|
|
2534
|
-
*/
|
|
2535
2527
|
/**
|
|
2536
2528
|
* Elicitation policy: whether a choice offers a free-text "Other…" escape.
|
|
2537
2529
|
*
|
|
@@ -2590,6 +2582,12 @@ class AparteConfig {
|
|
|
2590
2582
|
getElicitationFieldRenderer() {
|
|
2591
2583
|
return this._elicitationFieldRenderer;
|
|
2592
2584
|
}
|
|
2585
|
+
/**
|
|
2586
|
+
* Opt-in UX: when enabled, `<aparte-composer>` blocks sending and greys out
|
|
2587
|
+
* until {@link hasSelectedModel} is true (e.g. while the model selector is
|
|
2588
|
+
* still fetching its list). Off by default so single-model / backend setups
|
|
2589
|
+
* that never select a model are unaffected.
|
|
2590
|
+
*/
|
|
2593
2591
|
setRequireModelSelection(required) {
|
|
2594
2592
|
if (this._requireModelSelection === required) return;
|
|
2595
2593
|
this._requireModelSelection = required;
|
|
@@ -2625,7 +2623,8 @@ class AparteConfig {
|
|
|
2625
2623
|
// Public Rendering Methods (with Fallbacks)
|
|
2626
2624
|
// ─────────────────────────────────────────────────────────────────────────
|
|
2627
2625
|
/**
|
|
2628
|
-
*
|
|
2626
|
+
* The locale's string for `key`, or the default (English) locale's when the locale
|
|
2627
|
+
* leaves it out; `''` for a key neither declares.
|
|
2629
2628
|
*/
|
|
2630
2629
|
t(key) {
|
|
2631
2630
|
const val = this._locale[key] || APARTE_DEFAULT_LOCALE[key];
|
|
@@ -2665,7 +2664,7 @@ class AparteConfig {
|
|
|
2665
2664
|
}
|
|
2666
2665
|
/**
|
|
2667
2666
|
* Highlight code block
|
|
2668
|
-
* Fallback: Returns raw code wrapped in
|
|
2667
|
+
* Fallback: Returns raw code wrapped in `<pre><code>`
|
|
2669
2668
|
* Supports Promise if provider is async/streaming
|
|
2670
2669
|
*/
|
|
2671
2670
|
async highlightCode(code, lang) {
|
|
@@ -2701,18 +2700,11 @@ class AparteConfig {
|
|
|
2701
2700
|
return Array.from(this._tools.values()).map((e) => e.tool);
|
|
2702
2701
|
}
|
|
2703
2702
|
/**
|
|
2704
|
-
* The registered tools' own `systemPrompt`s,
|
|
2705
|
-
*
|
|
2706
|
-
*
|
|
2707
|
-
*
|
|
2708
|
-
*
|
|
2709
|
-
* writes it, sees the tool work (the model has the schema either way), and never learns
|
|
2710
|
-
* that the sentence explaining WHEN to reach for it was dropped. `@aparte/plugin-ask-user`
|
|
2711
|
-
* sets one, so a shipped plugin was losing its instructions.
|
|
2712
|
-
*
|
|
2713
|
-
* Registration order, blank line between, because two tools' instructions are two
|
|
2714
|
-
* paragraphs and not one run-on sentence. The app's own template stays a separate
|
|
2715
|
-
* message — see `resolveSystemPrompt`, which is about the app and not about the tools.
|
|
2703
|
+
* The registered tools' own `systemPrompt`s, in registration order with a blank line
|
|
2704
|
+
* between, or `null` if none sets one. `AparteClient` sends them with every request, as a
|
|
2705
|
+
* system message after the app's own ({@link AparteConfig.resolveSystemPrompt | resolveSystemPrompt()});
|
|
2706
|
+
* a request you assemble yourself should send them the same way, or the model loses the
|
|
2707
|
+
* tools' instructions.
|
|
2716
2708
|
*/
|
|
2717
2709
|
resolveToolSystemPrompts() {
|
|
2718
2710
|
const prompts = Array.from(this._tools.values()).map((entry) => entry.tool.systemPrompt?.trim()).filter((prompt) => Boolean(prompt));
|
|
@@ -2876,6 +2868,12 @@ class AparteConfig {
|
|
|
2876
2868
|
* confirmation, a text field, a form) and resolves with the user's answer.
|
|
2877
2869
|
* The `<aparte-elicitation>` Web Component registers itself here; an app can
|
|
2878
2870
|
* override with its own framework-native presenter. Pass `null` to clear.
|
|
2871
|
+
*
|
|
2872
|
+
* Several can be registered, one per chat: `owner` is an element of the chat the
|
|
2873
|
+
* presenter belongs to (`<aparte-elicitation>` passes itself). A request whose `target`
|
|
2874
|
+
* is inside that chat reaches its presenter; a request with no `target`, or from a chat
|
|
2875
|
+
* with no presenter of its own, reaches the most recently registered one. Registering a
|
|
2876
|
+
* presenter again makes it the most recent.
|
|
2879
2877
|
*/
|
|
2880
2878
|
setElicitationPresenter(presenter, owner) {
|
|
2881
2879
|
if (presenter === null) {
|
|
@@ -2927,35 +2925,21 @@ class AparteConfig {
|
|
|
2927
2925
|
return stack[stack.length - 1]?.fn;
|
|
2928
2926
|
}
|
|
2929
2927
|
/**
|
|
2930
|
-
* Ask the
|
|
2931
|
-
*
|
|
2932
|
-
* question is the schema, not a bespoke tool.
|
|
2933
|
-
*
|
|
2934
|
-
* **Resolves** `accept` with the value, or `decline` when the user declines.
|
|
2935
|
-
*
|
|
2936
|
-
* **Rejects** — it does not resolve — when the request ends without an answer, with
|
|
2937
|
-
* an {@link AparteElicitationAbortError} whose `name` is `'AbortError'` and whose
|
|
2938
|
-
* `reason` is `'aborted'` (the turn was stopped, the signal fired, or another request
|
|
2939
|
-
* took the question away) or `'no-presenter'` (nothing was mounted to ask it). So
|
|
2940
|
-
* `await` it inside a `try`, or attach a `.catch()`.
|
|
2928
|
+
* Ask the person for typed input in the middle of a run, and wait for the answer. The
|
|
2929
|
+
* primitive behind `ask_user` and tool approval: the kind of question is its schema.
|
|
2941
2930
|
*
|
|
2942
|
-
*
|
|
2943
|
-
* "resolves `cancel` when the turn is cancelled", and "with NO presenter registered it
|
|
2944
|
-
* resolves `cancel` rather than hanging". Both were false the moment `cancel` was
|
|
2945
|
-
* removed — and the second is the one that bites, because a developer who reads
|
|
2946
|
-
* "rather than hanging" writes no `.catch()` and gets an unhandled rejection. It ships
|
|
2947
|
-
* in the `.d.ts` and in the custom-elements manifest, which is the API surface the
|
|
2948
|
-
* docs site renders, so it was the most-read wrong sentence in the library.
|
|
2931
|
+
* **Resolves** `accept` with the value, or `decline` when the person declines.
|
|
2949
2932
|
*
|
|
2950
|
-
*
|
|
2951
|
-
*
|
|
2952
|
-
*
|
|
2933
|
+
* **Rejects**, with no answer, with an {@link AparteElicitationAbortError} (its `name` is
|
|
2934
|
+
* `'AbortError'`) whose `reason` is `'aborted'` — the turn was stopped, the signal fired,
|
|
2935
|
+
* or another request took the question away — or `'no-presenter'`, when nothing is mounted
|
|
2936
|
+
* to ask it (with a console warning, once). `await` it in a `try`, or attach a `.catch()`.
|
|
2953
2937
|
*
|
|
2954
|
-
*
|
|
2955
|
-
*
|
|
2956
|
-
*
|
|
2957
|
-
*
|
|
2958
|
-
*
|
|
2938
|
+
* The question goes to the presenter of the chat that holds `request.target` (see
|
|
2939
|
+
* {@link AparteConfig.setElicitationPresenter | setElicitationPresenter()});
|
|
2940
|
+
* `<aparte-elicitation>` is the default one, and registers itself once it is in the DOM.
|
|
2941
|
+
* A chat shows one question at a time: a second request in the same chat waits until the
|
|
2942
|
+
* first is answered or ends, and requests with no `target` share one queue.
|
|
2959
2943
|
*/
|
|
2960
2944
|
requestUserInput(request) {
|
|
2961
2945
|
const queue = this._elicitationQueueKey(request);
|
|
@@ -3000,10 +2984,13 @@ class AparteConfig {
|
|
|
3000
2984
|
return presenter(request);
|
|
3001
2985
|
}
|
|
3002
2986
|
/**
|
|
3003
|
-
*
|
|
3004
|
-
*
|
|
3005
|
-
*
|
|
3006
|
-
*
|
|
2987
|
+
* Put the configuration back to its defaults: the providers and the selected model,
|
|
2988
|
+
* the tools with their renderers and summaries, the renderers and render hooks, the
|
|
2989
|
+
* Markdown and highlight providers, the sanitizer, the system prompt, the locale, icons,
|
|
2990
|
+
* avatars, actions and host handlers, the conversation manager, the question presenters
|
|
2991
|
+
* and their queue. Three things stay: the transport, the key sources added with
|
|
2992
|
+
* {@link AparteConfig.registerKeyProvider | registerKeyProvider()} (an `AparteClient`'s
|
|
2993
|
+
* `keyResolver` among them), and the {@link AparteConfig.subscribe | subscribe()} listeners.
|
|
3007
2994
|
*/
|
|
3008
2995
|
reset() {
|
|
3009
2996
|
this._markdownProvider = void 0;
|
|
@@ -3192,6 +3179,9 @@ const textRenderer = {
|
|
|
3192
3179
|
getStyles: () => ``
|
|
3193
3180
|
};
|
|
3194
3181
|
const THINKING_ANCHOR_SLACK = 24;
|
|
3182
|
+
function labelOf(segment) {
|
|
3183
|
+
return segment.label && segment.label !== "Thinking" ? segment.label : contextConfig().t("thinking");
|
|
3184
|
+
}
|
|
3195
3185
|
const thinkingRenderer = {
|
|
3196
3186
|
type: "thinking",
|
|
3197
3187
|
/*
|
|
@@ -3222,13 +3212,13 @@ const thinkingRenderer = {
|
|
|
3222
3212
|
* only part that is genuinely about reasoning rather than about disclosure.
|
|
3223
3213
|
* `.aparte-thinking-label` stays too: `relabel` below queries it.
|
|
3224
3214
|
*/
|
|
3225
|
-
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
|
|
3215
|
+
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(labelOf(segment))}</span><span class="aparte-accordion__icon">${contextConfig().getIcon("expand")}</span></summary><div class="aparte-accordion__panel aparte-thinking-content" role="region" tabindex="0" aria-label="${escapeAttr(labelOf(segment))}">${contextConfig().renderMarkdown(segment.content)}</div></details>`,
|
|
3226
3216
|
/**
|
|
3227
3217
|
* The default label is the only config-derived text here — and `segment.label`
|
|
3228
3218
|
* still wins, exactly as in `render`, because that string is the app's.
|
|
3229
3219
|
*/
|
|
3230
3220
|
relabel: (el2, segment) => {
|
|
3231
|
-
const text = segment
|
|
3221
|
+
const text = labelOf(segment);
|
|
3232
3222
|
const label = el2.querySelector(".aparte-thinking-label");
|
|
3233
3223
|
if (label) label.textContent = text;
|
|
3234
3224
|
el2.querySelector(".aparte-thinking-content")?.setAttribute("aria-label", text);
|
|
@@ -5063,6 +5053,14 @@ class AparteChatHost {
|
|
|
5063
5053
|
}
|
|
5064
5054
|
ac.signal.addEventListener("abort", () => resolve("aborted"), { once: true });
|
|
5065
5055
|
});
|
|
5056
|
+
const { host, hostId } = this.binding;
|
|
5057
|
+
const onAbort = (e) => {
|
|
5058
|
+
const target = e.detail?.targetId;
|
|
5059
|
+
if ((!target || target === hostId) && this._streamAbort === ac) this.stopTokenStream();
|
|
5060
|
+
};
|
|
5061
|
+
window.addEventListener("aparte-abort", onAbort);
|
|
5062
|
+
const replaced = () => this._streamAbort !== void 0 && this._streamAbort !== ac;
|
|
5063
|
+
dispatchLifecycleEvent(host, "aparte-message-start", { messageId, role: "assistant" }, hostId);
|
|
5066
5064
|
try {
|
|
5067
5065
|
for (; ; ) {
|
|
5068
5066
|
const res = await Promise.race([iterator.next(), aborted]);
|
|
@@ -5072,12 +5070,24 @@ class AparteChatHost {
|
|
|
5072
5070
|
}
|
|
5073
5071
|
this._flushStreamState();
|
|
5074
5072
|
this._vp()?.completeMessage?.(messageId);
|
|
5075
|
-
|
|
5073
|
+
if (!replaced()) {
|
|
5074
|
+
this._setStreamingId(null);
|
|
5075
|
+
dispatchLifecycleEvent(
|
|
5076
|
+
host,
|
|
5077
|
+
ac.signal.aborted ? "aparte-message-aborted" : "aparte-message-done",
|
|
5078
|
+
ac.signal.aborted ? { messageId } : { messageId, role: "assistant" },
|
|
5079
|
+
hostId
|
|
5080
|
+
);
|
|
5081
|
+
}
|
|
5076
5082
|
} catch (err) {
|
|
5077
5083
|
this._flushStreamState();
|
|
5078
|
-
|
|
5084
|
+
if (!replaced()) {
|
|
5085
|
+
this._setStreamingId(null);
|
|
5086
|
+
dispatchLifecycleEvent(host, "aparte-message-error", { messageId, error: err }, hostId);
|
|
5087
|
+
}
|
|
5079
5088
|
throw err;
|
|
5080
5089
|
} finally {
|
|
5090
|
+
window.removeEventListener("aparte-abort", onAbort);
|
|
5081
5091
|
if (ac.signal.aborted) {
|
|
5082
5092
|
try {
|
|
5083
5093
|
await iterator.return?.();
|
|
@@ -5814,6 +5824,7 @@ function el(tag, className, text) {
|
|
|
5814
5824
|
if (text != null) node.textContent = text;
|
|
5815
5825
|
return node;
|
|
5816
5826
|
}
|
|
5827
|
+
let askSeq = 0;
|
|
5817
5828
|
function buildApprovalPanel(message, options, onChange, details) {
|
|
5818
5829
|
const cfg = contextConfig();
|
|
5819
5830
|
const panel = el("div", "aparte-elic-panel aparte-approval-panel");
|
|
@@ -5843,6 +5854,10 @@ function buildApprovalPanel(message, options, onChange, details) {
|
|
|
5843
5854
|
const row = el("div", "aparte-approval-options");
|
|
5844
5855
|
row.setAttribute("role", "group");
|
|
5845
5856
|
row.setAttribute("aria-label", cfg.t("approvalOptionsLabel"));
|
|
5857
|
+
if (ask) {
|
|
5858
|
+
ask.id ||= `aparte-approval-ask-${++askSeq}`;
|
|
5859
|
+
row.setAttribute("aria-describedby", ask.id);
|
|
5860
|
+
}
|
|
5846
5861
|
const labelled = [];
|
|
5847
5862
|
const textOf = (option) => typeof option.label === "function" ? option.label() : option.label;
|
|
5848
5863
|
const descriptionOf = (option) => typeof option.description === "function" ? option.description() : option.description;
|
|
@@ -5943,6 +5958,11 @@ class AparteClient {
|
|
|
5943
5958
|
options;
|
|
5944
5959
|
/** Config read by this client — an instance config, or the global default. */
|
|
5945
5960
|
_config;
|
|
5961
|
+
/**
|
|
5962
|
+
* A `keyResolver` becomes a key source on the client's config at once, before `start()`,
|
|
5963
|
+
* so everything that needs a key can use it — the model selector's list included.
|
|
5964
|
+
* `autoRegister` (on by default) installs core's built-in renderers on that config.
|
|
5965
|
+
*/
|
|
5946
5966
|
constructor(options = {}) {
|
|
5947
5967
|
this.options = {
|
|
5948
5968
|
autoRegister: true,
|
|
@@ -6004,7 +6024,9 @@ class AparteClient {
|
|
|
6004
6024
|
};
|
|
6005
6025
|
}
|
|
6006
6026
|
/**
|
|
6007
|
-
*
|
|
6027
|
+
* Answer the chats: listen on `window` for `aparte-send`, `aparte-retry`, `aparte-edit`,
|
|
6028
|
+
* `aparte-abort` and `aparte-conversation-loaded`. Nothing streams before this is
|
|
6029
|
+
* called. Starting a stopped client puts its `keyResolver` back on the config.
|
|
6008
6030
|
*/
|
|
6009
6031
|
start() {
|
|
6010
6032
|
this._registerKeyResolver();
|
|
@@ -6047,7 +6069,8 @@ class AparteClient {
|
|
|
6047
6069
|
window.addEventListener("aparte-conversation-loaded", this._boundLoadedHandler);
|
|
6048
6070
|
}
|
|
6049
6071
|
/**
|
|
6050
|
-
* Stop
|
|
6072
|
+
* Stop answering: abort the reply in flight, take this client's `keyResolver` off the
|
|
6073
|
+
* config, and remove the listeners.
|
|
6051
6074
|
*/
|
|
6052
6075
|
stop() {
|
|
6053
6076
|
this.abort();
|
|
@@ -7728,4 +7751,4 @@ export {
|
|
|
7728
7751
|
collectRendererStyles as y,
|
|
7729
7752
|
contentToText as z
|
|
7730
7753
|
};
|
|
7731
|
-
//# sourceMappingURL=dialog-triggers-
|
|
7754
|
+
//# sourceMappingURL=dialog-triggers-ChnPFG9M.js.map
|