@aparte/angular 0.16.11 → 0.17.1

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.
@@ -55,7 +55,7 @@ export declare class AparteChatBubbleDirective extends AparteElementBase {
55
55
  readonly edit: EventEmitter<AparteEditEventDetail>;
56
56
  /** The `‹1/2›` picker moved between sibling versions. */
57
57
  readonly branchNavigate: EventEmitter<AparteBranchNavigateEventDetail>;
58
- /** 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. */
58
+ /** 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()". */
59
59
  readonly linkClick: EventEmitter<AparteLinkClickEventDetail>;
60
60
  protected onAttachmentPreview(e: CustomEvent<AparteAttachmentPreviewEventDetail>): void;
61
61
  protected onAction(e: CustomEvent<AparteActionEventDetail>): void;
@@ -85,6 +85,8 @@ export declare class AparteChatViewportDirective extends AparteElementBase {
85
85
  set scrollThreshold(v: number);
86
86
  /** Caps how many bubbles stay in the DOM; older ones are released. */
87
87
  set maxRenderedBubbles(v: number);
88
+ /** 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)`). */
89
+ set loading(v: boolean);
88
90
  /** 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. */
89
91
  set frameworkManaged(v: boolean);
90
92
  /** 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. */
@@ -99,9 +101,10 @@ export declare class AparteChatViewportDirective extends AparteElementBase {
99
101
  protected onResetDone(): void;
100
102
  protected onPathChanged(e: CustomEvent<ApartePathChangedEventDetail>): void;
101
103
  static ɵfac: i0.ɵɵFactoryDeclaration<AparteChatViewportDirective, never>;
102
- static ɵdir: i0.ɵɵDirectiveDeclaration<AparteChatViewportDirective, "aparte-chat-viewport", never, { "scrollThreshold": { "alias": "scrollThreshold"; "required": false; }; "maxRenderedBubbles": { "alias": "maxRenderedBubbles"; "required": false; }; "frameworkManaged": { "alias": "frameworkManaged"; "required": false; }; "dataBusy": { "alias": "dataBusy"; "required": false; }; }, { "segmentUpdate": "segmentUpdate"; "resetDone": "resetDone"; "pathChanged": "pathChanged"; }, never, never, true, never>;
104
+ static ɵdir: i0.ɵɵDirectiveDeclaration<AparteChatViewportDirective, "aparte-chat-viewport", never, { "scrollThreshold": { "alias": "scrollThreshold"; "required": false; }; "maxRenderedBubbles": { "alias": "maxRenderedBubbles"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "frameworkManaged": { "alias": "frameworkManaged"; "required": false; }; "dataBusy": { "alias": "dataBusy"; "required": false; }; }, { "segmentUpdate": "segmentUpdate"; "resetDone": "resetDone"; "pathChanged": "pathChanged"; }, never, never, true, never>;
103
105
  static ngAcceptInputType_scrollThreshold: unknown;
104
106
  static ngAcceptInputType_maxRenderedBubbles: unknown;
107
+ static ngAcceptInputType_loading: unknown;
105
108
  static ngAcceptInputType_frameworkManaged: unknown;
106
109
  static ngAcceptInputType_dataBusy: unknown;
107
110
  }
@@ -139,7 +142,7 @@ export declare class AparteComposerDirective extends AparteElementBase {
139
142
  }
140
143
  /** `<aparte-composer-action>` — Generic action button primitive for <aparte-composer>. */
141
144
  export declare class AparteComposerActionDirective extends AparteElementBase {
142
- /** Icon key for aparteGlobalConfig.getIcon(), or raw SVG/HTML starting with `<` */
145
+ /** 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. */
143
146
  set icon(v: string | undefined);
144
147
  /** Accessible label (also used as tooltip) */
145
148
  set label(v: string | undefined);
@@ -259,36 +262,42 @@ export declare class AparteContextDirective extends AparteElementBase {
259
262
  static ngAcceptInputType_autoCompact: unknown;
260
263
  static ngAcceptInputType_dataEmpty: unknown;
261
264
  }
262
- /** `<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, never acting on it itself. */
265
+ /** `<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. */
263
266
  export declare class AparteConversationListDirective extends AparteElementBase {
264
267
  /** The id of the conversation to render as selected. */
265
268
  set activeId(v: string | undefined);
266
269
  /** Render the rows flat, in host order, with no date headings. */
267
- set noGroups(v: boolean);
268
- /** The same item on a pinned row; same detail shape, opposite intent. */
269
- readonly unpinConversation: EventEmitter<AparteConversationPinDetail>;
270
- /** The pin item was chosen on an unpinned row. */
271
- readonly pinConversation: EventEmitter<AparteConversationPinDetail>;
272
- /** The same item on an already-archived one; same detail shape, opposite intent. */
273
- readonly unarchiveConversation: EventEmitter<AparteConversationArchiveDetail>;
274
- /** The archive item was chosen on a live conversation. */
275
- readonly archiveConversation: EventEmitter<AparteConversationArchiveDetail>;
276
- /** The delete was confirmed. Nothing is removed here. */
277
- readonly deleteConversation: EventEmitter<AparteConversationDeleteDetail>;
278
- /** A rename was committed with a new, non-empty title. Nothing is renamed here. */
279
- readonly renameConversation: EventEmitter<AparteConversationRenameDetail>;
270
+ set flat(v: boolean);
271
+ /** 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`. */
272
+ set loading(v: boolean);
273
+ /** 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. */
274
+ set manage(v: boolean);
280
275
  /** A row's title was activated; the host loads that conversation. */
281
- readonly selectConversation: EventEmitter<AparteConversationSelectDetail>;
282
- protected onUnpinConversation(e: CustomEvent<AparteConversationPinDetail>): void;
283
- protected onPinConversation(e: CustomEvent<AparteConversationPinDetail>): void;
284
- protected onUnarchiveConversation(e: CustomEvent<AparteConversationArchiveDetail>): void;
285
- protected onArchiveConversation(e: CustomEvent<AparteConversationArchiveDetail>): void;
286
- protected onDeleteConversation(e: CustomEvent<AparteConversationDeleteDetail>): void;
287
- protected onRenameConversation(e: CustomEvent<AparteConversationRenameDetail>): void;
288
- protected onSelectConversation(e: CustomEvent<AparteConversationSelectDetail>): void;
276
+ readonly conversationSelect: EventEmitter<AparteConversationSelectDetail>;
277
+ /** 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()". */
278
+ readonly conversationRename: EventEmitter<AparteConversationRenameDetail>;
279
+ /** 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()". */
280
+ readonly conversationPin: EventEmitter<AparteConversationPinDetail>;
281
+ /** 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()". */
282
+ readonly conversationUnpin: EventEmitter<AparteConversationPinDetail>;
283
+ /** 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()". */
284
+ readonly conversationArchive: EventEmitter<AparteConversationArchiveDetail>;
285
+ /** 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()". */
286
+ readonly conversationUnarchive: EventEmitter<AparteConversationArchiveDetail>;
287
+ /** 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()". */
288
+ readonly conversationDelete: EventEmitter<AparteConversationDeleteDetail>;
289
+ protected onConversationSelect(e: CustomEvent<AparteConversationSelectDetail>): void;
290
+ protected onConversationRename(e: CustomEvent<AparteConversationRenameDetail>): void;
291
+ protected onConversationPin(e: CustomEvent<AparteConversationPinDetail>): void;
292
+ protected onConversationUnpin(e: CustomEvent<AparteConversationPinDetail>): void;
293
+ protected onConversationArchive(e: CustomEvent<AparteConversationArchiveDetail>): void;
294
+ protected onConversationUnarchive(e: CustomEvent<AparteConversationArchiveDetail>): void;
295
+ protected onConversationDelete(e: CustomEvent<AparteConversationDeleteDetail>): void;
289
296
  static ɵfac: i0.ɵɵFactoryDeclaration<AparteConversationListDirective, never>;
290
- static ɵdir: i0.ɵɵDirectiveDeclaration<AparteConversationListDirective, "aparte-conversation-list", never, { "activeId": { "alias": "activeId"; "required": false; }; "noGroups": { "alias": "noGroups"; "required": false; }; }, { "unpinConversation": "unpinConversation"; "pinConversation": "pinConversation"; "unarchiveConversation": "unarchiveConversation"; "archiveConversation": "archiveConversation"; "deleteConversation": "deleteConversation"; "renameConversation": "renameConversation"; "selectConversation": "selectConversation"; }, never, never, true, never>;
291
- static ngAcceptInputType_noGroups: unknown;
297
+ static ɵdir: i0.ɵɵDirectiveDeclaration<AparteConversationListDirective, "aparte-conversation-list", never, { "activeId": { "alias": "activeId"; "required": false; }; "flat": { "alias": "flat"; "required": false; }; "loading": { "alias": "loading"; "required": false; }; "manage": { "alias": "manage"; "required": false; }; }, { "conversationSelect": "conversationSelect"; "conversationRename": "conversationRename"; "conversationPin": "conversationPin"; "conversationUnpin": "conversationUnpin"; "conversationArchive": "conversationArchive"; "conversationUnarchive": "conversationUnarchive"; "conversationDelete": "conversationDelete"; }, never, never, true, never>;
298
+ static ngAcceptInputType_flat: unknown;
299
+ static ngAcceptInputType_loading: unknown;
300
+ static ngAcceptInputType_manage: unknown;
292
301
  }
293
302
  /** `<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. */
294
303
  /**
@@ -358,7 +367,7 @@ export declare class AparteScrollRailDirective extends AparteElementBase {
358
367
  set every(v: string | undefined);
359
368
  /** Reflected BY the element while it has fewer than two ticks. Read-only. */
360
369
  set dataEmpty(v: boolean);
361
- /** A tick was activated. Bubbles, cancelable: `preventDefault()` leaves the transcript where it is. */
370
+ /** 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()". */
362
371
  readonly scrollRailJump: EventEmitter<AparteScrollRailJumpDetail>;
363
372
  protected onScrollRailJump(e: CustomEvent<AparteScrollRailJumpDetail>): void;
364
373
  static ɵfac: i0.ɵɵFactoryDeclaration<AparteScrollRailDirective, never>;
@@ -447,17 +456,17 @@ export declare class AparteSuggestionsDirective extends AparteElementBase {
447
456
  set suggestions(v: string | undefined);
448
457
  /** The id of the `<aparte-chat>` whose composer should receive the click, when the element is not inside that composer. */
449
458
  set target(v: string | undefined);
459
+ /** Shows the row only while the conversation it serves is empty: hidden (`hidden`) once a message is in its transcript or on its way — a thread being loaded counts — and shown again when the transcript is cleared, by a new chat for instance. The composer's send hides it at once, before its message lands. The element sets and lifts `hidden` itself and never lifts one you set. With no transcript to read — a composer outside any chat — the first send hides it until the attribute is removed. */
460
+ set emptyOnly(v: boolean);
450
461
  /** `send` (default): the click fills the composer and submits. `fill`: the click fills the composer and focuses it, so the reader edits first. */
451
462
  set mode(v: string | undefined);
452
- /** Hides the row (`hidden`) once its composer has sent something. Remove the attribute, or `hidden`, to show it again. */
453
- set emptyOnly(v: boolean);
454
463
  /** Reflected BY the element while it has no suggestion to show; the stylesheet hides it then. Read-only, do not set it yourself. */
455
464
  set dataEmpty(v: boolean);
456
- /** A starter was clicked. Bubbles, and is cancelable: `preventDefault()` stops the composer from being filled or submitted. */
465
+ /** 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()". */
457
466
  readonly suggestion: EventEmitter<AparteSuggestionEventDetail>;
458
467
  protected onSuggestion(e: CustomEvent<AparteSuggestionEventDetail>): void;
459
468
  static ɵfac: i0.ɵɵFactoryDeclaration<AparteSuggestionsDirective, never>;
460
- static ɵdir: i0.ɵɵDirectiveDeclaration<AparteSuggestionsDirective, "aparte-suggestions", never, { "suggestions": { "alias": "suggestions"; "required": false; }; "target": { "alias": "target"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "emptyOnly": { "alias": "emptyOnly"; "required": false; }; "dataEmpty": { "alias": "dataEmpty"; "required": false; }; }, { "suggestion": "suggestion"; }, never, never, true, never>;
469
+ static ɵdir: i0.ɵɵDirectiveDeclaration<AparteSuggestionsDirective, "aparte-suggestions", never, { "suggestions": { "alias": "suggestions"; "required": false; }; "target": { "alias": "target"; "required": false; }; "emptyOnly": { "alias": "emptyOnly"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "dataEmpty": { "alias": "dataEmpty"; "required": false; }; }, { "suggestion": "suggestion"; }, never, never, true, never>;
461
470
  static ngAcceptInputType_emptyOnly: unknown;
462
471
  static ngAcceptInputType_dataEmpty: unknown;
463
472
  }
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export { provideAparte, APARTE_CONFIG_TOKEN } from './lib/provide-aparte';
6
6
  export type { ProvideAparteOptions, ApartePluginLoader } from './lib/provide-aparte';
7
7
  export { AparteChatComponent } from './lib/aparte-chat.component';
8
8
  export { AparteUiComponent } from './lib/aparte-ui.component';
9
- export type { AparteUiHandle } from './lib/aparte-ui.component';
9
+ export type { AparteUiHandle, AparteUiProps } from './lib/aparte-ui.component';
10
10
  export * from './generated/element.directives';
11
11
  export { AparteAiService, APARTE_CLIENT_OPTIONS } from './lib/aparte-ai.service';
12
12
  export { ConversationManagerService } from './lib/conversation-manager.service';
@@ -1,5 +1,5 @@
1
1
  import { OnDestroy, InjectionToken } from '@angular/core';
2
- import { AparteClientOptions } from '@aparte/core';
2
+ import { AparteClient, AparteClientOptions } from '@aparte/core';
3
3
  import * as i0 from "@angular/core";
4
4
  /**
5
5
  * Injection token for AparteClient configuration. Provided by `provideAparte()`;
@@ -14,7 +14,14 @@ export declare const APARTE_CLIENT_OPTIONS: InjectionToken<AparteClientOptions>;
14
14
  */
15
15
  export declare class AparteAiService implements OnDestroy {
16
16
  private readonly _clientOptions;
17
- private _client;
17
+ private readonly _client;
18
+ /**
19
+ * The `AparteClient` this service drives — the same object React's
20
+ * `useAparteClient`, Vue's and Svelte's `createAparteClient` return as
21
+ * `{ client, abort }`. Angular alone kept it private, so the instance was
22
+ * unreachable while each service's docblock called itself the others' equivalent.
23
+ */
24
+ get client(): AparteClient;
18
25
  /**
19
26
  * Start listening to `aparte-send` events globally.
20
27
  * This connects the <aparte-chat> components to the AI Providers.
@@ -25,7 +32,9 @@ export declare class AparteAiService implements OnDestroy {
25
32
  */
26
33
  connect(): void;
27
34
  /**
28
- * Stop listening.
35
+ * Stop listening. The client also takes its `keyResolver` back off the config,
36
+ * so a paused bridge no longer answers for keys — a model list refreshed while
37
+ * disconnected sees the config's other key sources only.
29
38
  */
30
39
  disconnect(): void;
31
40
  /**
@@ -43,6 +43,35 @@ export declare class AparteChatComponent implements AfterViewInit, OnDestroy, Ap
43
43
  */
44
44
  set centerWhenEmptyInput(val: boolean);
45
45
  readonly centerWhenEmpty: import("@angular/core").WritableSignal<boolean>;
46
+ /**
47
+ * The conversation is on its way: the viewport draws two skeleton turns, the empty
48
+ * state stays off and the composer is disabled until it lands. The conversation
49
+ * controller sets this itself while it fetches through your adapter's `loadFull()`;
50
+ * the input is for a wait of your own. Off by default.
51
+ */
52
+ set loadingInput(val: boolean);
53
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
54
+ /** The controller's wait (a conversation fetched through the adapter). */
55
+ readonly hostLoading: import("@angular/core").WritableSignal<boolean>;
56
+ readonly waiting: import("@angular/core").Signal<boolean>;
57
+ get loadingText(): string;
58
+ /**
59
+ * Class(es) merged onto the INNER `.aparte-chat-container`, not onto this
60
+ * component's own `<aparte-chat>` host.
61
+ *
62
+ * The other three wrappers have no such host: their root IS the container, so a
63
+ * consumer's `className` / `class` lands on the div that carries
64
+ * `.aparte-chat-container`, `[overlay-composer]` and `[data-aparte-empty]` — the
65
+ * three selectors core's shell recipe keys on. Here a class written on the tag
66
+ * lands one level above them, so overriding the shell needed a descendant
67
+ * selector on Angular alone. This input is that parity. Additive: the recipe's
68
+ * own classes stay.
69
+ */
70
+ set containerClassInput(val: string);
71
+ readonly containerClass: import("@angular/core").WritableSignal<string>;
72
+ /** Inline style for the same div — a string or a `{ prop: value }` map. */
73
+ set containerStyleInput(val: string | Record<string, string> | null);
74
+ readonly containerStyle: import("@angular/core").WritableSignal<string | Record<string, string>>;
46
75
  /**
47
76
  * Overlay the composer on the transcript (the ChatGPT anatomy): the scroll
48
77
  * surface spans the whole column, the scrollbar runs edge to edge, and the
@@ -168,8 +197,13 @@ export declare class AparteChatComponent implements AfterViewInit, OnDestroy, Ap
168
197
  appendToSegment(segmentId: string, content: string): void;
169
198
  /** Read the current message list. */
170
199
  getMessages(): AparteMessage[];
171
- /** Clear all messages + reset state. */
172
- clearMessages(): void;
200
+ /**
201
+ * Clear all messages + reset state. `{ revokeAttachments: false }` keeps the
202
+ * attachments' object URLs alive — pass it when the same messages are going back.
203
+ */
204
+ clearMessages(options?: {
205
+ revokeAttachments?: boolean;
206
+ }): void;
173
207
  /** Scroll the viewport to the latest message. */
174
208
  scrollToBottom(): void;
175
209
  /**
@@ -209,10 +243,11 @@ export declare class AparteChatComponent implements AfterViewInit, OnDestroy, Ap
209
243
  /** Handle aparte-send event from the composer Web Component. */
210
244
  onAparteSend(event: Event): void;
211
245
  static ɵfac: i0.ɵɵFactoryDeclaration<AparteChatComponent, never>;
212
- static ɵcmp: i0.ɵɵComponentDeclaration<AparteChatComponent, "aparte-chat", never, { "messagesInput": { "alias": "messages"; "required": false; }; "placeholderInput": { "alias": "placeholder"; "required": false; }; "disabledInput": { "alias": "disabled"; "required": false; }; "submitOnEnterInput": { "alias": "submitOnEnter"; "required": false; }; "centerWhenEmptyInput": { "alias": "centerWhenEmpty"; "required": false; }; "overlayComposerInput": { "alias": "overlayComposer"; "required": false; }; "attachmentsInput": { "alias": "attachments"; "required": false; }; "elicitationInput": { "alias": "elicitation"; "required": false; }; "isTypingInput": { "alias": "isTyping"; "required": false; }; "typingTextInput": { "alias": "typingText"; "required": false; }; "layoutTransitionMs": { "alias": "layoutTransitionMs"; "required": false; }; "conversationIdInput": { "alias": "conversationId"; "required": false; }; "config": { "alias": "config"; "required": false; }; "bubbleTemplate": { "alias": "bubbleTemplate"; "required": false; }; }, { "messageSent": "messageSent"; "action": "action"; "messagesChange": "messagesChange"; "messageAppended": "messageAppended"; "typingChange": "typingChange"; "conversationCreated": "conversationCreated"; }, never, ["[slot='empty-state']", "[slot='above-composer']", "[slot='toolbar']", "[slot='composer']"], true, never>;
246
+ static ɵcmp: i0.ɵɵComponentDeclaration<AparteChatComponent, "aparte-chat", never, { "messagesInput": { "alias": "messages"; "required": false; }; "placeholderInput": { "alias": "placeholder"; "required": false; }; "disabledInput": { "alias": "disabled"; "required": false; }; "submitOnEnterInput": { "alias": "submitOnEnter"; "required": false; }; "centerWhenEmptyInput": { "alias": "centerWhenEmpty"; "required": false; }; "loadingInput": { "alias": "loading"; "required": false; }; "containerClassInput": { "alias": "containerClass"; "required": false; }; "containerStyleInput": { "alias": "containerStyle"; "required": false; }; "overlayComposerInput": { "alias": "overlayComposer"; "required": false; }; "attachmentsInput": { "alias": "attachments"; "required": false; }; "elicitationInput": { "alias": "elicitation"; "required": false; }; "isTypingInput": { "alias": "isTyping"; "required": false; }; "typingTextInput": { "alias": "typingText"; "required": false; }; "layoutTransitionMs": { "alias": "layoutTransitionMs"; "required": false; }; "conversationIdInput": { "alias": "conversationId"; "required": false; }; "config": { "alias": "config"; "required": false; }; "bubbleTemplate": { "alias": "bubbleTemplate"; "required": false; }; }, { "messageSent": "messageSent"; "action": "action"; "messagesChange": "messagesChange"; "messageAppended": "messageAppended"; "typingChange": "typingChange"; "conversationCreated": "conversationCreated"; }, never, ["[slot='empty-state']", "[slot='above-composer']", "[slot='toolbar']", "[slot='composer']"], true, never>;
213
247
  static ngAcceptInputType_disabledInput: unknown;
214
248
  static ngAcceptInputType_submitOnEnterInput: unknown;
215
249
  static ngAcceptInputType_centerWhenEmptyInput: unknown;
250
+ static ngAcceptInputType_loadingInput: unknown;
216
251
  static ngAcceptInputType_overlayComposerInput: unknown;
217
252
  static ngAcceptInputType_attachmentsInput: unknown;
218
253
  static ngAcceptInputType_elicitationInput: unknown;
@@ -1,12 +1,27 @@
1
1
  import { OnChanges, OnDestroy, AfterViewInit, SimpleChanges } from '@angular/core';
2
+ import { type AparteUiHandle } from '@aparte/core';
2
3
  import * as i0 from "@angular/core";
3
4
  /**
4
- * The imperative surface of the `AparteUi` proxy — the same
5
- * `getElement`/`callMethod` contract on all four wrappers.
5
+ * The imperative surface of the `AparteUi` proxy — one declaration in `@aparte/core`,
6
+ * re-exported by all four wrappers. It used to be four hand-written copies, each
7
+ * promising "the same contract on all four", and it had already drifted.
6
8
  */
7
- export interface AparteUiHandle {
8
- getElement<T extends HTMLElement = HTMLElement>(): T | null;
9
- callMethod<T = unknown>(methodName: string, ...args: unknown[]): T | undefined;
9
+ export type { AparteUiHandle } from '@aparte/core';
10
+ /**
11
+ * The proxy's inputs, named for parity with React/Vue/Svelte, which all export an
12
+ * `AparteUiProps`. Angular's are `@Input`s on the component rather than an object, so
13
+ * this is the shape a caller building `[props]` programmatically writes against.
14
+ */
15
+ export interface AparteUiProps {
16
+ /** The custom element tag name (e.g. 'aparte-model-selector'). */
17
+ name: string;
18
+ /** Props to apply. Keys starting with `--` become CSS variables. */
19
+ props?: Record<string, unknown>;
20
+ /**
21
+ * Which custom events to forward through `elementEvent`. Defaults to the
22
+ * interactive aparté surface ({@link APARTE_DEFAULT_UI_EVENTS}).
23
+ */
24
+ events?: string[];
10
25
  }
11
26
  /**
12
27
  * AparteUiComponent - Universal UI Proxy
@@ -2,8 +2,9 @@ import { InjectionToken, type EnvironmentProviders } from '@angular/core';
2
2
  import { type AparteAIProvider, type AparteClientOptions, type AparteIconProvider, type AparteLocale, type AparteMarkdownProvider, type AparteModelConfig } from '@aparte/core';
3
3
  /**
4
4
  * Any function that initialises a plugin — sync or async, so it can wrap a
5
- * dynamic `import()` of a package YOU choose:
6
- * `() => import('@aparte/plugin-model-selector')`.
5
+ * dynamic `import()` of a package YOU choose. It resolves to nothing, so `await`
6
+ * the import rather than returning it:
7
+ * `async () => { await import('@aparte/plugin-model-selector'); }`.
7
8
  */
8
9
  export type ApartePluginLoader = () => void | Promise<void>;
9
10
  /**
@@ -17,7 +18,11 @@ export interface ProvideAparteOptions {
17
18
  providers?: AparteAIProvider[];
18
19
  /** Optional plugin wiring — objects or loaders, never package-name strings. */
19
20
  plugins?: {
20
- /** Action plugins, as loaders: `[() => import('@aparte/plugin-model-selector')]`. */
21
+ /**
22
+ * Action plugins, as loaders. A loader resolves to nothing, so `await` the
23
+ * import rather than returning it:
24
+ * `[async () => { await import('@aparte/plugin-model-selector'); }]`.
25
+ */
21
26
  actions?: ApartePluginLoader[];
22
27
  /** An icon provider object, or a loader that registers one. */
23
28
  icons?: AparteIconProvider | ApartePluginLoader;
@@ -53,8 +58,22 @@ export declare const APARTE_CONFIG_TOKEN: InjectionToken<ProvideAparteOptions>;
53
58
  * `AparteAiService.connect()` needed.
54
59
  *
55
60
  * The components (`AparteChatComponent`, `AparteUiComponent`) are standalone and
56
- * work WITHOUT this — it is config sugar. You can equally call `aparteGlobalConfig.*`
57
- * yourself, exactly like the React/Vue/Svelte wrappers do.
61
+ * work WITHOUT this — it is config sugar. Angular is the only wrapper that has it,
62
+ * and the other three lose nothing: what this provider exists for is an initializer
63
+ * that runs before the first component and a `DestroyRef` to release the theme
64
+ * listener. Everything it configures is a plain call — bar `theme: 'auto'`, which is a
65
+ * `matchMedia` listener you own — so React, Vue and Svelte make the same ones at module
66
+ * scope, before the app mounts:
67
+ *
68
+ * ```ts
69
+ * aparteGlobalConfig.registerAIProvider(createOpenAICompatProvider(presets.OPENROUTER));
70
+ * aparteGlobalConfig.setModelConfig({ defaultModel: 'openai/gpt-4o-mini' });
71
+ * aparteGlobalConfig.setLocale(fr);
72
+ * document.documentElement.setAttribute('data-aparte-theme', 'dark');
73
+ * // 'auto' is the one option with no one-liner: match prefers-color-scheme, rewrite the
74
+ * // attribute on change, and drop the listener when your app tears down.
75
+ * // The client stays the wrapper's own: useAparteClient(), createAparteClient().
76
+ * ```
58
77
  *
59
78
  * @example
60
79
  * bootstrapApplication(App, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aparte/angular",
3
- "version": "0.16.11",
3
+ "version": "0.17.1",
4
4
  "homepage": "https://apartejs.dev/frameworks/angular/",
5
5
  "description": "Angular 19 wrapper for aparté — an ergonomic <aparte-chat> component plus services over the framework-agnostic web components.",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "peerDependencies": {
31
31
  "@angular/common": "^19.2.0",
32
32
  "@angular/core": "^19.2.0",
33
- "@aparte/core": ">=0.16.11 <1.0.0",
33
+ "@aparte/core": ">=0.17.1 <1.0.0",
34
34
  "rxjs": "^7.8.0"
35
35
  },
36
36
  "devDependencies": {
@@ -46,7 +46,7 @@
46
46
  "typescript": "^5.4.0",
47
47
  "vite": "^6.0.0",
48
48
  "zone.js": "~0.15.0",
49
- "@aparte/core": "0.16.11"
49
+ "@aparte/core": "0.17.1"
50
50
  },
51
51
  "keywords": [
52
52
  "angular",