@aparte/angular 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/dist/README.md +12 -9
- package/dist/fesm2022/aparte-angular.mjs +298 -132
- package/dist/fesm2022/aparte-angular.mjs.map +1 -1
- package/dist/generated/element.directives.d.ts +4 -2
- package/dist/index.d.ts +3 -1
- package/dist/lib/aparte-bubble.directive.d.ts +27 -0
- package/dist/lib/aparte-chat.component.d.ts +58 -51
- package/dist/lib/aparte-tool-ui.directive.d.ts +63 -0
- package/dist/lib/aparte-ui.component.d.ts +7 -0
- package/package.json +8 -7
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, Injectable, makeEnvironmentProviders, provideAppInitializer, DestroyRef, effect, ElementRef, signal, computed,
|
|
3
|
-
import { AparteClient, aparteGlobalConfig, isAwaitingReply, uuid, AparteChatHost, APARTE_DEFAULT_UI_EVENTS, applyElementProps, AparteConversationManager } from '@aparte/core';
|
|
2
|
+
import { InjectionToken, inject, Injectable, makeEnvironmentProviders, provideAppInitializer, DestroyRef, TemplateRef, Directive, effect, untracked, afterNextRender, ElementRef, Injector, PLATFORM_ID, input, linkedSignal, booleanAttribute, signal, computed, numberAttribute, contentChild, output, ViewChildren, ViewChild, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, Component, ViewContainerRef, Renderer2, Input, EventEmitter, HostListener, Output } from '@angular/core';
|
|
3
|
+
import { AparteClient, aparteGlobalConfig, isAwaitingReply, releaseDeferredHydration, uuid, AparteChatHost, defineMountedToolRenderer, defineMountedSegmentRenderer, overrideSegmentRenderer, APARTE_DEFAULT_UI_EVENTS, applyElementProps, AparteConversationManager } from '@aparte/core';
|
|
4
4
|
export { aparteGlobalConfig } from '@aparte/core';
|
|
5
|
-
import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
|
|
5
|
+
import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Injection token for AparteClient configuration. Provided by `provideAparte()`;
|
|
@@ -210,7 +210,37 @@ function provideAparte(options = {}) {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
/**
|
|
213
|
-
*
|
|
213
|
+
* Marks the template `<aparte-chat>` draws each message with, in place of
|
|
214
|
+
* `<aparte-chat-bubble>`: write it as content of the chat, the message as `$implicit`.
|
|
215
|
+
*
|
|
216
|
+
* ```html
|
|
217
|
+
* <aparte-chat [messages]="messages">
|
|
218
|
+
* <ng-template aparteBubble let-message>
|
|
219
|
+
* <app-bubble [message]="message" />
|
|
220
|
+
* </ng-template>
|
|
221
|
+
* </aparte-chat>
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* The same thing as passing a `TemplateRef` to `[bubbleTemplate]`, written where Angular
|
|
225
|
+
* developers write a per-item template. When both are given, `[bubbleTemplate]` wins.
|
|
226
|
+
* The built-in action bar (retry, edit, the version picker) belongs to the native bubble;
|
|
227
|
+
* a template of yours owns whatever it wires.
|
|
228
|
+
*/
|
|
229
|
+
class AparteBubbleDirective {
|
|
230
|
+
template = inject(TemplateRef);
|
|
231
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteBubbleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
232
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.25", type: AparteBubbleDirective, isStandalone: true, selector: "ng-template[aparteBubble]", ngImport: i0 });
|
|
233
|
+
}
|
|
234
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteBubbleDirective, decorators: [{
|
|
235
|
+
type: Directive,
|
|
236
|
+
args: [{
|
|
237
|
+
selector: 'ng-template[aparteBubble]',
|
|
238
|
+
standalone: true,
|
|
239
|
+
}]
|
|
240
|
+
}] });
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* AparteChatComponent — the Angular wrapper (19.2 to 21, zone or zoneless)
|
|
214
244
|
*
|
|
215
245
|
* Standalone component wrapping aparté Web Components with Angular Signals.
|
|
216
246
|
* The streaming / branch-navigation / host-method orchestration (orphan-stream
|
|
@@ -232,42 +262,83 @@ class AparteChatComponent {
|
|
|
232
262
|
// bubbles `bubbleRefs.changes` hid the divergence; under a custom
|
|
233
263
|
// `[bubbleTemplate]` that query never fires and this was the only path.
|
|
234
264
|
this.messages();
|
|
235
|
-
|
|
265
|
+
// A server render has no frame to wait for and no host to sync.
|
|
266
|
+
if (!this._isBrowser || this._syncRafId !== null)
|
|
236
267
|
return;
|
|
237
268
|
this._syncRafId = requestAnimationFrame(() => {
|
|
238
269
|
this._syncRafId = null;
|
|
239
270
|
this._host?.syncBubbles();
|
|
240
271
|
});
|
|
241
272
|
});
|
|
273
|
+
// A parent that empties [messages] starts a new conversation: the host lets go of
|
|
274
|
+
// its render cache then — the same rule React, Vue and Svelte apply.
|
|
275
|
+
effect(() => {
|
|
276
|
+
const next = this.messagesInput();
|
|
277
|
+
// An echo of the chat's own emptied list is not a parent starting over.
|
|
278
|
+
if (next.length === 0 && !this._emitted.has(next))
|
|
279
|
+
untracked(() => this._host?.clearRenderCache());
|
|
280
|
+
});
|
|
281
|
+
// [conversationId] drives the host; an echo of the id the host already holds
|
|
282
|
+
// (e.g. the one it just created and reported) is a no-op.
|
|
283
|
+
effect(() => {
|
|
284
|
+
const next = this.conversationIdInput() ?? null;
|
|
285
|
+
untracked(() => {
|
|
286
|
+
if (next === this._conversationId)
|
|
287
|
+
return;
|
|
288
|
+
this._conversationId = next;
|
|
289
|
+
if (this._host)
|
|
290
|
+
void this._host.setConversationId(next);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
// The host touches the DOM and the custom elements: browser only, never in a server
|
|
294
|
+
// render, where there is no element to drive. Built in ngAfterViewInit (below), so a
|
|
295
|
+
// parent's own ngAfterViewInit, which runs after it, can already call the API; this
|
|
296
|
+
// render hook is the fallback, and does nothing once the host exists.
|
|
297
|
+
afterNextRender(() => this._createHost());
|
|
298
|
+
}
|
|
299
|
+
ngAfterViewInit() {
|
|
300
|
+
if (this._isBrowser)
|
|
301
|
+
this._createHost();
|
|
242
302
|
}
|
|
243
303
|
elementRef = inject(ElementRef);
|
|
304
|
+
_injector = inject(Injector);
|
|
305
|
+
_isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
244
306
|
/**
|
|
245
307
|
* Template bridge to core's shared `isAwaitingReply` (an Angular template can't
|
|
246
308
|
* call an imported function). One rule for the four wrappers and core itself,
|
|
247
309
|
* so "is this bubble in flight" can't drift between them.
|
|
248
310
|
*/
|
|
249
311
|
awaitingReply = isAwaitingReply;
|
|
250
|
-
// ──
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
312
|
+
// ── Inputs ───────────────────────────────────────────────────────────────
|
|
313
|
+
//
|
|
314
|
+
// Signal inputs under the same public names as before (`[messages]`, `disabled`,
|
|
315
|
+
// `centerWhenEmpty`…), so no template changes. Where the chat also writes a value
|
|
316
|
+
// itself (the list, the typing line), the input feeds a `linkedSignal`: a parent write
|
|
317
|
+
// resets it, the chat's own writes move it, and neither emits — `messagesChange` is
|
|
318
|
+
// emitted by the host's report only, never by a `set` (which is why this is not a
|
|
319
|
+
// `model()`: a model emits on every write, and an optimistic append must stay silent).
|
|
320
|
+
/** Messages to display. Two-way: `[(messages)]`, with `(messagesChange)` reporting the chat's own changes. */
|
|
321
|
+
messagesInput = input([], { alias: 'messages' });
|
|
322
|
+
/** Every list the host reported — read only by the render-cache rule below. */
|
|
323
|
+
_emitted = new WeakSet();
|
|
324
|
+
/**
|
|
325
|
+
* Under `[(messages)]` each report comes back through the input — possibly an EARLIER one,
|
|
326
|
+
* after the chat appended without reporting (an optimistic append, or core's append right
|
|
327
|
+
* after a flush) — and an echo is not the parent's word: it keeps the local list. The host
|
|
328
|
+
* tells them apart (`takeEcho`), the rule React, Vue and Svelte apply, so a list the parent
|
|
329
|
+
* restores later (a reopened conversation, an undo) is applied. Before the host exists
|
|
330
|
+
* nothing was reported, so the input is taken as it is.
|
|
331
|
+
*/
|
|
332
|
+
messages = linkedSignal({
|
|
333
|
+
source: this.messagesInput,
|
|
334
|
+
computation: (next, previous) => (previous && this._host?.takeEcho(next) ? previous.value : next),
|
|
335
|
+
});
|
|
262
336
|
/** Input placeholder text */
|
|
263
|
-
|
|
264
|
-
placeholder = signal('Type a message...');
|
|
337
|
+
placeholder = input('Type a message...');
|
|
265
338
|
/** Whether the input is disabled */
|
|
266
|
-
|
|
267
|
-
disabled = signal(false);
|
|
339
|
+
disabled = input(false, { transform: booleanAttribute });
|
|
268
340
|
/** When false, Shift+Enter submits and a bare Enter inserts a newline. */
|
|
269
|
-
|
|
270
|
-
submitOnEnter = signal(true);
|
|
341
|
+
submitOnEnter = input(true, { transform: booleanAttribute });
|
|
271
342
|
/**
|
|
272
343
|
* Opt in to the "centered composer when empty" layout: the composer sits
|
|
273
344
|
* vertically centered with the `[slot='empty-state']` content above it while
|
|
@@ -275,23 +346,29 @@ class AparteChatComponent {
|
|
|
275
346
|
* Off by default — additive (adds the `--auto-center` modifier + a
|
|
276
347
|
* `data-aparte-empty` attribute the shipped `aparte.css` recipe keys off).
|
|
277
348
|
*/
|
|
278
|
-
|
|
279
|
-
centerWhenEmpty = signal(false);
|
|
349
|
+
centerWhenEmpty = input(false, { transform: booleanAttribute });
|
|
280
350
|
/**
|
|
281
351
|
* The conversation is on its way: the viewport draws two skeleton turns, the empty
|
|
282
352
|
* state stays off and the composer is disabled until it lands. The conversation
|
|
283
353
|
* controller sets this itself while it fetches through your adapter's `loadFull()`;
|
|
284
354
|
* the input is for a wait of your own. Off by default.
|
|
285
355
|
*/
|
|
286
|
-
|
|
287
|
-
loading = signal(false);
|
|
356
|
+
loading = input(false, { transform: booleanAttribute });
|
|
288
357
|
/** The controller's wait (a conversation fetched through the adapter). */
|
|
289
358
|
hostLoading = signal(false);
|
|
290
359
|
waiting = computed(() => this.loading() || this.hostLoading());
|
|
291
360
|
/** Bumped on every config change, so the two locale strings below follow a language switch under OnPush. */
|
|
292
361
|
_localeTick = signal(0);
|
|
362
|
+
/**
|
|
363
|
+
* True until the host is built — on the server always, and in the browser only while Angular
|
|
364
|
+
* hydrates the server's HTML: the host element is then the one the server sent, still carrying
|
|
365
|
+
* the attribute. A client-only render holds nothing, so a component projected into the empty
|
|
366
|
+
* state (built in an @if, after the host binding) talks to live elements in its own
|
|
367
|
+
* ngAfterViewInit, which runs before this component's.
|
|
368
|
+
*/
|
|
369
|
+
hydrating = signal(!this._isBrowser || this.elementRef.nativeElement.hasAttribute('defer-hydration'));
|
|
293
370
|
_unsubscribeLocale;
|
|
294
|
-
get loadingText() { this._localeTick(); return (this.config ?? aparteGlobalConfig).t('loadingConversation'); }
|
|
371
|
+
get loadingText() { this._localeTick(); return (this.config() ?? aparteGlobalConfig).t('loadingConversation'); }
|
|
295
372
|
/**
|
|
296
373
|
* Class(es) merged onto the INNER `.aparte-chat-container`, not onto this
|
|
297
374
|
* component's own `<aparte-chat>` host.
|
|
@@ -304,11 +381,12 @@ class AparteChatComponent {
|
|
|
304
381
|
* selector on Angular alone. This input is that parity. Additive: the recipe's
|
|
305
382
|
* own classes stay.
|
|
306
383
|
*/
|
|
307
|
-
|
|
308
|
-
containerClass = signal('');
|
|
384
|
+
containerClass = input('', { alias: 'containerClass', transform: (val) => val ?? '' });
|
|
309
385
|
/** Inline style for the same div — a string or a `{ prop: value }` map. */
|
|
310
|
-
|
|
311
|
-
|
|
386
|
+
containerStyle = input(null, {
|
|
387
|
+
alias: 'containerStyle',
|
|
388
|
+
transform: (val) => val ?? null,
|
|
389
|
+
});
|
|
312
390
|
/**
|
|
313
391
|
* Overlay the composer on the transcript (the ChatGPT anatomy): the scroll
|
|
314
392
|
* surface spans the whole column, the scrollbar runs edge to edge, and the
|
|
@@ -317,8 +395,7 @@ class AparteChatComponent {
|
|
|
317
395
|
* core's recipe and the viewport's measurement both key on the attribute
|
|
318
396
|
* there. Read when the viewport mounts.
|
|
319
397
|
*/
|
|
320
|
-
|
|
321
|
-
overlayComposer = signal(false);
|
|
398
|
+
overlayComposer = input(false, { transform: booleanAttribute });
|
|
322
399
|
/**
|
|
323
400
|
* Add the file picker + chips strip to the default composer shell. **Off by
|
|
324
401
|
* default**, because the capability needs a host that consumes the files: an
|
|
@@ -328,8 +405,7 @@ class AparteChatComponent {
|
|
|
328
405
|
* project a `[slot='composer']` (drop `<aparte-composer-add-attachment>` and
|
|
329
406
|
* `<aparte-composer-attachments>` in it yourself).
|
|
330
407
|
*/
|
|
331
|
-
|
|
332
|
-
attachments = signal(false);
|
|
408
|
+
attachments = input(false, { transform: booleanAttribute });
|
|
333
409
|
/**
|
|
334
410
|
* Render the `<aparte-elicitation>` presenter inside the host — on by default, as
|
|
335
411
|
* it is in `<aparte-chat>`: the built-in approval gate and `requestUserInput()`
|
|
@@ -337,51 +413,45 @@ class AparteChatComponent {
|
|
|
337
413
|
* when you mount a presenter of your own (`setElicitationPresenter`) or place the
|
|
338
414
|
* element yourself.
|
|
339
415
|
*/
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
isTyping = signal(false);
|
|
416
|
+
elicitation = input(true, { transform: booleanAttribute });
|
|
417
|
+
/** Whether the assistant is currently typing/streaming. The host turns it off on the first token. */
|
|
418
|
+
isTypingInput = input(false, { alias: 'isTyping', transform: booleanAttribute });
|
|
419
|
+
isTyping = linkedSignal(() => this.isTypingInput());
|
|
345
420
|
/** Text to show in the typing status */
|
|
346
|
-
|
|
347
|
-
typingText = signal(undefined);
|
|
421
|
+
typingText = input(undefined);
|
|
348
422
|
/** The status line's word: the input, else the locale's, like the rest of the chat. */
|
|
349
|
-
get typingLabel() { this._localeTick(); return this.typingText() ?? (this.config ?? aparteGlobalConfig).t('typing'); }
|
|
350
|
-
/** Duration in ms to freeze spacer recalculation after a conversation swap. */
|
|
351
|
-
layoutTransitionMs = 0;
|
|
423
|
+
get typingLabel() { this._localeTick(); return this.typingText() ?? (this.config() ?? aparteGlobalConfig).t('typing'); }
|
|
424
|
+
/** Duration in ms to freeze spacer recalculation after a conversation swap. Read when the host is created. */
|
|
425
|
+
layoutTransitionMs = input(0, { transform: numberAttribute });
|
|
352
426
|
/**
|
|
353
427
|
* Active conversation id. When provided, the host attaches an
|
|
354
428
|
* `AparteConversationController` that loads/persists messages via the
|
|
355
429
|
* `AparteConversationManager` registered in `aparteGlobalConfig`. Setting a different id
|
|
356
430
|
* mid-stream aborts the previous request; `null` deselects.
|
|
357
431
|
*/
|
|
358
|
-
|
|
359
|
-
const next = val ?? null;
|
|
360
|
-
if (next === this._conversationId)
|
|
361
|
-
return;
|
|
362
|
-
this._conversationId = next;
|
|
363
|
-
if (this._host)
|
|
364
|
-
void this._host.setConversationId(next);
|
|
365
|
-
}
|
|
432
|
+
conversationIdInput = input(undefined, { alias: 'conversationId' });
|
|
366
433
|
_conversationId = null;
|
|
367
434
|
/**
|
|
368
435
|
* Instance {@link AparteConfig} for this chat. When set, aparté components
|
|
369
436
|
* inside resolve THIS config instead of `aparteGlobalConfig`, so
|
|
370
437
|
* several independently-configured chats can coexist on one page. Omit for the
|
|
371
|
-
* global config. Read once
|
|
438
|
+
* global config. Read once, when the host is created after the first render.
|
|
372
439
|
*/
|
|
373
|
-
config;
|
|
440
|
+
config = input(undefined);
|
|
374
441
|
/**
|
|
375
442
|
* Render your OWN element per message in place of `<aparte-chat-bubble>`.
|
|
376
|
-
* Opt-in — provide a `<ng-template let-message>` and pass its ref
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
443
|
+
* Opt-in — provide a `<ng-template let-message>` and pass its ref, or write the
|
|
444
|
+
* template as content with {@link AparteBubbleDirective} (`<ng-template aparteBubble
|
|
445
|
+
* let-message>`); this input wins when both are given. Driven by the reactive
|
|
446
|
+
* `messages()` signal, so it updates live during streaming. The built-in action bar
|
|
447
|
+
* (retry/edit/branch) belongs to the native bubble; a custom bubble owns whatever it wires.
|
|
380
448
|
* @example
|
|
381
449
|
* <aparte-chat [bubbleTemplate]="tpl" />
|
|
382
450
|
* <ng-template #tpl let-message>{{ message.content }}</ng-template>
|
|
383
451
|
*/
|
|
384
|
-
bubbleTemplate;
|
|
452
|
+
bubbleTemplate = input(undefined);
|
|
453
|
+
/** The `<ng-template aparteBubble>` written as content, if any. */
|
|
454
|
+
bubbleDirective = contentChild(AparteBubbleDirective);
|
|
385
455
|
// ── Outputs ────────────────────────────────────────────────────────────
|
|
386
456
|
/**
|
|
387
457
|
* User submitted a message from the composer. It is **appended to the thread
|
|
@@ -415,14 +485,21 @@ class AparteChatComponent {
|
|
|
415
485
|
isStreaming = computed(() => this._streamingId() !== null);
|
|
416
486
|
/** rAF id used to coalesce rapid signal updates during streaming */
|
|
417
487
|
_syncRafId = null;
|
|
418
|
-
/** The framework-agnostic chat-host orchestrator (created in
|
|
488
|
+
/** The framework-agnostic chat-host orchestrator (created after the first render, in the browser). */
|
|
419
489
|
_host;
|
|
420
490
|
_unbindHost;
|
|
421
491
|
/** QueryList.changes subscription — released in ngOnDestroy so SPA route
|
|
422
492
|
* churn doesn't leak a live subscription per mount. */
|
|
423
493
|
_bubbleRefsSub;
|
|
424
|
-
|
|
425
|
-
|
|
494
|
+
/** Set by ngOnDestroy: an afterNextRender that lands after a destroy must not build a host. */
|
|
495
|
+
_destroyed = false;
|
|
496
|
+
_createHost() {
|
|
497
|
+
if (this._destroyed || this._host)
|
|
498
|
+
return;
|
|
499
|
+
// The elements a server render left waiting are upgraded first: the host talks to them.
|
|
500
|
+
releaseDeferredHydration(this.elementRef.nativeElement);
|
|
501
|
+
this.hydrating.set(false);
|
|
502
|
+
this._unsubscribeLocale = (this.config() ?? aparteGlobalConfig).subscribe(() => this._localeTick.update((n) => n + 1));
|
|
426
503
|
const host = this.elementRef.nativeElement;
|
|
427
504
|
// Ensure a stable id so aparte-composer can reference the host via `target`,
|
|
428
505
|
// letting AparteClient find it without DOM traversal across re-renders.
|
|
@@ -437,25 +514,29 @@ class AparteChatComponent {
|
|
|
437
514
|
viewport: this.viewportRef?.nativeElement ?? null,
|
|
438
515
|
getMessages: () => this.messages(),
|
|
439
516
|
setMessages: (msgs) => this.messages.set(msgs),
|
|
440
|
-
onMessagesChange: (msgs) =>
|
|
517
|
+
onMessagesChange: (msgs) => {
|
|
518
|
+
this._emitted.add(msgs);
|
|
519
|
+
this.messagesChange.emit(msgs);
|
|
520
|
+
},
|
|
441
521
|
onMessageAppended: (msg) => this.messageAppended.emit(msg),
|
|
442
522
|
onTypingChange: (typing) => { this.isTyping.set(typing); this.typingChange.emit(typing); },
|
|
443
523
|
onStreamingChange: (id) => this._streamingId.set(id),
|
|
444
524
|
onLoadingChange: (on) => this.hostLoading.set(on),
|
|
445
|
-
// Sibling-info / deferred work runs after Angular has re-rendered
|
|
446
|
-
|
|
525
|
+
// Sibling-info / deferred work runs after Angular has re-rendered — Angular's
|
|
526
|
+
// own hook for exactly that, which also holds under zoneless.
|
|
527
|
+
afterRender: (cb) => { afterNextRender(cb, { injector: this._injector }); },
|
|
447
528
|
resetComposer: () => {
|
|
448
529
|
this.inputRef?.nativeElement?.reset?.();
|
|
449
530
|
},
|
|
450
531
|
};
|
|
451
532
|
this._host = new AparteChatHost(binding, {
|
|
452
|
-
layoutTransitionMs: this.layoutTransitionMs,
|
|
533
|
+
layoutTransitionMs: this.layoutTransitionMs(),
|
|
453
534
|
conversationId: this._conversationId,
|
|
454
535
|
onConversationCreated: (id) => {
|
|
455
536
|
this._conversationId = id;
|
|
456
537
|
this.conversationCreated.emit(id);
|
|
457
538
|
},
|
|
458
|
-
config: this.config,
|
|
539
|
+
config: this.config(),
|
|
459
540
|
});
|
|
460
541
|
this._unbindHost = this._host.bind();
|
|
461
542
|
// Custom bubble actions bubble to the host as `aparte-action`; surface them as
|
|
@@ -470,6 +551,7 @@ class AparteChatComponent {
|
|
|
470
551
|
this.action.emit(event.detail);
|
|
471
552
|
};
|
|
472
553
|
ngOnDestroy() {
|
|
554
|
+
this._destroyed = true;
|
|
473
555
|
this._unsubscribeLocale?.();
|
|
474
556
|
this._unsubscribeLocale = undefined;
|
|
475
557
|
if (this._syncRafId !== null) {
|
|
@@ -641,7 +723,7 @@ class AparteChatComponent {
|
|
|
641
723
|
this.messageSent.emit(detail);
|
|
642
724
|
}
|
|
643
725
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
644
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: AparteChatComponent, isStandalone: true, selector: "aparte-chat", inputs: { messagesInput:
|
|
726
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.25", type: AparteChatComponent, isStandalone: true, selector: "aparte-chat", inputs: { messagesInput: { classPropertyName: "messagesInput", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, submitOnEnter: { classPropertyName: "submitOnEnter", publicName: "submitOnEnter", isSignal: true, isRequired: false, transformFunction: null }, centerWhenEmpty: { classPropertyName: "centerWhenEmpty", publicName: "centerWhenEmpty", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, containerClass: { classPropertyName: "containerClass", publicName: "containerClass", isSignal: true, isRequired: false, transformFunction: null }, containerStyle: { classPropertyName: "containerStyle", publicName: "containerStyle", isSignal: true, isRequired: false, transformFunction: null }, overlayComposer: { classPropertyName: "overlayComposer", publicName: "overlayComposer", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, elicitation: { classPropertyName: "elicitation", publicName: "elicitation", isSignal: true, isRequired: false, transformFunction: null }, isTypingInput: { classPropertyName: "isTypingInput", publicName: "isTyping", isSignal: true, isRequired: false, transformFunction: null }, typingText: { classPropertyName: "typingText", publicName: "typingText", isSignal: true, isRequired: false, transformFunction: null }, layoutTransitionMs: { classPropertyName: "layoutTransitionMs", publicName: "layoutTransitionMs", isSignal: true, isRequired: false, transformFunction: null }, conversationIdInput: { classPropertyName: "conversationIdInput", publicName: "conversationId", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, bubbleTemplate: { classPropertyName: "bubbleTemplate", publicName: "bubbleTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { messageSent: "messageSent", action: "action", messagesChange: "messagesChange", messageAppended: "messageAppended", typingChange: "typingChange", conversationCreated: "conversationCreated" }, host: { attributes: { "framework-managed": "" }, properties: { "attr.defer-hydration": "hydrating() ? '' : null" } }, queries: [{ propertyName: "bubbleDirective", first: true, predicate: AparteBubbleDirective, descendants: true, isSignal: true }], 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: `
|
|
645
727
|
<div
|
|
646
728
|
class="aparte-chat-container"
|
|
647
729
|
[class]="containerClass()"
|
|
@@ -668,11 +750,12 @@ class AparteChatComponent {
|
|
|
668
750
|
<ng-content select="[slot='empty-state']"></ng-content>
|
|
669
751
|
}
|
|
670
752
|
@for (message of messages(); track message.id) {
|
|
671
|
-
@if (bubbleTemplate) {
|
|
672
|
-
<!-- Render your OWN element per message
|
|
673
|
-
|
|
753
|
+
@if (bubbleTemplate() || bubbleDirective()) {
|
|
754
|
+
<!-- Render your OWN element per message ([bubbleTemplate], else an
|
|
755
|
+
<ng-template aparteBubble> written as content); driven by the
|
|
756
|
+
reactive messages() signal so it streams live. -->
|
|
674
757
|
<ng-container
|
|
675
|
-
[ngTemplateOutlet]="bubbleTemplate"
|
|
758
|
+
[ngTemplateOutlet]="bubbleTemplate() ?? bubbleDirective()?.template ?? null"
|
|
676
759
|
[ngTemplateOutletContext]="{ $implicit: message }"
|
|
677
760
|
></ng-container>
|
|
678
761
|
} @else {
|
|
@@ -743,7 +826,7 @@ class AparteChatComponent {
|
|
|
743
826
|
}
|
|
744
827
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteChatComponent, decorators: [{
|
|
745
828
|
type: Component,
|
|
746
|
-
args: [{ selector: 'aparte-chat', standalone: true, imports: [NgTemplateOutlet], schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, host: { 'framework-managed': '' }, template: `
|
|
829
|
+
args: [{ selector: 'aparte-chat', standalone: true, imports: [NgTemplateOutlet], schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, host: { 'framework-managed': '', '[attr.defer-hydration]': "hydrating() ? '' : null" }, template: `
|
|
747
830
|
<div
|
|
748
831
|
class="aparte-chat-container"
|
|
749
832
|
[class]="containerClass()"
|
|
@@ -770,11 +853,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
770
853
|
<ng-content select="[slot='empty-state']"></ng-content>
|
|
771
854
|
}
|
|
772
855
|
@for (message of messages(); track message.id) {
|
|
773
|
-
@if (bubbleTemplate) {
|
|
774
|
-
<!-- Render your OWN element per message
|
|
775
|
-
|
|
856
|
+
@if (bubbleTemplate() || bubbleDirective()) {
|
|
857
|
+
<!-- Render your OWN element per message ([bubbleTemplate], else an
|
|
858
|
+
<ng-template aparteBubble> written as content); driven by the
|
|
859
|
+
reactive messages() signal so it streams live. -->
|
|
776
860
|
<ng-container
|
|
777
|
-
[ngTemplateOutlet]="bubbleTemplate"
|
|
861
|
+
[ngTemplateOutlet]="bubbleTemplate() ?? bubbleDirective()?.template ?? null"
|
|
778
862
|
[ngTemplateOutletContext]="{ $implicit: message }"
|
|
779
863
|
></ng-container>
|
|
780
864
|
} @else {
|
|
@@ -842,56 +926,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
842
926
|
</aparte-composer>
|
|
843
927
|
</div>
|
|
844
928
|
`, styles: [":host{display:block;width:100%;height:100%}.aparte-chat-container{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}aparte-chat-viewport{flex:1;min-height:0}aparte-composer{flex-shrink:0}\n"] }]
|
|
845
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
846
|
-
type: Input,
|
|
847
|
-
args: ['messages']
|
|
848
|
-
}], placeholderInput: [{
|
|
849
|
-
type: Input,
|
|
850
|
-
args: ['placeholder']
|
|
851
|
-
}], disabledInput: [{
|
|
852
|
-
type: Input,
|
|
853
|
-
args: [{ alias: 'disabled', transform: booleanAttribute }]
|
|
854
|
-
}], submitOnEnterInput: [{
|
|
855
|
-
type: Input,
|
|
856
|
-
args: [{ alias: 'submitOnEnter', transform: booleanAttribute }]
|
|
857
|
-
}], centerWhenEmptyInput: [{
|
|
858
|
-
type: Input,
|
|
859
|
-
args: [{ alias: 'centerWhenEmpty', transform: booleanAttribute }]
|
|
860
|
-
}], loadingInput: [{
|
|
861
|
-
type: Input,
|
|
862
|
-
args: [{ alias: 'loading', transform: booleanAttribute }]
|
|
863
|
-
}], containerClassInput: [{
|
|
864
|
-
type: Input,
|
|
865
|
-
args: ['containerClass']
|
|
866
|
-
}], containerStyleInput: [{
|
|
867
|
-
type: Input,
|
|
868
|
-
args: ['containerStyle']
|
|
869
|
-
}], overlayComposerInput: [{
|
|
870
|
-
type: Input,
|
|
871
|
-
args: [{ alias: 'overlayComposer', transform: booleanAttribute }]
|
|
872
|
-
}], attachmentsInput: [{
|
|
873
|
-
type: Input,
|
|
874
|
-
args: [{ alias: 'attachments', transform: booleanAttribute }]
|
|
875
|
-
}], elicitationInput: [{
|
|
876
|
-
type: Input,
|
|
877
|
-
args: [{ alias: 'elicitation', transform: booleanAttribute }]
|
|
878
|
-
}], isTypingInput: [{
|
|
879
|
-
type: Input,
|
|
880
|
-
args: [{ alias: 'isTyping', transform: booleanAttribute }]
|
|
881
|
-
}], typingTextInput: [{
|
|
882
|
-
type: Input,
|
|
883
|
-
args: ['typingText']
|
|
884
|
-
}], layoutTransitionMs: [{
|
|
885
|
-
type: Input,
|
|
886
|
-
args: [{ alias: 'layoutTransitionMs', transform: numberAttribute }]
|
|
887
|
-
}], conversationIdInput: [{
|
|
888
|
-
type: Input,
|
|
889
|
-
args: ['conversationId']
|
|
890
|
-
}], config: [{
|
|
891
|
-
type: Input
|
|
892
|
-
}], bubbleTemplate: [{
|
|
893
|
-
type: Input
|
|
894
|
-
}], viewportRef: [{
|
|
929
|
+
}], ctorParameters: () => [], propDecorators: { viewportRef: [{
|
|
895
930
|
type: ViewChild,
|
|
896
931
|
args: ['viewport']
|
|
897
932
|
}], inputRef: [{
|
|
@@ -902,6 +937,126 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
902
937
|
args: ['bubble']
|
|
903
938
|
}] } });
|
|
904
939
|
|
|
940
|
+
/**
|
|
941
|
+
* Create the template's view where it is declared — so its injector, and what your app
|
|
942
|
+
* provides there, is the declaration's — then move its nodes into the element core gave.
|
|
943
|
+
* The same move the CDK's portal outlet makes. `update` hands the view the next state and
|
|
944
|
+
* checks it at once, which is what a zoneless app needs.
|
|
945
|
+
*/
|
|
946
|
+
function mountTemplate(container, template, host, view) {
|
|
947
|
+
const ref = container.createEmbeddedView(template, { $implicit: view });
|
|
948
|
+
ref.detectChanges();
|
|
949
|
+
for (const node of ref.rootNodes)
|
|
950
|
+
host.appendChild(node);
|
|
951
|
+
return {
|
|
952
|
+
update: (next) => {
|
|
953
|
+
ref.context.$implicit = next;
|
|
954
|
+
ref.detectChanges();
|
|
955
|
+
},
|
|
956
|
+
destroy: () => { ref.destroy(); },
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Draws a tool call with your own template, for as long as the template is in the page.
|
|
961
|
+
*
|
|
962
|
+
* ```html
|
|
963
|
+
* <ng-template aparteToolUI="get_weather" let-tool>
|
|
964
|
+
* <app-weather-card [tool]="tool" />
|
|
965
|
+
* </ng-template>
|
|
966
|
+
* ```
|
|
967
|
+
*
|
|
968
|
+
* `let-tool` is an `AparteToolCallView`; its `input` is the model's arguments, `unknown` until
|
|
969
|
+
* the card narrows it — give the card the view (`input.required<AparteToolCallView>()`) and type
|
|
970
|
+
* its arguments there.
|
|
971
|
+
*
|
|
972
|
+
* Every `get_weather` call is drawn with the template, the call's view as `$implicit`, and
|
|
973
|
+
* given back to core's row when the template leaves. The view is created where you declare
|
|
974
|
+
* the template, so the components in it inject what your app provides there. Underneath it is
|
|
975
|
+
* core's `overrideToolRenderer` + `defineMountedToolRenderer`; nothing is declared in a server
|
|
976
|
+
* render, and the cards are drawn in the browser.
|
|
977
|
+
*/
|
|
978
|
+
class AparteToolUIDirective {
|
|
979
|
+
/** The tool this draws: a name, a `RegExp`, or `'*'` for every tool. */
|
|
980
|
+
name = input.required({ alias: 'aparteToolUI' });
|
|
981
|
+
/** The config to declare it on. Defaults to the global one. */
|
|
982
|
+
config = input(undefined, { alias: 'aparteToolUIConfig' });
|
|
983
|
+
template = inject(TemplateRef);
|
|
984
|
+
container = inject(ViewContainerRef);
|
|
985
|
+
constructor() {
|
|
986
|
+
const browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
987
|
+
effect((onCleanup) => {
|
|
988
|
+
const name = this.name();
|
|
989
|
+
const target = this.config() ?? aparteGlobalConfig;
|
|
990
|
+
if (!browser)
|
|
991
|
+
return;
|
|
992
|
+
const restore = untracked(() => target.overrideToolRenderer(name, defineMountedToolRenderer((host, view) => mountTemplate(this.container, this.template, host, view))));
|
|
993
|
+
onCleanup(restore);
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
/** Types `let-tool` in a strict template. */
|
|
997
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
998
|
+
return true;
|
|
999
|
+
}
|
|
1000
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteToolUIDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1001
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.25", type: AparteToolUIDirective, isStandalone: true, selector: "ng-template[aparteToolUI]", inputs: { name: { classPropertyName: "name", publicName: "aparteToolUI", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "aparteToolUIConfig", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
1002
|
+
}
|
|
1003
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteToolUIDirective, decorators: [{
|
|
1004
|
+
type: Directive,
|
|
1005
|
+
args: [{
|
|
1006
|
+
selector: 'ng-template[aparteToolUI]',
|
|
1007
|
+
standalone: true,
|
|
1008
|
+
}]
|
|
1009
|
+
}], ctorParameters: () => [] });
|
|
1010
|
+
/**
|
|
1011
|
+
* Draws a segment type with your own template — one your stream emits, or a built-in such as
|
|
1012
|
+
* `code` — the segment as `$implicit`. Taking the last declaration off a built-in type gives it
|
|
1013
|
+
* back to core's renderer.
|
|
1014
|
+
*
|
|
1015
|
+
* ```html
|
|
1016
|
+
* <ng-template aparteSegmentUI="chart" let-segment>
|
|
1017
|
+
* <app-chart [segment]="segment" />
|
|
1018
|
+
* </ng-template>
|
|
1019
|
+
* ```
|
|
1020
|
+
*
|
|
1021
|
+
* `let-segment` is the base segment with its own fields: the component it is handed to types it.
|
|
1022
|
+
*/
|
|
1023
|
+
class AparteSegmentUIDirective {
|
|
1024
|
+
/** The segment `type` this draws. */
|
|
1025
|
+
type = input.required({ alias: 'aparteSegmentUI' });
|
|
1026
|
+
/** The config to declare it on. Defaults to the global one. */
|
|
1027
|
+
config = input(undefined, { alias: 'aparteSegmentUIConfig' });
|
|
1028
|
+
template = inject(TemplateRef);
|
|
1029
|
+
container = inject(ViewContainerRef);
|
|
1030
|
+
constructor() {
|
|
1031
|
+
const browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
1032
|
+
effect((onCleanup) => {
|
|
1033
|
+
const type = this.type();
|
|
1034
|
+
const target = this.config();
|
|
1035
|
+
if (!browser)
|
|
1036
|
+
return;
|
|
1037
|
+
const restore = untracked(() => {
|
|
1038
|
+
const renderer = defineMountedSegmentRenderer(type, (host, segment) => mountTemplate(this.container, this.template, host, segment));
|
|
1039
|
+
return target ? overrideSegmentRenderer(renderer, target) : overrideSegmentRenderer(renderer);
|
|
1040
|
+
});
|
|
1041
|
+
onCleanup(restore);
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
// The segment's shape is the app's: `let-segment` is typed as the base, narrow it in a pipe
|
|
1045
|
+
// or a component input.
|
|
1046
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
1047
|
+
return true;
|
|
1048
|
+
}
|
|
1049
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteSegmentUIDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1050
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.25", type: AparteSegmentUIDirective, isStandalone: true, selector: "ng-template[aparteSegmentUI]", inputs: { type: { classPropertyName: "type", publicName: "aparteSegmentUI", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "aparteSegmentUIConfig", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
1051
|
+
}
|
|
1052
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteSegmentUIDirective, decorators: [{
|
|
1053
|
+
type: Directive,
|
|
1054
|
+
args: [{
|
|
1055
|
+
selector: 'ng-template[aparteSegmentUI]',
|
|
1056
|
+
standalone: true,
|
|
1057
|
+
}]
|
|
1058
|
+
}], ctorParameters: () => [] });
|
|
1059
|
+
|
|
905
1060
|
/**
|
|
906
1061
|
* AparteUiComponent - Universal UI Proxy
|
|
907
1062
|
*
|
|
@@ -964,6 +1119,13 @@ class AparteUiComponent {
|
|
|
964
1119
|
// ─────────────────────────────────────────────────────────────
|
|
965
1120
|
/** The dynamically created Web Component element */
|
|
966
1121
|
element = null;
|
|
1122
|
+
/**
|
|
1123
|
+
* On a server the element is not built: it would be serialized into the HTML without its
|
|
1124
|
+
* props or listeners, hydration would keep it, and the browser would append its own beside
|
|
1125
|
+
* it — two counters, two pickers. React, Vue and Svelte build theirs in an effect or onMount,
|
|
1126
|
+
* which a server never runs.
|
|
1127
|
+
*/
|
|
1128
|
+
isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
967
1129
|
/** Cleanup functions for event listeners */
|
|
968
1130
|
eventCleanups = [];
|
|
969
1131
|
/** Joined key of the bound event set, so a fresh inline `[events]` array
|
|
@@ -1000,7 +1162,7 @@ class AparteUiComponent {
|
|
|
1000
1162
|
return (this.events ?? APARTE_DEFAULT_UI_EVENTS).join('|');
|
|
1001
1163
|
}
|
|
1002
1164
|
createElement() {
|
|
1003
|
-
if (!this.name)
|
|
1165
|
+
if (!this.isBrowser || !this.name)
|
|
1004
1166
|
return;
|
|
1005
1167
|
// Create the custom element
|
|
1006
1168
|
this.element = this.renderer.createElement(this.name);
|
|
@@ -1584,6 +1746,8 @@ class AparteConversationListDirective extends AparteElementBase {
|
|
|
1584
1746
|
set loading(v) { this.write('loading', v); }
|
|
1585
1747
|
/** 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. */
|
|
1586
1748
|
set manage(v) { this.write('manage', v); }
|
|
1749
|
+
/** Set the list of conversations to display. Triggers a re-render. */
|
|
1750
|
+
set conversations(v) { this.write('conversations', v); }
|
|
1587
1751
|
/** A row's title was activated; the host loads that conversation. */
|
|
1588
1752
|
conversationSelect = new EventEmitter();
|
|
1589
1753
|
/** 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()". */
|
|
@@ -1606,7 +1770,7 @@ class AparteConversationListDirective extends AparteElementBase {
|
|
|
1606
1770
|
onConversationUnarchive(e) { this.conversationUnarchive.emit(e.detail); }
|
|
1607
1771
|
onConversationDelete(e) { this.conversationDelete.emit(e.detail); }
|
|
1608
1772
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteConversationListDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
|
|
1609
|
-
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 });
|
|
1773
|
+
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], conversations: "conversations" }, 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 });
|
|
1610
1774
|
}
|
|
1611
1775
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: AparteConversationListDirective, decorators: [{
|
|
1612
1776
|
type: Directive,
|
|
@@ -1622,6 +1786,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
1622
1786
|
}], manage: [{
|
|
1623
1787
|
type: Input,
|
|
1624
1788
|
args: [{ transform: booleanAttribute }]
|
|
1789
|
+
}], conversations: [{
|
|
1790
|
+
type: Input
|
|
1625
1791
|
}], conversationSelect: [{
|
|
1626
1792
|
type: Output
|
|
1627
1793
|
}], conversationRename: [{
|
|
@@ -2167,5 +2333,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
2167
2333
|
* Generated bundle index. Do not edit.
|
|
2168
2334
|
*/
|
|
2169
2335
|
|
|
2170
|
-
export { APARTE_CLIENT_OPTIONS, APARTE_CONFIG_TOKEN, APARTE_ELEMENT_DIRECTIVES, AparteAiService, AparteChatBubbleDirective, AparteChatComponent, AparteChatStatusDirective, AparteChatViewportDirective, AparteComposerActionDirective, AparteComposerAddAttachmentDirective, AparteComposerAttachmentsDirective, AparteComposerCancelDirective, AparteComposerDirective, AparteComposerInputDirective, AparteComposerSendDirective, AparteComposerToolbarDirective, AparteContextDirective, AparteConversationListDirective, AparteElicitationDirective, AparteIconDirective, AparteOptgroupDirective, AparteOptionDirective, AparteProgressSpinnerDirective, AparteScrollRailDirective, AparteSelectDirective, AparteSidebarDirective, AparteSplitDirective, AparteSuggestionsDirective, AparteUiComponent, ConversationManagerService, provideAparte };
|
|
2336
|
+
export { APARTE_CLIENT_OPTIONS, APARTE_CONFIG_TOKEN, APARTE_ELEMENT_DIRECTIVES, AparteAiService, AparteBubbleDirective, AparteChatBubbleDirective, AparteChatComponent, AparteChatStatusDirective, AparteChatViewportDirective, AparteComposerActionDirective, AparteComposerAddAttachmentDirective, AparteComposerAttachmentsDirective, AparteComposerCancelDirective, AparteComposerDirective, AparteComposerInputDirective, AparteComposerSendDirective, AparteComposerToolbarDirective, AparteContextDirective, AparteConversationListDirective, AparteElicitationDirective, AparteIconDirective, AparteOptgroupDirective, AparteOptionDirective, AparteProgressSpinnerDirective, AparteScrollRailDirective, AparteSegmentUIDirective, AparteSelectDirective, AparteSidebarDirective, AparteSplitDirective, AparteSuggestionsDirective, AparteToolUIDirective, AparteUiComponent, ConversationManagerService, provideAparte };
|
|
2171
2337
|
//# sourceMappingURL=aparte-angular.mjs.map
|