@aparte/angular 0.16.10 → 0.17.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/fesm2022/aparte-angular.mjs +189 -75
- package/dist/fesm2022/aparte-angular.mjs.map +1 -1
- package/dist/generated/element.directives.d.ts +38 -29
- package/dist/index.d.ts +1 -1
- package/dist/lib/aparte-ai.service.d.ts +12 -3
- package/dist/lib/aparte-chat.component.d.ts +38 -3
- package/dist/lib/aparte-ui.component.d.ts +20 -5
- package/dist/lib/provide-aparte.d.ts +24 -5
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, Injectable, makeEnvironmentProviders, provideAppInitializer, DestroyRef, effect, ElementRef, signal,
|
|
2
|
+
import { InjectionToken, inject, Injectable, makeEnvironmentProviders, provideAppInitializer, DestroyRef, effect, ElementRef, signal, computed, output, numberAttribute, booleanAttribute, ViewChildren, ViewChild, Input, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, Component, Renderer2, Directive, EventEmitter, HostListener, Output } from '@angular/core';
|
|
3
3
|
import { AparteClient, aparteGlobalConfig, isAwaitingReply, uuid, AparteChatHost, APARTE_DEFAULT_UI_EVENTS, applyElementProps, AparteConversationManager } from '@aparte/core';
|
|
4
4
|
export { aparteGlobalConfig } from '@aparte/core';
|
|
5
5
|
import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
@@ -18,6 +18,15 @@ const APARTE_CLIENT_OPTIONS = new InjectionToken('APARTE_CLIENT_OPTIONS');
|
|
|
18
18
|
class AparteAiService {
|
|
19
19
|
_clientOptions = inject(APARTE_CLIENT_OPTIONS, { optional: true });
|
|
20
20
|
_client = new AparteClient(this._clientOptions ?? {});
|
|
21
|
+
/**
|
|
22
|
+
* The `AparteClient` this service drives — the same object React's
|
|
23
|
+
* `useAparteClient`, Vue's and Svelte's `createAparteClient` return as
|
|
24
|
+
* `{ client, abort }`. Angular alone kept it private, so the instance was
|
|
25
|
+
* unreachable while each service's docblock called itself the others' equivalent.
|
|
26
|
+
*/
|
|
27
|
+
get client() {
|
|
28
|
+
return this._client;
|
|
29
|
+
}
|
|
21
30
|
/**
|
|
22
31
|
* Start listening to `aparte-send` events globally.
|
|
23
32
|
* This connects the <aparte-chat> components to the AI Providers.
|
|
@@ -30,7 +39,9 @@ class AparteAiService {
|
|
|
30
39
|
this._client.start();
|
|
31
40
|
}
|
|
32
41
|
/**
|
|
33
|
-
* Stop listening.
|
|
42
|
+
* Stop listening. The client also takes its `keyResolver` back off the config,
|
|
43
|
+
* so a paused bridge no longer answers for keys — a model list refreshed while
|
|
44
|
+
* disconnected sees the config's other key sources only.
|
|
34
45
|
*/
|
|
35
46
|
disconnect() {
|
|
36
47
|
this._client.stop();
|
|
@@ -139,8 +150,22 @@ function applyThemeMode(mode, doc, destroyRef) {
|
|
|
139
150
|
* `AparteAiService.connect()` needed.
|
|
140
151
|
*
|
|
141
152
|
* The components (`AparteChatComponent`, `AparteUiComponent`) are standalone and
|
|
142
|
-
* work WITHOUT this — it is config sugar.
|
|
143
|
-
*
|
|
153
|
+
* work WITHOUT this — it is config sugar. Angular is the only wrapper that has it,
|
|
154
|
+
* and the other three lose nothing: what this provider exists for is an initializer
|
|
155
|
+
* that runs before the first component and a `DestroyRef` to release the theme
|
|
156
|
+
* listener. Everything it configures is a plain call — bar `theme: 'auto'`, which is a
|
|
157
|
+
* `matchMedia` listener you own — so React, Vue and Svelte make the same ones at module
|
|
158
|
+
* scope, before the app mounts:
|
|
159
|
+
*
|
|
160
|
+
* ```ts
|
|
161
|
+
* aparteGlobalConfig.registerAIProvider(createOpenAICompatProvider(presets.OPENROUTER));
|
|
162
|
+
* aparteGlobalConfig.setModelConfig({ defaultModel: 'openai/gpt-4o-mini' });
|
|
163
|
+
* aparteGlobalConfig.setLocale(fr);
|
|
164
|
+
* document.documentElement.setAttribute('data-aparte-theme', 'dark');
|
|
165
|
+
* // 'auto' is the one option with no one-liner: match prefers-color-scheme, rewrite the
|
|
166
|
+
* // attribute on change, and drop the listener when your app tears down.
|
|
167
|
+
* // The client stays the wrapper's own: useAparteClient(), createAparteClient().
|
|
168
|
+
* ```
|
|
144
169
|
*
|
|
145
170
|
* @example
|
|
146
171
|
* bootstrapApplication(App, {
|
|
@@ -200,15 +225,19 @@ class AparteChatComponent {
|
|
|
200
225
|
// Debounced with requestAnimationFrame so rapid token bursts only trigger
|
|
201
226
|
// one sync per paint frame instead of N syncs for N tokens.
|
|
202
227
|
effect(() => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
228
|
+
// Read the signal so the effect tracks it — and react to EVERY value,
|
|
229
|
+
// the empty list included. React, Vue and Svelte all re-sync
|
|
230
|
+
// unconditionally, and emptying the transcript is exactly when the host
|
|
231
|
+
// has to let go of the bubbles that just went away. With the default
|
|
232
|
+
// bubbles `bubbleRefs.changes` hid the divergence; under a custom
|
|
233
|
+
// `[bubbleTemplate]` that query never fires and this was the only path.
|
|
234
|
+
this.messages();
|
|
235
|
+
if (this._syncRafId !== null)
|
|
236
|
+
return;
|
|
237
|
+
this._syncRafId = requestAnimationFrame(() => {
|
|
238
|
+
this._syncRafId = null;
|
|
239
|
+
this._host?.syncBubbles();
|
|
240
|
+
});
|
|
212
241
|
});
|
|
213
242
|
}
|
|
214
243
|
elementRef = inject(ElementRef);
|
|
@@ -248,6 +277,35 @@ class AparteChatComponent {
|
|
|
248
277
|
*/
|
|
249
278
|
set centerWhenEmptyInput(val) { this.centerWhenEmpty.set(val); }
|
|
250
279
|
centerWhenEmpty = signal(false);
|
|
280
|
+
/**
|
|
281
|
+
* The conversation is on its way: the viewport draws two skeleton turns, the empty
|
|
282
|
+
* state stays off and the composer is disabled until it lands. The conversation
|
|
283
|
+
* controller sets this itself while it fetches through your adapter's `loadFull()`;
|
|
284
|
+
* the input is for a wait of your own. Off by default.
|
|
285
|
+
*/
|
|
286
|
+
set loadingInput(val) { this.loading.set(val); }
|
|
287
|
+
loading = signal(false);
|
|
288
|
+
/** The controller's wait (a conversation fetched through the adapter). */
|
|
289
|
+
hostLoading = signal(false);
|
|
290
|
+
waiting = computed(() => this.loading() || this.hostLoading());
|
|
291
|
+
get loadingText() { return (this.config ?? aparteGlobalConfig).t('loadingConversation'); }
|
|
292
|
+
/**
|
|
293
|
+
* Class(es) merged onto the INNER `.aparte-chat-container`, not onto this
|
|
294
|
+
* component's own `<aparte-chat>` host.
|
|
295
|
+
*
|
|
296
|
+
* The other three wrappers have no such host: their root IS the container, so a
|
|
297
|
+
* consumer's `className` / `class` lands on the div that carries
|
|
298
|
+
* `.aparte-chat-container`, `[overlay-composer]` and `[data-aparte-empty]` — the
|
|
299
|
+
* three selectors core's shell recipe keys on. Here a class written on the tag
|
|
300
|
+
* lands one level above them, so overriding the shell needed a descendant
|
|
301
|
+
* selector on Angular alone. This input is that parity. Additive: the recipe's
|
|
302
|
+
* own classes stay.
|
|
303
|
+
*/
|
|
304
|
+
set containerClassInput(val) { this.containerClass.set(val ?? ''); }
|
|
305
|
+
containerClass = signal('');
|
|
306
|
+
/** Inline style for the same div — a string or a `{ prop: value }` map. */
|
|
307
|
+
set containerStyleInput(val) { this.containerStyle.set(val ?? null); }
|
|
308
|
+
containerStyle = signal(null);
|
|
251
309
|
/**
|
|
252
310
|
* Overlay the composer on the transcript (the ChatGPT anatomy): the scroll
|
|
253
311
|
* surface spans the whole column, the scrollbar runs edge to edge, and the
|
|
@@ -377,6 +435,7 @@ class AparteChatComponent {
|
|
|
377
435
|
onMessageAppended: (msg) => this.messageAppended.emit(msg),
|
|
378
436
|
onTypingChange: (typing) => { this.isTyping.set(typing); this.typingChange.emit(typing); },
|
|
379
437
|
onStreamingChange: (id) => this._streamingId.set(id),
|
|
438
|
+
onLoadingChange: (on) => this.hostLoading.set(on),
|
|
380
439
|
// Sibling-info / deferred work runs after Angular has re-rendered.
|
|
381
440
|
afterRender: (cb) => { setTimeout(cb, 0); },
|
|
382
441
|
resetComposer: () => {
|
|
@@ -443,8 +502,11 @@ class AparteChatComponent {
|
|
|
443
502
|
}
|
|
444
503
|
/** Read the current message list. */
|
|
445
504
|
getMessages() { return this._host?.getMessages() ?? this.messages(); }
|
|
446
|
-
/**
|
|
447
|
-
|
|
505
|
+
/**
|
|
506
|
+
* Clear all messages + reset state. `{ revokeAttachments: false }` keeps the
|
|
507
|
+
* attachments' object URLs alive — pass it when the same messages are going back.
|
|
508
|
+
*/
|
|
509
|
+
clearMessages(options) { this._host?.clearMessages(options); }
|
|
448
510
|
/** Scroll the viewport to the latest message. */
|
|
449
511
|
scrollToBottom() {
|
|
450
512
|
this.viewportRef?.nativeElement?.scrollToBottom?.();
|
|
@@ -571,15 +633,29 @@ class AparteChatComponent {
|
|
|
571
633
|
this.messageSent.emit(detail);
|
|
572
634
|
}
|
|
573
635
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
574
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: AparteChatComponent, isStandalone: true, selector: "aparte-chat", inputs: { messagesInput: ["messages", "messagesInput"], placeholderInput: ["placeholder", "placeholderInput"], disabledInput: ["disabled", "disabledInput", booleanAttribute], submitOnEnterInput: ["submitOnEnter", "submitOnEnterInput", booleanAttribute], centerWhenEmptyInput: ["centerWhenEmpty", "centerWhenEmptyInput", booleanAttribute], overlayComposerInput: ["overlayComposer", "overlayComposerInput", booleanAttribute], attachmentsInput: ["attachments", "attachmentsInput", booleanAttribute], elicitationInput: ["elicitation", "elicitationInput", booleanAttribute], isTypingInput: ["isTyping", "isTypingInput", booleanAttribute], typingTextInput: ["typingText", "typingTextInput"], layoutTransitionMs: ["layoutTransitionMs", "layoutTransitionMs", numberAttribute], conversationIdInput: ["conversationId", "conversationIdInput"], config: "config", bubbleTemplate: "bubbleTemplate" }, outputs: { messageSent: "messageSent", action: "action", messagesChange: "messagesChange", messageAppended: "messageAppended", typingChange: "typingChange", conversationCreated: "conversationCreated" }, host: { attributes: { "framework-managed": "" } }, viewQueries: [{ propertyName: "viewportRef", first: true, predicate: ["viewport"], descendants: true }, { propertyName: "inputRef", first: true, predicate: ["input"], descendants: true }, { propertyName: "bubbleRefs", predicate: ["bubble"], descendants: true }], ngImport: i0, template: `
|
|
636
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: AparteChatComponent, isStandalone: true, selector: "aparte-chat", inputs: { messagesInput: ["messages", "messagesInput"], placeholderInput: ["placeholder", "placeholderInput"], disabledInput: ["disabled", "disabledInput", booleanAttribute], submitOnEnterInput: ["submitOnEnter", "submitOnEnterInput", booleanAttribute], centerWhenEmptyInput: ["centerWhenEmpty", "centerWhenEmptyInput", booleanAttribute], loadingInput: ["loading", "loadingInput", booleanAttribute], containerClassInput: ["containerClass", "containerClassInput"], containerStyleInput: ["containerStyle", "containerStyleInput"], overlayComposerInput: ["overlayComposer", "overlayComposerInput", booleanAttribute], attachmentsInput: ["attachments", "attachmentsInput", booleanAttribute], elicitationInput: ["elicitation", "elicitationInput", booleanAttribute], isTypingInput: ["isTyping", "isTypingInput", booleanAttribute], typingTextInput: ["typingText", "typingTextInput"], layoutTransitionMs: ["layoutTransitionMs", "layoutTransitionMs", numberAttribute], conversationIdInput: ["conversationId", "conversationIdInput"], config: "config", bubbleTemplate: "bubbleTemplate" }, outputs: { messageSent: "messageSent", action: "action", messagesChange: "messagesChange", messageAppended: "messageAppended", typingChange: "typingChange", conversationCreated: "conversationCreated" }, host: { attributes: { "framework-managed": "" } }, viewQueries: [{ propertyName: "viewportRef", first: true, predicate: ["viewport"], descendants: true }, { propertyName: "inputRef", first: true, predicate: ["input"], descendants: true }, { propertyName: "bubbleRefs", predicate: ["bubble"], descendants: true }], ngImport: i0, template: `
|
|
575
637
|
<div
|
|
576
638
|
class="aparte-chat-container"
|
|
639
|
+
[class]="containerClass()"
|
|
640
|
+
[style]="containerStyle()"
|
|
577
641
|
[class.aparte-chat-container--auto-center]="centerWhenEmpty()"
|
|
578
642
|
[attr.overlay-composer]="overlayComposer() ? '' : null"
|
|
579
|
-
[attr.data-aparte-empty]="centerWhenEmpty() && messages().length === 0 ? '' : null"
|
|
643
|
+
[attr.data-aparte-empty]="centerWhenEmpty() && messages().length === 0 && !waiting() ? '' : null"
|
|
580
644
|
>
|
|
581
|
-
<aparte-chat-viewport #viewport framework-managed="">
|
|
582
|
-
@if (
|
|
645
|
+
<aparte-chat-viewport #viewport framework-managed="" [attr.loading]="waiting() ? '' : null">
|
|
646
|
+
@if (waiting()) {
|
|
647
|
+
<!-- The wait, drawn here because this DOM is Angular's: the kit's skeleton
|
|
648
|
+
recipe, and a line for a screen reader. -->
|
|
649
|
+
<div class="aparte-viewport-loading" aria-hidden="true">
|
|
650
|
+
<span class="aparte-skeleton aparte-skeleton--rect aparte-viewport-loading__user"></span>
|
|
651
|
+
<span class="aparte-skeleton aparte-skeleton--text"></span>
|
|
652
|
+
<span class="aparte-skeleton aparte-skeleton--text"></span>
|
|
653
|
+
<span class="aparte-skeleton aparte-skeleton--text"></span>
|
|
654
|
+
<span class="aparte-skeleton aparte-skeleton--text aparte-viewport-loading__last"></span>
|
|
655
|
+
</div>
|
|
656
|
+
<span class="aparte-viewport-loading-status aparte-sr-only" role="status">{{ loadingText }}</span>
|
|
657
|
+
}
|
|
658
|
+
@if (messages().length === 0 && !waiting()) {
|
|
583
659
|
<!-- Welcome / placeholder shown inside the viewport while empty. -->
|
|
584
660
|
<ng-content select="[slot='empty-state']"></ng-content>
|
|
585
661
|
}
|
|
@@ -620,7 +696,7 @@ class AparteChatComponent {
|
|
|
620
696
|
<aparte-composer
|
|
621
697
|
#input
|
|
622
698
|
[attr.placeholder]="placeholder()"
|
|
623
|
-
[attr.disabled]="disabled() ? '' : null"
|
|
699
|
+
[attr.disabled]="disabled() || waiting() ? '' : null"
|
|
624
700
|
[attr.submit-on-enter]="submitOnEnter() ? null : 'false'"
|
|
625
701
|
(aparte-send)="onAparteSend($event)"
|
|
626
702
|
>
|
|
@@ -662,12 +738,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
662
738
|
args: [{ selector: 'aparte-chat', standalone: true, imports: [NgTemplateOutlet], schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, host: { 'framework-managed': '' }, template: `
|
|
663
739
|
<div
|
|
664
740
|
class="aparte-chat-container"
|
|
741
|
+
[class]="containerClass()"
|
|
742
|
+
[style]="containerStyle()"
|
|
665
743
|
[class.aparte-chat-container--auto-center]="centerWhenEmpty()"
|
|
666
744
|
[attr.overlay-composer]="overlayComposer() ? '' : null"
|
|
667
|
-
[attr.data-aparte-empty]="centerWhenEmpty() && messages().length === 0 ? '' : null"
|
|
745
|
+
[attr.data-aparte-empty]="centerWhenEmpty() && messages().length === 0 && !waiting() ? '' : null"
|
|
668
746
|
>
|
|
669
|
-
<aparte-chat-viewport #viewport framework-managed="">
|
|
670
|
-
@if (
|
|
747
|
+
<aparte-chat-viewport #viewport framework-managed="" [attr.loading]="waiting() ? '' : null">
|
|
748
|
+
@if (waiting()) {
|
|
749
|
+
<!-- The wait, drawn here because this DOM is Angular's: the kit's skeleton
|
|
750
|
+
recipe, and a line for a screen reader. -->
|
|
751
|
+
<div class="aparte-viewport-loading" aria-hidden="true">
|
|
752
|
+
<span class="aparte-skeleton aparte-skeleton--rect aparte-viewport-loading__user"></span>
|
|
753
|
+
<span class="aparte-skeleton aparte-skeleton--text"></span>
|
|
754
|
+
<span class="aparte-skeleton aparte-skeleton--text"></span>
|
|
755
|
+
<span class="aparte-skeleton aparte-skeleton--text"></span>
|
|
756
|
+
<span class="aparte-skeleton aparte-skeleton--text aparte-viewport-loading__last"></span>
|
|
757
|
+
</div>
|
|
758
|
+
<span class="aparte-viewport-loading-status aparte-sr-only" role="status">{{ loadingText }}</span>
|
|
759
|
+
}
|
|
760
|
+
@if (messages().length === 0 && !waiting()) {
|
|
671
761
|
<!-- Welcome / placeholder shown inside the viewport while empty. -->
|
|
672
762
|
<ng-content select="[slot='empty-state']"></ng-content>
|
|
673
763
|
}
|
|
@@ -708,7 +798,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
708
798
|
<aparte-composer
|
|
709
799
|
#input
|
|
710
800
|
[attr.placeholder]="placeholder()"
|
|
711
|
-
[attr.disabled]="disabled() ? '' : null"
|
|
801
|
+
[attr.disabled]="disabled() || waiting() ? '' : null"
|
|
712
802
|
[attr.submit-on-enter]="submitOnEnter() ? null : 'false'"
|
|
713
803
|
(aparte-send)="onAparteSend($event)"
|
|
714
804
|
>
|
|
@@ -759,6 +849,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
759
849
|
}], centerWhenEmptyInput: [{
|
|
760
850
|
type: Input,
|
|
761
851
|
args: [{ alias: 'centerWhenEmpty', transform: booleanAttribute }]
|
|
852
|
+
}], loadingInput: [{
|
|
853
|
+
type: Input,
|
|
854
|
+
args: [{ alias: 'loading', transform: booleanAttribute }]
|
|
855
|
+
}], containerClassInput: [{
|
|
856
|
+
type: Input,
|
|
857
|
+
args: ['containerClass']
|
|
858
|
+
}], containerStyleInput: [{
|
|
859
|
+
type: Input,
|
|
860
|
+
args: ['containerStyle']
|
|
762
861
|
}], overlayComposerInput: [{
|
|
763
862
|
type: Input,
|
|
764
863
|
args: [{ alias: 'overlayComposer', transform: booleanAttribute }]
|
|
@@ -1034,7 +1133,7 @@ class AparteChatBubbleDirective extends AparteElementBase {
|
|
|
1034
1133
|
edit = new EventEmitter();
|
|
1035
1134
|
/** The `‹1/2›` picker moved between sibling versions. */
|
|
1036
1135
|
branchNavigate = new EventEmitter();
|
|
1037
|
-
/** A link in the message body is about to be followed. Cancelable: `preventDefault()` keeps the browser from navigating, so a host can route the link itself. */
|
|
1136
|
+
/** A link in the message body is about to be followed. Cancelable: `preventDefault()` keeps the browser from navigating, so a host can route the link itself. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-link-click)="$event.preventDefault()". */
|
|
1038
1137
|
linkClick = new EventEmitter();
|
|
1039
1138
|
onAttachmentPreview(e) { this.attachmentPreview.emit(e.detail); }
|
|
1040
1139
|
onAction(e) { this.action.emit(e.detail); }
|
|
@@ -1128,6 +1227,8 @@ class AparteChatViewportDirective extends AparteElementBase {
|
|
|
1128
1227
|
set scrollThreshold(v) { this.write('scroll-threshold', v); }
|
|
1129
1228
|
/** Caps how many bubbles stay in the DOM; older ones are released. */
|
|
1130
1229
|
set maxRenderedBubbles(v) { this.write('max-rendered-bubbles', v); }
|
|
1230
|
+
/** The transcript is on its way: a conversation was chosen and its messages are being fetched. The element draws two skeleton turns, marks its scroll surface `aria-busy` and names the wait for a screen reader; `aparte-chat[center-empty]` does not count a loading viewport as empty. Set by `AparteConversationController` through its binding, or by hand from a loop of your own (`setLoading(true)`). */
|
|
1231
|
+
set loading(v) { this.write('loading', v); }
|
|
1131
1232
|
/** The wrapper's explicit hands-off signal: set it and this element builds no wrapper of its own and relocates none of the nodes the FRAMEWORK renders into it, because the framework owns them. Not "none of its children": core's own scroll-to-bottom button is re-appended whenever it stops being last, and that path runs in this mode only. All four wrappers set it. */
|
|
1132
1233
|
set frameworkManaged(v) { this.write('framework-managed', v); }
|
|
1133
1234
|
/** Reflected BY the element while a turn streams: the transcript is read-only meanwhile, and every bubble inside reads it (at connect, and when it changes). The vanilla path derives it from the repository; a framework host sets it through `setTranscriptBusy()`. Read-only from the outside. */
|
|
@@ -1142,7 +1243,7 @@ class AparteChatViewportDirective extends AparteElementBase {
|
|
|
1142
1243
|
onResetDone() { this.resetDone.emit(); }
|
|
1143
1244
|
onPathChanged(e) { this.pathChanged.emit(e.detail); }
|
|
1144
1245
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteChatViewportDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
1145
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "19.2.25", type: AparteChatViewportDirective, isStandalone: true, selector: "aparte-chat-viewport", inputs: { scrollThreshold: ["scrollThreshold", "scrollThreshold", numberAttribute], maxRenderedBubbles: ["maxRenderedBubbles", "maxRenderedBubbles", numberAttribute], frameworkManaged: ["frameworkManaged", "frameworkManaged", booleanAttribute], dataBusy: ["dataBusy", "dataBusy", booleanAttribute] }, outputs: { segmentUpdate: "segmentUpdate", resetDone: "resetDone", pathChanged: "pathChanged" }, host: { listeners: { "aparte-segment-update": "onSegmentUpdate($event)", "aparte-reset-done": "onResetDone()", "aparte-path-changed": "onPathChanged($event)" } }, usesInheritance: true, ngImport: i0 });
|
|
1246
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "19.2.25", type: AparteChatViewportDirective, isStandalone: true, selector: "aparte-chat-viewport", inputs: { scrollThreshold: ["scrollThreshold", "scrollThreshold", numberAttribute], maxRenderedBubbles: ["maxRenderedBubbles", "maxRenderedBubbles", numberAttribute], loading: ["loading", "loading", booleanAttribute], frameworkManaged: ["frameworkManaged", "frameworkManaged", booleanAttribute], dataBusy: ["dataBusy", "dataBusy", booleanAttribute] }, outputs: { segmentUpdate: "segmentUpdate", resetDone: "resetDone", pathChanged: "pathChanged" }, host: { listeners: { "aparte-segment-update": "onSegmentUpdate($event)", "aparte-reset-done": "onResetDone()", "aparte-path-changed": "onPathChanged($event)" } }, usesInheritance: true, ngImport: i0 });
|
|
1146
1247
|
}
|
|
1147
1248
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteChatViewportDirective, decorators: [{
|
|
1148
1249
|
type: Directive,
|
|
@@ -1153,6 +1254,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
1153
1254
|
}], maxRenderedBubbles: [{
|
|
1154
1255
|
type: Input,
|
|
1155
1256
|
args: [{ transform: numberAttribute }]
|
|
1257
|
+
}], loading: [{
|
|
1258
|
+
type: Input,
|
|
1259
|
+
args: [{ transform: booleanAttribute }]
|
|
1156
1260
|
}], frameworkManaged: [{
|
|
1157
1261
|
type: Input,
|
|
1158
1262
|
args: [{ transform: booleanAttribute }]
|
|
@@ -1242,7 +1346,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
1242
1346
|
}] } });
|
|
1243
1347
|
/** `<aparte-composer-action>` — Generic action button primitive for <aparte-composer>. */
|
|
1244
1348
|
class AparteComposerActionDirective extends AparteElementBase {
|
|
1245
|
-
/** Icon key for aparteGlobalConfig.getIcon(), or raw SVG/HTML starting with
|
|
1349
|
+
/** Icon key for aparteGlobalConfig.getIcon(), or raw SVG/HTML starting with `<`. A key core's glyph set does not hold renders AS TEXT (`icon="star"` draws the word *star*), so bring your own set through `setIconProvider` or pass the SVG. */
|
|
1246
1350
|
set icon(v) { this.write('icon', v); }
|
|
1247
1351
|
/** Accessible label (also used as tooltip) */
|
|
1248
1352
|
set label(v) { this.write('label', v); }
|
|
@@ -1457,79 +1561,89 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
1457
1561
|
type: HostListener,
|
|
1458
1562
|
args: ['aparte-compact', ['$event']]
|
|
1459
1563
|
}] } });
|
|
1460
|
-
/** `<aparte-conversation-list>` — Conversation-history sidebar — a framework-agnostic web component. The host sets the `conversations` JS property and the `active-id` attribute; this renders the rows and fires the user's intent
|
|
1564
|
+
/** `<aparte-conversation-list>` — Conversation-history sidebar — a framework-agnostic web component. The host sets the `conversations` JS property and the `active-id` attribute; this renders the rows and fires the user's intent. Acting on it is the host's, unless `manage` hands the row menu's writes to the registered conversation manager. */
|
|
1461
1565
|
class AparteConversationListDirective extends AparteElementBase {
|
|
1462
1566
|
/** The id of the conversation to render as selected. */
|
|
1463
1567
|
set activeId(v) { this.write('active-id', v); }
|
|
1464
1568
|
/** Render the rows flat, in host order, with no date headings. */
|
|
1465
|
-
set
|
|
1466
|
-
/** The
|
|
1467
|
-
|
|
1468
|
-
/**
|
|
1469
|
-
|
|
1470
|
-
/** The same item on an already-archived one; same detail shape, opposite intent. */
|
|
1471
|
-
unarchiveConversation = new EventEmitter();
|
|
1472
|
-
/** The archive item was chosen on a live conversation. */
|
|
1473
|
-
archiveConversation = new EventEmitter();
|
|
1474
|
-
/** The delete was confirmed. Nothing is removed here. */
|
|
1475
|
-
deleteConversation = new EventEmitter();
|
|
1476
|
-
/** A rename was committed with a new, non-empty title. Nothing is renamed here. */
|
|
1477
|
-
renameConversation = new EventEmitter();
|
|
1569
|
+
set flat(v) { this.write('flat', v); }
|
|
1570
|
+
/** The list is on its way: six skeleton rows stand where the rows will be, the element is `aria-busy` and a visually hidden line names the wait. A list that is not there yet is not an empty list. Set it while your store answers, clear it when you assign `conversations`. */
|
|
1571
|
+
set loading(v) { this.write('loading', v); }
|
|
1572
|
+
/** Let the list finish the gesture: rename, pin, unpin, archive, unarchive and delete are performed on the conversation manager registered with `setConversationManager()`, instead of leaving you six listeners to write. The event still fires first, and `preventDefault()` on it takes the gesture back. Off by default, and selecting stays yours either way. */
|
|
1573
|
+
set manage(v) { this.write('manage', v); }
|
|
1478
1574
|
/** A row's title was activated; the host loads that conversation. */
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1575
|
+
conversationSelect = new EventEmitter();
|
|
1576
|
+
/** A rename was committed with a new, non-empty title. Cancelable: `preventDefault()` takes the gesture back from `manage`. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-conversation-rename)="$event.preventDefault()". */
|
|
1577
|
+
conversationRename = new EventEmitter();
|
|
1578
|
+
/** The pin item was chosen on an unpinned row. Cancelable: `preventDefault()` takes the gesture back from `manage`. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-conversation-pin)="$event.preventDefault()". */
|
|
1579
|
+
conversationPin = new EventEmitter();
|
|
1580
|
+
/** The same item on a pinned row; same detail shape, opposite intent. Cancelable, like the rest. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-conversation-unpin)="$event.preventDefault()". */
|
|
1581
|
+
conversationUnpin = new EventEmitter();
|
|
1582
|
+
/** The archive item was chosen on a live conversation. Cancelable: `preventDefault()` takes the gesture back from `manage`. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-conversation-archive)="$event.preventDefault()". */
|
|
1583
|
+
conversationArchive = new EventEmitter();
|
|
1584
|
+
/** The same item on an already-archived one; same detail shape, opposite intent. Cancelable, like the rest. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-conversation-unarchive)="$event.preventDefault()". */
|
|
1585
|
+
conversationUnarchive = new EventEmitter();
|
|
1586
|
+
/** The delete was confirmed. Cancelable: `preventDefault()` takes the gesture back from `manage`. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-conversation-delete)="$event.preventDefault()". */
|
|
1587
|
+
conversationDelete = new EventEmitter();
|
|
1588
|
+
onConversationSelect(e) { this.conversationSelect.emit(e.detail); }
|
|
1589
|
+
onConversationRename(e) { this.conversationRename.emit(e.detail); }
|
|
1590
|
+
onConversationPin(e) { this.conversationPin.emit(e.detail); }
|
|
1591
|
+
onConversationUnpin(e) { this.conversationUnpin.emit(e.detail); }
|
|
1592
|
+
onConversationArchive(e) { this.conversationArchive.emit(e.detail); }
|
|
1593
|
+
onConversationUnarchive(e) { this.conversationUnarchive.emit(e.detail); }
|
|
1594
|
+
onConversationDelete(e) { this.conversationDelete.emit(e.detail); }
|
|
1487
1595
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteConversationListDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
1488
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "19.2.25", type: AparteConversationListDirective, isStandalone: true, selector: "aparte-conversation-list", inputs: { activeId: "activeId",
|
|
1596
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "19.2.25", type: AparteConversationListDirective, isStandalone: true, selector: "aparte-conversation-list", inputs: { activeId: "activeId", flat: ["flat", "flat", booleanAttribute], loading: ["loading", "loading", booleanAttribute], manage: ["manage", "manage", booleanAttribute] }, outputs: { conversationSelect: "conversationSelect", conversationRename: "conversationRename", conversationPin: "conversationPin", conversationUnpin: "conversationUnpin", conversationArchive: "conversationArchive", conversationUnarchive: "conversationUnarchive", conversationDelete: "conversationDelete" }, host: { listeners: { "aparte-conversation-select": "onConversationSelect($event)", "aparte-conversation-rename": "onConversationRename($event)", "aparte-conversation-pin": "onConversationPin($event)", "aparte-conversation-unpin": "onConversationUnpin($event)", "aparte-conversation-archive": "onConversationArchive($event)", "aparte-conversation-unarchive": "onConversationUnarchive($event)", "aparte-conversation-delete": "onConversationDelete($event)" } }, usesInheritance: true, ngImport: i0 });
|
|
1489
1597
|
}
|
|
1490
1598
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteConversationListDirective, decorators: [{
|
|
1491
1599
|
type: Directive,
|
|
1492
1600
|
args: [{ selector: 'aparte-conversation-list', standalone: true }]
|
|
1493
1601
|
}], propDecorators: { activeId: [{
|
|
1494
1602
|
type: Input
|
|
1495
|
-
}],
|
|
1603
|
+
}], flat: [{
|
|
1604
|
+
type: Input,
|
|
1605
|
+
args: [{ transform: booleanAttribute }]
|
|
1606
|
+
}], loading: [{
|
|
1607
|
+
type: Input,
|
|
1608
|
+
args: [{ transform: booleanAttribute }]
|
|
1609
|
+
}], manage: [{
|
|
1496
1610
|
type: Input,
|
|
1497
1611
|
args: [{ transform: booleanAttribute }]
|
|
1498
|
-
}],
|
|
1612
|
+
}], conversationSelect: [{
|
|
1499
1613
|
type: Output
|
|
1500
|
-
}],
|
|
1614
|
+
}], conversationRename: [{
|
|
1501
1615
|
type: Output
|
|
1502
|
-
}],
|
|
1616
|
+
}], conversationPin: [{
|
|
1503
1617
|
type: Output
|
|
1504
|
-
}],
|
|
1618
|
+
}], conversationUnpin: [{
|
|
1505
1619
|
type: Output
|
|
1506
|
-
}],
|
|
1620
|
+
}], conversationArchive: [{
|
|
1507
1621
|
type: Output
|
|
1508
|
-
}],
|
|
1622
|
+
}], conversationUnarchive: [{
|
|
1509
1623
|
type: Output
|
|
1510
|
-
}],
|
|
1624
|
+
}], conversationDelete: [{
|
|
1511
1625
|
type: Output
|
|
1512
|
-
}],
|
|
1626
|
+
}], onConversationSelect: [{
|
|
1513
1627
|
type: HostListener,
|
|
1514
|
-
args: ['aparte-
|
|
1515
|
-
}],
|
|
1628
|
+
args: ['aparte-conversation-select', ['$event']]
|
|
1629
|
+
}], onConversationRename: [{
|
|
1516
1630
|
type: HostListener,
|
|
1517
|
-
args: ['aparte-
|
|
1518
|
-
}],
|
|
1631
|
+
args: ['aparte-conversation-rename', ['$event']]
|
|
1632
|
+
}], onConversationPin: [{
|
|
1519
1633
|
type: HostListener,
|
|
1520
|
-
args: ['aparte-
|
|
1521
|
-
}],
|
|
1634
|
+
args: ['aparte-conversation-pin', ['$event']]
|
|
1635
|
+
}], onConversationUnpin: [{
|
|
1522
1636
|
type: HostListener,
|
|
1523
|
-
args: ['aparte-
|
|
1524
|
-
}],
|
|
1637
|
+
args: ['aparte-conversation-unpin', ['$event']]
|
|
1638
|
+
}], onConversationArchive: [{
|
|
1525
1639
|
type: HostListener,
|
|
1526
|
-
args: ['aparte-
|
|
1527
|
-
}],
|
|
1640
|
+
args: ['aparte-conversation-archive', ['$event']]
|
|
1641
|
+
}], onConversationUnarchive: [{
|
|
1528
1642
|
type: HostListener,
|
|
1529
|
-
args: ['aparte-
|
|
1530
|
-
}],
|
|
1643
|
+
args: ['aparte-conversation-unarchive', ['$event']]
|
|
1644
|
+
}], onConversationDelete: [{
|
|
1531
1645
|
type: HostListener,
|
|
1532
|
-
args: ['aparte-
|
|
1646
|
+
args: ['aparte-conversation-delete', ['$event']]
|
|
1533
1647
|
}] } });
|
|
1534
1648
|
/** `<aparte-elicitation>` — The default presenter for a request to the human. It renders nothing itself: it registers as the presenter for the config governing its subtree, and mounts a panel inside the nearest `<aparte-composer>` when something asks — a tool handler calling `requestUserInput`, or core's own approval gate. */
|
|
1535
1649
|
/**
|
|
@@ -1644,7 +1758,7 @@ class AparteScrollRailDirective extends AparteElementBase {
|
|
|
1644
1758
|
set every(v) { this.write('every', v); }
|
|
1645
1759
|
/** Reflected BY the element while it has fewer than two ticks. Read-only. */
|
|
1646
1760
|
set dataEmpty(v) { this.write('data-empty', v); }
|
|
1647
|
-
/** A tick was activated. Bubbles, cancelable: `preventDefault()` leaves the transcript where it is. */
|
|
1761
|
+
/** A tick was activated. Bubbles, cancelable: `preventDefault()` leaves the transcript where it is. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-scroll-rail-jump)="$event.preventDefault()". */
|
|
1648
1762
|
scrollRailJump = new EventEmitter();
|
|
1649
1763
|
onScrollRailJump(e) { this.scrollRailJump.emit(e.detail); }
|
|
1650
1764
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteScrollRailDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -1827,7 +1941,7 @@ class AparteSuggestionsDirective extends AparteElementBase {
|
|
|
1827
1941
|
set emptyOnly(v) { this.write('empty-only', v); }
|
|
1828
1942
|
/** Reflected BY the element while it has no suggestion to show; the stylesheet hides it then. Read-only, do not set it yourself. */
|
|
1829
1943
|
set dataEmpty(v) { this.write('data-empty', v); }
|
|
1830
|
-
/** A starter was clicked. Bubbles, and is cancelable: `preventDefault()` stops the composer from being filled or submitted. */
|
|
1944
|
+
/** A starter was clicked. Bubbles, and is cancelable: `preventDefault()` stops the composer from being filled or submitted. Angular: this Output emits the detail, so preventDefault() is not on it — bind the DOM event for that: (aparte-suggestion)="$event.preventDefault()". */
|
|
1831
1945
|
suggestion = new EventEmitter();
|
|
1832
1946
|
onSuggestion(e) { this.suggestion.emit(e.detail); }
|
|
1833
1947
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteSuggestionsDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -1965,7 +2079,7 @@ class ConversationManagerService {
|
|
|
1965
2079
|
// The active conversation is owned exclusively by AparteConversationController
|
|
1966
2080
|
// (inside <aparte-chat>). To switch conversations, either bind
|
|
1967
2081
|
// `[conversationId]` on <aparte-chat> or dispatch a window event:
|
|
1968
|
-
// window.dispatchEvent(new CustomEvent('aparte-select
|
|
2082
|
+
// window.dispatchEvent(new CustomEvent('aparte-conversation-select', {
|
|
1969
2083
|
// detail: { id, targetId? },
|
|
1970
2084
|
// }));
|
|
1971
2085
|
// The `activeId` signal here is read-only and stays in sync via subscribe().
|