@aparte/vue 0.2.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 aparté
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @aparte/vue
2
+
3
+ Vue 3.5+ wrapper for [aparté](https://github.com/apartejs/aparte) — an ergonomic `<AparteChat>`
4
+ component plus composables (`useAparteChat`, `useAparteClient`, `useConversationManager`) over the
5
+ framework-agnostic web components in `@aparte/core`.
6
+
7
+ ```bash
8
+ npm install @aparte/vue @aparte/core vue
9
+ ```
10
+
11
+ ```vue
12
+ <script setup lang="ts">
13
+ import { AparteChat, useAparteChat } from '@aparte/vue';
14
+ import '@aparte/core/styles.css';
15
+
16
+ const chat = useAparteChat();
17
+ </script>
18
+
19
+ <template>
20
+ <AparteChat
21
+ :ref="chat.chatRef"
22
+ :messages="chat.messages.value"
23
+ @messages-change="chat.onMessagesChange"
24
+ />
25
+ </template>
26
+ ```
27
+
28
+ The user's message is appended automatically on send — don't add it yourself. `@message-sent` is
29
+ optional and only for side-effects (scroll, analytics).
30
+
31
+ `@aparte/core` and `vue` are **peer dependencies**. For any `<aparte-*>` element without a dedicated
32
+ component, the generic `<AparteUi name="aparte-…" />` escape hatch mounts it.
33
+
34
+ > ESM-only. See the docs for the full API. Part of the aparté monorepo.
@@ -0,0 +1,380 @@
1
+ import { type AparteConfigClass } from '@aparte/core';
2
+ import type { AparteMessage, AparteSegment, AparteSendEventDetail, AparteActionEventDetail } from '../types.js';
3
+ interface Props {
4
+ /** Optional: omit for an uncontrolled chat (defaults to []); use `v-model:messages` to control. */
5
+ messages?: AparteMessage[];
6
+ placeholder?: string;
7
+ disabled?: boolean;
8
+ isTyping?: boolean;
9
+ typingText?: string;
10
+ /** When false, Shift+Enter submits and a bare Enter inserts a newline. */
11
+ submitOnEnter?: boolean;
12
+ /** Freeze viewport spacer recalculation for this many ms after a conv swap. */
13
+ layoutTransitionMs?: number;
14
+ /**
15
+ * Opt in to the "centered composer when empty" layout: the composer sits
16
+ * vertically centered with the `empty-state` slot above it while the list is
17
+ * empty, then slides to the bottom on the first message (~0.3s). Off by
18
+ * default — additive (adds the `--auto-center` modifier + a `data-aparte-empty`
19
+ * attribute the shipped `aparte.css` recipe keys off).
20
+ */
21
+ centerWhenEmpty?: boolean;
22
+ /** Active conversation id (loads/persists via the registered ConversationManager). */
23
+ conversationId?: string | null;
24
+ /**
25
+ * Instance {@link AparteConfigClass} for this chat. When set, aparté components
26
+ * inside resolve THIS config instead of the global `AparteConfig` singleton, so
27
+ * several independently-configured chats can coexist on one page. Omit for the
28
+ * global config. Read once when the host mounts.
29
+ */
30
+ config?: AparteConfigClass;
31
+ }
32
+ declare var __VLS_7: {}, __VLS_9: {
33
+ message: {
34
+ id: string;
35
+ role: import("@aparte/core").AparteBubbleRole;
36
+ content?: string | undefined;
37
+ segments?: ({
38
+ type: "tool_call";
39
+ toolCall: {
40
+ id: string;
41
+ name: string;
42
+ input: Record<string, unknown>;
43
+ };
44
+ status: "pending" | "resolved" | "aborted" | "awaiting-approval" | "rejected";
45
+ result?: string | undefined;
46
+ id: string;
47
+ isStreaming?: boolean | undefined;
48
+ } | {
49
+ type: "text";
50
+ content: string;
51
+ id: string;
52
+ isStreaming?: boolean | undefined;
53
+ } | {
54
+ type: "thinking";
55
+ content: string;
56
+ collapsed?: boolean | undefined;
57
+ label?: string | undefined;
58
+ id: string;
59
+ isStreaming?: boolean | undefined;
60
+ } | {
61
+ type: "code";
62
+ content: string;
63
+ language?: string | undefined;
64
+ filename?: string | undefined;
65
+ showLineNumbers?: boolean | undefined;
66
+ id: string;
67
+ isStreaming?: boolean | undefined;
68
+ } | {
69
+ type: "diff";
70
+ filename?: string | undefined;
71
+ hunks: {
72
+ oldStart: number;
73
+ newStart: number;
74
+ lines: {
75
+ type: "add" | "remove" | "context";
76
+ content: string;
77
+ }[];
78
+ }[];
79
+ id: string;
80
+ isStreaming?: boolean | undefined;
81
+ } | {
82
+ type: "terminal";
83
+ command?: string | undefined;
84
+ output?: string | undefined;
85
+ exitCode?: number | undefined;
86
+ isRunning?: boolean | undefined;
87
+ id: string;
88
+ isStreaming?: boolean | undefined;
89
+ } | {
90
+ type: "file-tree";
91
+ files: {
92
+ name: string;
93
+ path: string;
94
+ type: "file" | "directory";
95
+ children?: /*elided*/ any[] | undefined;
96
+ status?: "added" | "modified" | "deleted" | undefined;
97
+ }[];
98
+ title?: string | undefined;
99
+ id: string;
100
+ isStreaming?: boolean | undefined;
101
+ } | {
102
+ type: "image";
103
+ url: string;
104
+ alt?: string | undefined;
105
+ caption?: string | undefined;
106
+ id: string;
107
+ isStreaming?: boolean | undefined;
108
+ } | {
109
+ type: "preview";
110
+ url: string;
111
+ title?: string | undefined;
112
+ height?: number | undefined;
113
+ id: string;
114
+ isStreaming?: boolean | undefined;
115
+ } | {
116
+ type: "error";
117
+ content: string;
118
+ details?: string | undefined;
119
+ stack?: string | undefined;
120
+ id: string;
121
+ isStreaming?: boolean | undefined;
122
+ } | {
123
+ type: "progress";
124
+ label: string;
125
+ percent?: number | undefined;
126
+ status?: "pending" | "running" | "complete" | "error" | undefined;
127
+ id: string;
128
+ isStreaming?: boolean | undefined;
129
+ } | {
130
+ type: "custom";
131
+ subType: string;
132
+ data?: unknown;
133
+ fallback?: string | undefined;
134
+ id: string;
135
+ isStreaming?: boolean | undefined;
136
+ } | {
137
+ type: "artifact";
138
+ mimeType: string;
139
+ artifactType: string;
140
+ title?: string | undefined;
141
+ content: string;
142
+ inline?: boolean | undefined;
143
+ id: string;
144
+ isStreaming?: boolean | undefined;
145
+ } | {
146
+ type: "pipeline-waiting";
147
+ id: string;
148
+ isStreaming?: boolean | undefined;
149
+ })[] | undefined;
150
+ timestamp: number;
151
+ isStreaming?: boolean | undefined;
152
+ status?: import("@aparte/core").AparteStatus | undefined;
153
+ attachments?: {
154
+ id: string;
155
+ name: string;
156
+ type: string;
157
+ url: string;
158
+ size?: number | undefined;
159
+ thumbnailUrl?: string | undefined;
160
+ metadata?: Record<string, unknown> | undefined;
161
+ blob?: {
162
+ readonly size: number;
163
+ readonly type: string;
164
+ arrayBuffer: () => Promise<ArrayBuffer>;
165
+ bytes: () => Promise<Uint8Array>;
166
+ slice: (start?: number, end?: number, contentType?: string) => Blob;
167
+ stream: () => ReadableStream<Uint8Array>;
168
+ text: () => Promise<string>;
169
+ } | undefined;
170
+ }[] | undefined;
171
+ usage?: {
172
+ inputTokens: number;
173
+ outputTokens: number;
174
+ totalTokens?: number | undefined;
175
+ cacheReadTokens?: number | undefined;
176
+ durationMs?: number | undefined;
177
+ ttftMs?: number | undefined;
178
+ decodeMs?: number | undefined;
179
+ decodeTokens?: number | undefined;
180
+ wallMs?: number | undefined;
181
+ modelId?: string | undefined;
182
+ device?: string | undefined;
183
+ phases?: /*elided*/ any[] | undefined;
184
+ } | undefined;
185
+ branches?: {
186
+ id: string;
187
+ content?: string | undefined;
188
+ segments?: ({
189
+ type: "tool_call";
190
+ toolCall: {
191
+ id: string;
192
+ name: string;
193
+ input: Record<string, unknown>;
194
+ };
195
+ status: "pending" | "resolved" | "aborted" | "awaiting-approval" | "rejected";
196
+ result?: string | undefined;
197
+ id: string;
198
+ isStreaming?: boolean | undefined;
199
+ } | {
200
+ type: "text";
201
+ content: string;
202
+ id: string;
203
+ isStreaming?: boolean | undefined;
204
+ } | {
205
+ type: "thinking";
206
+ content: string;
207
+ collapsed?: boolean | undefined;
208
+ label?: string | undefined;
209
+ id: string;
210
+ isStreaming?: boolean | undefined;
211
+ } | {
212
+ type: "code";
213
+ content: string;
214
+ language?: string | undefined;
215
+ filename?: string | undefined;
216
+ showLineNumbers?: boolean | undefined;
217
+ id: string;
218
+ isStreaming?: boolean | undefined;
219
+ } | {
220
+ type: "diff";
221
+ filename?: string | undefined;
222
+ hunks: {
223
+ oldStart: number;
224
+ newStart: number;
225
+ lines: {
226
+ type: "add" | "remove" | "context";
227
+ content: string;
228
+ }[];
229
+ }[];
230
+ id: string;
231
+ isStreaming?: boolean | undefined;
232
+ } | {
233
+ type: "terminal";
234
+ command?: string | undefined;
235
+ output?: string | undefined;
236
+ exitCode?: number | undefined;
237
+ isRunning?: boolean | undefined;
238
+ id: string;
239
+ isStreaming?: boolean | undefined;
240
+ } | {
241
+ type: "file-tree";
242
+ files: {
243
+ name: string;
244
+ path: string;
245
+ type: "file" | "directory";
246
+ children?: /*elided*/ any[] | undefined;
247
+ status?: "added" | "modified" | "deleted" | undefined;
248
+ }[];
249
+ title?: string | undefined;
250
+ id: string;
251
+ isStreaming?: boolean | undefined;
252
+ } | {
253
+ type: "image";
254
+ url: string;
255
+ alt?: string | undefined;
256
+ caption?: string | undefined;
257
+ id: string;
258
+ isStreaming?: boolean | undefined;
259
+ } | {
260
+ type: "preview";
261
+ url: string;
262
+ title?: string | undefined;
263
+ height?: number | undefined;
264
+ id: string;
265
+ isStreaming?: boolean | undefined;
266
+ } | {
267
+ type: "error";
268
+ content: string;
269
+ details?: string | undefined;
270
+ stack?: string | undefined;
271
+ id: string;
272
+ isStreaming?: boolean | undefined;
273
+ } | {
274
+ type: "progress";
275
+ label: string;
276
+ percent?: number | undefined;
277
+ status?: "pending" | "running" | "complete" | "error" | undefined;
278
+ id: string;
279
+ isStreaming?: boolean | undefined;
280
+ } | {
281
+ type: "custom";
282
+ subType: string;
283
+ data?: unknown;
284
+ fallback?: string | undefined;
285
+ id: string;
286
+ isStreaming?: boolean | undefined;
287
+ } | {
288
+ type: "artifact";
289
+ mimeType: string;
290
+ artifactType: string;
291
+ title?: string | undefined;
292
+ content: string;
293
+ inline?: boolean | undefined;
294
+ id: string;
295
+ isStreaming?: boolean | undefined;
296
+ } | {
297
+ type: "pipeline-waiting";
298
+ id: string;
299
+ isStreaming?: boolean | undefined;
300
+ })[] | undefined;
301
+ status?: import("@aparte/core").AparteStatus | undefined;
302
+ timestamp: number;
303
+ }[] | undefined;
304
+ activeBranchIndex?: number | undefined;
305
+ metadata?: Record<string, unknown> | undefined;
306
+ };
307
+ }, __VLS_19: {}, __VLS_27: {}, __VLS_45: {}, __VLS_47: {}, __VLS_49: {};
308
+ type __VLS_Slots = {} & {
309
+ 'empty-state'?: (props: typeof __VLS_7) => any;
310
+ } & {
311
+ bubble?: (props: typeof __VLS_9) => any;
312
+ } & {
313
+ 'above-composer'?: (props: typeof __VLS_19) => any;
314
+ } & {
315
+ composer?: (props: typeof __VLS_27) => any;
316
+ } & {
317
+ 'footer-left'?: (props: typeof __VLS_45) => any;
318
+ } & {
319
+ 'footer-center'?: (props: typeof __VLS_47) => any;
320
+ } & {
321
+ 'footer-right'?: (props: typeof __VLS_49) => any;
322
+ };
323
+ declare const __VLS_component: import("vue").DefineComponent<Props, {
324
+ appendMessage: (m: AparteMessage) => void | undefined;
325
+ updateMessage: (id: string, u: Partial<AparteMessage>) => void | undefined;
326
+ updateLastMessage: (c: string, o?: {
327
+ append?: boolean;
328
+ }) => void | undefined;
329
+ addSegment: (s: AparteSegment) => void | undefined;
330
+ updateSegment: (id: string, u: Partial<AparteSegment>) => void | undefined;
331
+ removeSegment: (id: string) => void | undefined;
332
+ appendToSegment: (id: string, c: string) => void | undefined;
333
+ getMessages: () => AparteMessage[];
334
+ clearMessages: () => void | undefined;
335
+ addBranch: (id: string) => number;
336
+ addSiblingOf: (id: string, m: AparteMessage) => string | null;
337
+ truncateFrom: (id: string) => void | undefined;
338
+ truncateResponsesAfter: (id: string) => void | undefined;
339
+ injectTokenStream: (id: string, tokens: AsyncIterable<string>) => Promise<void>;
340
+ stopTokenStream: () => void | undefined;
341
+ setConversationId: (id: string | null) => Promise<void>;
342
+ scrollToBottom: () => void | undefined;
343
+ focusInput: () => void | undefined;
344
+ isStreaming: () => boolean;
345
+ getViewport: () => HTMLElement | null;
346
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
347
+ action: (detail: AparteActionEventDetail) => any;
348
+ messageSent: (event: AparteSendEventDetail) => any;
349
+ messagesChange: (messages: AparteMessage[]) => any;
350
+ "update:messages": (messages: AparteMessage[]) => any;
351
+ messageAppended: (message: AparteMessage) => any;
352
+ typingChange: (isTyping: boolean) => any;
353
+ conversationCreated: (id: string) => any;
354
+ }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
355
+ onAction?: ((detail: AparteActionEventDetail) => any) | undefined;
356
+ onMessageSent?: ((event: AparteSendEventDetail) => any) | undefined;
357
+ onMessagesChange?: ((messages: AparteMessage[]) => any) | undefined;
358
+ "onUpdate:messages"?: ((messages: AparteMessage[]) => any) | undefined;
359
+ onMessageAppended?: ((message: AparteMessage) => any) | undefined;
360
+ onTypingChange?: ((isTyping: boolean) => any) | undefined;
361
+ onConversationCreated?: ((id: string) => any) | undefined;
362
+ }>, {
363
+ messages: AparteMessage[];
364
+ disabled: boolean;
365
+ placeholder: string;
366
+ isTyping: boolean;
367
+ typingText: string;
368
+ submitOnEnter: boolean;
369
+ layoutTransitionMs: number;
370
+ centerWhenEmpty: boolean;
371
+ conversationId: string | null;
372
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
373
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
374
+ export default _default;
375
+ type __VLS_WithSlots<T, S> = T & {
376
+ new (): {
377
+ $slots: S;
378
+ };
379
+ };
380
+ //# sourceMappingURL=AparteChat.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AparteChat.vue.d.ts","sourceRoot":"","sources":["../../src/components/AparteChat.vue"],"names":[],"mappings":"AAqPA,OAAO,EAA8C,KAAK,iBAAiB,EAAgC,MAAM,cAAc,CAAC;AAChI,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEhH,UAAU,KAAK;IACb,mGAAmG;IACnG,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sFAAsF;IACtF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AA0SD,QAAA,IAAuB,OAAO,IAAU,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAU,EAAE,QAAQ,IAAW,EAAuB,QAAQ,IAAW,EAAE,QAAQ,IAAW,EAAE,QAAQ,IAAW,EAAE,QAAQ,IAAY,CAAE;AAC5L,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,GAClD;IAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,GAC3C;IAAE,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAA;CAAE,GAC9C;IAAE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAA;CAAE,GACnD;IAAE,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAA;CAAE,GACrD;IAAE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAA;CAAE,CAAC;AA8BvD,QAAA,MAAM,eAAe;uBApOK,aAAa;wBACZ,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC;2BAC9B,MAAM,MAAM;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE;oBACvC,aAAa;wBACT,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC;wBACjC,MAAM;0BACJ,MAAM,KAAK,MAAM;;;oBAGvB,MAAM;uBACH,MAAM,KAAK,aAAa;uBACxB,MAAM;iCACI,MAAM;4BACX,MAAM,UAAU,aAAa,CAAC,MAAM,CAAC;;4BAGrC,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;cAtJ/B,aAAa,EAAE;cAEf,OAAO;iBADJ,MAAM;cAET,OAAO;gBACL,MAAM;mBAEH,OAAO;wBAEF,MAAM;qBAQT,OAAO;oBAER,MAAM,GAAG,IAAI;6EAiW9B,CAAC;wBACkB,eAAe,CAAC,OAAO,eAAe,EAAE,WAAW,CAAC;AAAzE,wBAA0E;AAa1E,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -0,0 +1,22 @@
1
+ type __VLS_Props = {
2
+ /** The custom element tag name (e.g. 'aparte-model-selector'). */
3
+ name: string;
4
+ /** Props to apply. Keys starting with `--` become CSS variables. */
5
+ props?: Record<string, unknown>;
6
+ /**
7
+ * Which custom events to forward through `elementEvent`. Defaults to the
8
+ * interactive aparté surface (DEFAULT_UI_EVENTS); pass your own list to listen to
9
+ * other events (e.g. ['aparte-composer-change'] for attachments).
10
+ */
11
+ events?: string[];
12
+ };
13
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {
14
+ getElement: () => HTMLElement | null;
15
+ callMethod: (methodName: string, ...args: unknown[]) => unknown;
16
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
+ elementEvent: (event: CustomEvent<any>) => any;
18
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
19
+ onElementEvent?: ((event: CustomEvent<any>) => any) | undefined;
20
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
21
+ export default _default;
22
+ //# sourceMappingURL=AparteUi.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AparteUi.vue.d.ts","sourceRoot":"","sources":["../../src/components/AparteUi.vue"],"names":[],"mappings":"AA2EA,KAAK,WAAW,GAAG;IACjB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;;;6BA+CyB,MAAM,WAAW,OAAO,EAAE;;;;;;AA0CrD,wBAQG"}
@@ -0,0 +1,89 @@
1
+ import { type Ref } from 'vue';
2
+ import type { AparteChatImperativeApi } from '@aparte/core';
3
+ import type { AparteMessage, AparteSegment } from '../types.js';
4
+ /**
5
+ * The imperative surface `<AparteChat>` exposes via `defineExpose` — the
6
+ * canonical contract shared by all four wrappers (`AparteChatImperativeApi`).
7
+ */
8
+ export type AparteChatInstance = AparteChatImperativeApi;
9
+ /**
10
+ * Idiomatic Vue ergonomics for `<AparteChat>`. Owns the `messages` ref and a
11
+ * component template ref so the consumer skips the manual
12
+ * `@messages-change` → `messages` round-trip.
13
+ *
14
+ * @example
15
+ * const chat = useAparteChat();
16
+ * // template:
17
+ * // <AparteChat :ref="chat.chatRef" :messages="chat.messages.value"
18
+ * // @messages-change="chat.onMessagesChange" />
19
+ */
20
+ export declare function useAparteChat(initial?: AparteMessage[]): {
21
+ messages: Ref<AparteMessage[], AparteMessage[]>;
22
+ chatRef: Ref<{
23
+ appendMessage: (message: AparteMessage) => void;
24
+ updateMessage: (messageId: string, updates: Partial<AparteMessage>) => void;
25
+ updateLastMessage: (content: string, options?: {
26
+ append?: boolean;
27
+ }) => void;
28
+ addSegment: (segment: AparteSegment) => void;
29
+ updateSegment: (segmentId: string, updates: Partial<AparteSegment>) => void;
30
+ removeSegment: (segmentId: string) => void;
31
+ appendToSegment: (segmentId: string, content: string) => void;
32
+ getMessages: () => AparteMessage[];
33
+ clearMessages: () => void;
34
+ addBranch: (messageId: string) => number;
35
+ addSiblingOf: (existingId: string, message: AparteMessage) => string | null;
36
+ truncateFrom: (messageId: string) => void;
37
+ truncateResponsesAfter: (userMessageId: string) => void;
38
+ injectTokenStream: (messageId: string, tokens: AsyncIterable<string>) => Promise<void>;
39
+ stopTokenStream: () => void;
40
+ setConversationId: (id: string | null) => Promise<void>;
41
+ scrollToBottom: () => void;
42
+ focusInput: () => void;
43
+ isStreaming: () => boolean;
44
+ getViewport: () => HTMLElement | null;
45
+ } | null, AparteChatImperativeApi | {
46
+ appendMessage: (message: AparteMessage) => void;
47
+ updateMessage: (messageId: string, updates: Partial<AparteMessage>) => void;
48
+ updateLastMessage: (content: string, options?: {
49
+ append?: boolean;
50
+ }) => void;
51
+ addSegment: (segment: AparteSegment) => void;
52
+ updateSegment: (segmentId: string, updates: Partial<AparteSegment>) => void;
53
+ removeSegment: (segmentId: string) => void;
54
+ appendToSegment: (segmentId: string, content: string) => void;
55
+ getMessages: () => AparteMessage[];
56
+ clearMessages: () => void;
57
+ addBranch: (messageId: string) => number;
58
+ addSiblingOf: (existingId: string, message: AparteMessage) => string | null;
59
+ truncateFrom: (messageId: string) => void;
60
+ truncateResponsesAfter: (userMessageId: string) => void;
61
+ injectTokenStream: (messageId: string, tokens: AsyncIterable<string>) => Promise<void>;
62
+ stopTokenStream: () => void;
63
+ setConversationId: (id: string | null) => Promise<void>;
64
+ scrollToBottom: () => void;
65
+ focusInput: () => void;
66
+ isStreaming: () => boolean;
67
+ getViewport: () => HTMLElement | null;
68
+ } | null>;
69
+ onMessagesChange: (m: AparteMessage[]) => void;
70
+ appendMessage: (m: AparteMessage) => void | undefined;
71
+ updateMessage: (id: string, u: Partial<AparteMessage>) => void | undefined;
72
+ updateLastMessage: (content: string, o?: {
73
+ append?: boolean;
74
+ }) => void | undefined;
75
+ addSegment: (s: AparteSegment) => void | undefined;
76
+ updateSegment: (id: string, u: Partial<AparteSegment>) => void | undefined;
77
+ removeSegment: (id: string) => void | undefined;
78
+ appendToSegment: (id: string, content: string) => void | undefined;
79
+ clearMessages: () => void | undefined;
80
+ addBranch: (id: string) => number;
81
+ addSiblingOf: (id: string, m: AparteMessage) => string | null;
82
+ truncateFrom: (id: string) => void | undefined;
83
+ truncateResponsesAfter: (id: string) => void | undefined;
84
+ injectTokenStream: (id: string, tokens: AsyncIterable<string>) => Promise<void>;
85
+ stopTokenStream: () => void | undefined;
86
+ setConversationId: (id: string | null) => Promise<void>;
87
+ isStreaming: () => boolean;
88
+ };
89
+ //# sourceMappingURL=useAparteChat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAparteChat.d.ts","sourceRoot":"","sources":["../../src/composables/useAparteChat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,uBAAuB,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,aAAa,EAAO;;;;;oDAG/C,CAAA;kBAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;oDAAX,CAAA;kBAAU,CAAC;;;;;;;;;;;;;;;;;;;;0BACU,aAAa,EAAE;uBAMrB,aAAa;wBACZ,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC;iCACxB,MAAM,MAAM;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE;oBAC7C,aAAa;wBACT,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC;wBACjC,MAAM;0BACJ,MAAM,WAAW,MAAM;;oBAE7B,MAAM;uBACH,MAAM,KAAK,aAAa;uBACxB,MAAM;iCACI,MAAM;4BACX,MAAM,UAAU,aAAa,CAAC,MAAM,CAAC;;4BAGrC,MAAM,GAAG,IAAI;;EAG5C"}
@@ -0,0 +1,11 @@
1
+ import { AparteClient, type AparteClientOptions } from '@aparte/core';
2
+ /**
3
+ * Mounts an `AparteClient` that bridges `aparte-send` events to the configured AI
4
+ * providers. Starts on mount, stops on unmount. Vue equivalent of Angular's
5
+ * `AparteAiService`.
6
+ */
7
+ export declare function useAparteClient(options?: AparteClientOptions): {
8
+ client: AparteClient;
9
+ abort: () => void;
10
+ };
11
+ //# sourceMappingURL=useAparteClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAparteClient.d.ts","sourceRoot":"","sources":["../../src/composables/useAparteClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,mBAAmB;;;EAK5D"}
@@ -0,0 +1,24 @@
1
+ import { type Ref } from 'vue';
2
+ import { type AparteConversation, type AparteStorageAdapter } from '@aparte/core';
3
+ import type { AparteMessage } from '../types.js';
4
+ /**
5
+ * Vue-reactive wrapper around the core `ConversationManager`. The active
6
+ * conversation is owned by the chat component's controller; switch by binding
7
+ * `conversationId` on `<AparteChat>`. Vue equivalent of Angular's
8
+ * `ConversationManagerService`.
9
+ */
10
+ export declare function useConversationManager(): {
11
+ conversations: Ref<AparteConversation[], AparteConversation[]>;
12
+ activeConversations: import("vue").ComputedRef<AparteConversation[]>;
13
+ archivedConversations: import("vue").ComputedRef<AparteConversation[]>;
14
+ activeId: Ref<string | null, string | null>;
15
+ activeConversation: import("vue").ComputedRef<AparteConversation | null>;
16
+ init: (adapter: AparteStorageAdapter) => Promise<void>;
17
+ createNew: (title?: string) => Promise<AparteConversation>;
18
+ addMessage: (convId: string, message: AparteMessage) => Promise<void>;
19
+ updateMessages: (convId: string, messages: AparteMessage[]) => Promise<void>;
20
+ delete: (id: string) => Promise<void>;
21
+ archive: (id: string) => Promise<void>;
22
+ unarchive: (id: string) => Promise<void>;
23
+ };
24
+ //# sourceMappingURL=useConversationManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConversationManager.d.ts","sourceRoot":"","sources":["../../src/composables/useConversationManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AAC/D,OAAO,EAGH,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,sBAAsB;;;;;;oBAwBL,oBAAoB,KAAG,OAAO,CAAC,IAAI,CAAC;wBAmBzC,MAAM;yBACL,MAAM,WAAW,aAAa;6BAC1B,MAAM,YAAY,aAAa,EAAE;iBAC7C,MAAM;kBACL,MAAM;oBACJ,MAAM;EAE7B"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * aparté Vue wrapper
3
+ * Vue 3 integration with Composition API and segment support
4
+ */
5
+ import AparteChat from './components/AparteChat.vue';
6
+ export { AparteChat };
7
+ export { useAparteChat } from './composables/useAparteChat.js';
8
+ export type { AparteChatInstance } from './composables/useAparteChat.js';
9
+ export { useAparteClient } from './composables/useAparteClient.js';
10
+ export { useConversationManager } from './composables/useConversationManager.js';
11
+ import AparteUi from './components/AparteUi.vue';
12
+ export { AparteUi };
13
+ export type { AparteUiProps, AparteUiHandle } from './types.js';
14
+ export type { AparteMessage, AparteSendEventDetail, AparteActionEventDetail, AparteSegment, AparteTextSegment, AparteCodeSegment, AparteThinkingSegment, AparteTerminalSegment } from './types.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,UAAU,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,CAAC;AAGtB,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGzE,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEhE,YAAY,EACR,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACxB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,359 @@
1
+ import { defineComponent, useId, ref, onMounted, toRaw, onBeforeUnmount, watch, nextTick, openBlock, createElementBlock, normalizeClass, createElementVNode, renderSlot, createCommentVNode, Fragment, renderList, computed } from "vue";
2
+ import { AparteChatHost, AparteClient, ConversationManager, AparteConfig, DEFAULT_UI_EVENTS, applyElementProps } from "@aparte/core";
3
+ const _hoisted_1 = ["data-aparte-empty"];
4
+ const _hoisted_2 = ["message-id", "data-role", "timestamp", "content", "streaming"];
5
+ const _hoisted_3 = ["visible", "text"];
6
+ const _hoisted_4 = ["^placeholder", "^disabled", "submit-on-enter"];
7
+ const _hoisted_5 = { class: "aparte-composer-shell" };
8
+ const _hoisted_6 = {
9
+ key: 0,
10
+ class: "aparte-composer-footer"
11
+ };
12
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
13
+ __name: "AparteChat",
14
+ props: {
15
+ messages: { default: () => [] },
16
+ placeholder: { default: "Type a message..." },
17
+ disabled: { type: Boolean, default: false },
18
+ isTyping: { type: Boolean, default: false },
19
+ typingText: { default: "Assistant is thinking..." },
20
+ submitOnEnter: { type: Boolean, default: true },
21
+ layoutTransitionMs: { default: 0 },
22
+ centerWhenEmpty: { type: Boolean, default: false },
23
+ conversationId: { default: null },
24
+ config: {}
25
+ },
26
+ emits: ["messageSent", "action", "messagesChange", "update:messages", "messageAppended", "typingChange", "conversationCreated"],
27
+ setup(__props, { expose: __expose, emit: __emit }) {
28
+ const props = __props;
29
+ const emit = __emit;
30
+ const hostId = `aparte-chat-${useId()}`;
31
+ const rootRef = ref();
32
+ const viewportRef = ref();
33
+ const composerRef = ref();
34
+ const internalMessages = ref([...props.messages]);
35
+ const typingActive = ref(props.isTyping);
36
+ let host = null;
37
+ let teardown = null;
38
+ function onSend(e) {
39
+ viewportRef.value?.requestSmoothScroll?.();
40
+ emit("messageSent", e.detail);
41
+ }
42
+ function onAction(e) {
43
+ emit("action", e.detail);
44
+ }
45
+ onMounted(() => {
46
+ const binding = {
47
+ hostId,
48
+ host: rootRef.value,
49
+ viewport: viewportRef.value ?? null,
50
+ getMessages: () => internalMessages.value,
51
+ setMessages: (m) => {
52
+ internalMessages.value = m;
53
+ },
54
+ onMessagesChange: (m) => {
55
+ emit("messagesChange", m);
56
+ emit("update:messages", m);
57
+ },
58
+ onMessageAppended: (m) => emit("messageAppended", m),
59
+ onTypingChange: (t) => {
60
+ typingActive.value = t;
61
+ emit("typingChange", t);
62
+ },
63
+ onStreamingChange: () => {
64
+ },
65
+ afterRender: (cb) => {
66
+ void nextTick(cb);
67
+ },
68
+ resetComposer: () => composerRef.value?.reset?.()
69
+ };
70
+ host = new AparteChatHost(binding, {
71
+ layoutTransitionMs: props.layoutTransitionMs,
72
+ conversationId: props.conversationId ?? null,
73
+ onConversationCreated: (id) => emit("conversationCreated", id),
74
+ // Unwrap Vue's reactive proxy: the config is a plain class with internal
75
+ // Map registries the host/components must operate on directly, not through a
76
+ // deep reactive proxy (which would wrap those Maps and break lookups).
77
+ config: props.config ? toRaw(props.config) : void 0
78
+ });
79
+ teardown = host.bind();
80
+ host.syncBubbles();
81
+ composerRef.value?.addEventListener("aparte-send", onSend);
82
+ rootRef.value?.addEventListener("aparte-action", onAction);
83
+ });
84
+ onBeforeUnmount(() => {
85
+ composerRef.value?.removeEventListener("aparte-send", onSend);
86
+ rootRef.value?.removeEventListener("aparte-action", onAction);
87
+ teardown?.();
88
+ teardown = null;
89
+ host = null;
90
+ });
91
+ watch(() => props.messages, (m) => {
92
+ if (m === internalMessages.value) return;
93
+ internalMessages.value = [...m];
94
+ if (m.length === 0) host?.clearRenderCache();
95
+ });
96
+ watch(internalMessages, () => {
97
+ void nextTick(() => host?.syncBubbles());
98
+ });
99
+ watch(() => props.isTyping, (t) => {
100
+ typingActive.value = t;
101
+ });
102
+ watch(() => props.conversationId, (id) => {
103
+ void host?.setConversationId(id ?? null);
104
+ });
105
+ const appendMessage = (m) => host?.appendMessage(m);
106
+ const updateMessage = (id, u) => host?.updateMessage(id, u);
107
+ const updateLastMessage = (c, o) => host?.updateLastMessage(c, o);
108
+ const addSegment = (s) => host?.addSegment(s);
109
+ const updateSegment = (id, u) => host?.updateSegment(id, u);
110
+ const removeSegment = (id) => host?.removeSegment(id);
111
+ const appendToSegment = (id, c) => host?.appendToSegment(id, c);
112
+ const getMessages = () => host?.getMessages() ?? internalMessages.value;
113
+ const clearMessages = () => host?.clearMessages();
114
+ const addBranch = (id) => host?.addBranch(id) ?? 0;
115
+ const addSiblingOf = (id, m) => host?.addSiblingOf(id, m) ?? null;
116
+ const truncateFrom = (id) => host?.truncateFrom(id);
117
+ const truncateResponsesAfter = (id) => host?.truncateResponsesAfter(id);
118
+ const injectTokenStream = (id, tokens) => host?.streamTokens(id, tokens) ?? Promise.resolve();
119
+ const stopTokenStream = () => host?.stopTokenStream();
120
+ const setConversationId = (id) => host?.setConversationId(id) ?? Promise.resolve();
121
+ const scrollToBottom = () => viewportRef.value?.scrollToBottom?.();
122
+ const focusInput = () => composerRef.value?.focus?.();
123
+ const isStreaming = () => host?.isStreaming ?? false;
124
+ const getViewport = () => viewportRef.value ?? null;
125
+ __expose({
126
+ appendMessage,
127
+ updateMessage,
128
+ updateLastMessage,
129
+ addSegment,
130
+ updateSegment,
131
+ removeSegment,
132
+ appendToSegment,
133
+ getMessages,
134
+ clearMessages,
135
+ addBranch,
136
+ addSiblingOf,
137
+ truncateFrom,
138
+ truncateResponsesAfter,
139
+ injectTokenStream,
140
+ stopTokenStream,
141
+ setConversationId,
142
+ scrollToBottom,
143
+ focusInput,
144
+ isStreaming,
145
+ getViewport
146
+ });
147
+ return (_ctx, _cache) => {
148
+ return openBlock(), createElementBlock("div", {
149
+ class: normalizeClass(["aparte-chat-container", { "aparte-chat-container--auto-center": __props.centerWhenEmpty }]),
150
+ "data-aparte-chat": "",
151
+ "data-aparte-empty": __props.centerWhenEmpty && internalMessages.value.length === 0 ? "" : null,
152
+ id: hostId,
153
+ ref_key: "rootRef",
154
+ ref: rootRef
155
+ }, [
156
+ createElementVNode("aparte-chat-viewport", {
157
+ ref_key: "viewportRef",
158
+ ref: viewportRef,
159
+ "framework-managed": ""
160
+ }, [
161
+ internalMessages.value.length === 0 ? renderSlot(_ctx.$slots, "empty-state", { key: 0 }) : createCommentVNode("", true),
162
+ (openBlock(true), createElementBlock(Fragment, null, renderList(internalMessages.value, (m) => {
163
+ return renderSlot(_ctx.$slots, "bubble", {
164
+ key: m.id,
165
+ message: m
166
+ }, () => [
167
+ createElementVNode("aparte-chat-bubble", {
168
+ "message-id": m.id,
169
+ "data-role": m.role,
170
+ timestamp: m.timestamp,
171
+ content: m.content,
172
+ streaming: m.status === "streaming" || m.status === "pending" ? "" : null
173
+ }, null, 8, _hoisted_2)
174
+ ]);
175
+ }), 128)),
176
+ createElementVNode("aparte-chat-status", {
177
+ visible: typingActive.value ? "" : null,
178
+ text: __props.typingText
179
+ }, null, 8, _hoisted_3)
180
+ ], 512),
181
+ renderSlot(_ctx.$slots, "above-composer"),
182
+ createElementVNode("aparte-composer", {
183
+ ref_key: "composerRef",
184
+ ref: composerRef,
185
+ target: hostId,
186
+ "^placeholder": __props.placeholder,
187
+ "^disabled": __props.disabled ? "" : null,
188
+ "submit-on-enter": __props.submitOnEnter ? null : "false"
189
+ }, [
190
+ renderSlot(_ctx.$slots, "composer", {}, () => [
191
+ createElementVNode("div", _hoisted_5, [
192
+ _cache[0] || (_cache[0] = createElementVNode("aparte-composer-attachments", null, null, -1)),
193
+ _cache[1] || (_cache[1] = createElementVNode("div", { class: "aparte-composer-row" }, [
194
+ createElementVNode("aparte-composer-add-attachment"),
195
+ createElementVNode("aparte-composer-input"),
196
+ createElementVNode("aparte-composer-send")
197
+ ], -1)),
198
+ _ctx.$slots["footer-left"] || _ctx.$slots["footer-center"] || _ctx.$slots["footer-right"] ? (openBlock(), createElementBlock("div", _hoisted_6, [
199
+ renderSlot(_ctx.$slots, "footer-left"),
200
+ renderSlot(_ctx.$slots, "footer-center"),
201
+ renderSlot(_ctx.$slots, "footer-right")
202
+ ])) : createCommentVNode("", true)
203
+ ])
204
+ ])
205
+ ], 8, _hoisted_4)
206
+ ], 10, _hoisted_1);
207
+ };
208
+ }
209
+ });
210
+ function useAparteChat(initial = []) {
211
+ const messages = ref([...initial]);
212
+ const chatRef = ref(null);
213
+ const c = () => chatRef.value;
214
+ const onMessagesChange = (m) => {
215
+ messages.value = m;
216
+ };
217
+ return {
218
+ messages,
219
+ chatRef,
220
+ onMessagesChange,
221
+ appendMessage: (m) => c()?.appendMessage(m),
222
+ updateMessage: (id, u) => c()?.updateMessage(id, u),
223
+ updateLastMessage: (content, o) => c()?.updateLastMessage(content, o),
224
+ addSegment: (s) => c()?.addSegment(s),
225
+ updateSegment: (id, u) => c()?.updateSegment(id, u),
226
+ removeSegment: (id) => c()?.removeSegment(id),
227
+ appendToSegment: (id, content) => c()?.appendToSegment(id, content),
228
+ clearMessages: () => c()?.clearMessages(),
229
+ addBranch: (id) => c()?.addBranch(id) ?? 0,
230
+ addSiblingOf: (id, m) => c()?.addSiblingOf(id, m) ?? null,
231
+ truncateFrom: (id) => c()?.truncateFrom(id),
232
+ truncateResponsesAfter: (id) => c()?.truncateResponsesAfter(id),
233
+ injectTokenStream: (id, tokens) => c()?.injectTokenStream(id, tokens) ?? Promise.resolve(),
234
+ stopTokenStream: () => c()?.stopTokenStream(),
235
+ setConversationId: (id) => c()?.setConversationId(id) ?? Promise.resolve(),
236
+ isStreaming: () => c()?.isStreaming() ?? false
237
+ };
238
+ }
239
+ function useAparteClient(options) {
240
+ const client = new AparteClient(options ?? {});
241
+ onMounted(() => client.start());
242
+ onBeforeUnmount(() => client.stop());
243
+ return { client, abort: () => client.abort() };
244
+ }
245
+ function useConversationManager() {
246
+ let manager = null;
247
+ let unsub = null;
248
+ const conversations = ref([]);
249
+ const activeId = ref(null);
250
+ const activeConversations = computed(
251
+ () => conversations.value.filter((c) => !c.archivedAt).sort((a, b) => b.updatedAt - a.updatedAt)
252
+ );
253
+ const archivedConversations = computed(
254
+ () => conversations.value.filter((c) => !!c.archivedAt).sort((a, b) => b.updatedAt - a.updatedAt)
255
+ );
256
+ const activeConversation = computed(
257
+ () => activeId.value ? conversations.value.find((c) => c.id === activeId.value) ?? null : null
258
+ );
259
+ onBeforeUnmount(() => unsub?.());
260
+ const assert = () => {
261
+ if (!manager) throw new Error("[useConversationManager] Not initialised. Call init(adapter) first.");
262
+ return manager;
263
+ };
264
+ const init = async (adapter) => {
265
+ const m = new ConversationManager(adapter);
266
+ manager = m;
267
+ unsub = m.subscribe((convs) => {
268
+ conversations.value = [...convs];
269
+ activeId.value = m.activeId;
270
+ });
271
+ await m.init();
272
+ activeId.value = m.activeId;
273
+ AparteConfig.setConversationManager(m);
274
+ };
275
+ return {
276
+ conversations,
277
+ activeConversations,
278
+ archivedConversations,
279
+ activeId,
280
+ activeConversation,
281
+ init,
282
+ createNew: (title) => assert().createNew(title),
283
+ addMessage: (convId, message) => assert().addMessage(convId, message),
284
+ updateMessages: (convId, messages) => assert().updateMessages(convId, messages),
285
+ delete: (id) => assert().delete(id),
286
+ archive: (id) => assert().archive(id),
287
+ unarchive: (id) => assert().unarchive(id)
288
+ };
289
+ }
290
+ const _sfc_main = /* @__PURE__ */ defineComponent({
291
+ __name: "AparteUi",
292
+ props: {
293
+ name: {},
294
+ props: {},
295
+ events: {}
296
+ },
297
+ emits: ["elementEvent"],
298
+ setup(__props, { expose: __expose, emit: __emit }) {
299
+ const p = __props;
300
+ const emit = __emit;
301
+ const evtsKey = computed(() => (p.events ?? DEFAULT_UI_EVENTS).join("|"));
302
+ const hostRef = ref();
303
+ let el = null;
304
+ let cleanups = [];
305
+ function applyProps() {
306
+ if (el) applyElementProps(el, p.props ?? {}, toRaw);
307
+ }
308
+ function create() {
309
+ if (!hostRef.value) return;
310
+ el = document.createElement(p.name);
311
+ applyProps();
312
+ for (const ev of evtsKey.value.split("|").filter(Boolean)) {
313
+ const listener = (e) => emit("elementEvent", e);
314
+ el.addEventListener(ev, listener);
315
+ cleanups.push(() => el?.removeEventListener(ev, listener));
316
+ }
317
+ hostRef.value.appendChild(el);
318
+ }
319
+ function destroy() {
320
+ for (const c of cleanups) c();
321
+ cleanups = [];
322
+ el?.remove();
323
+ el = null;
324
+ }
325
+ onMounted(create);
326
+ onBeforeUnmount(destroy);
327
+ watch(() => p.name, () => {
328
+ destroy();
329
+ create();
330
+ });
331
+ watch(evtsKey, () => {
332
+ destroy();
333
+ create();
334
+ });
335
+ watch(() => p.props, applyProps, { deep: true });
336
+ __expose({
337
+ getElement: () => el,
338
+ callMethod: (methodName, ...args) => {
339
+ const fn = el?.[methodName];
340
+ return typeof fn === "function" ? fn.apply(el, args) : void 0;
341
+ }
342
+ });
343
+ return (_ctx, _cache) => {
344
+ return openBlock(), createElementBlock("span", {
345
+ ref_key: "hostRef",
346
+ ref: hostRef,
347
+ style: { "display": "contents" }
348
+ }, null, 512);
349
+ };
350
+ }
351
+ });
352
+ export {
353
+ _sfc_main$1 as AparteChat,
354
+ _sfc_main as AparteUi,
355
+ useAparteChat,
356
+ useAparteClient,
357
+ useConversationManager
358
+ };
359
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/components/AparteChat.vue","../src/composables/useAparteChat.ts","../src/composables/useAparteClient.ts","../src/composables/useConversationManager.ts","../src/components/AparteUi.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, nextTick, useId, toRaw } from 'vue';\nimport { AparteChatHost, type AparteChatHostBinding, type AparteConfigClass, type AparteChatImperativeApi } from '@aparte/core';\nimport type { AparteMessage, AparteSegment, AparteSendEventDetail, AparteActionEventDetail } from '../types.js';\n\ninterface Props {\n /** Optional: omit for an uncontrolled chat (defaults to []); use `v-model:messages` to control. */\n messages?: AparteMessage[];\n placeholder?: string;\n disabled?: boolean;\n isTyping?: boolean;\n typingText?: string;\n /** When false, Shift+Enter submits and a bare Enter inserts a newline. */\n submitOnEnter?: boolean;\n /** Freeze viewport spacer recalculation for this many ms after a conv swap. */\n layoutTransitionMs?: number;\n /**\n * Opt in to the \"centered composer when empty\" layout: the composer sits\n * vertically centered with the `empty-state` slot above it while the list is\n * empty, then slides to the bottom on the first message (~0.3s). Off by\n * default — additive (adds the `--auto-center` modifier + a `data-aparte-empty`\n * attribute the shipped `aparte.css` recipe keys off).\n */\n centerWhenEmpty?: boolean;\n /** Active conversation id (loads/persists via the registered ConversationManager). */\n conversationId?: string | null;\n /**\n * Instance {@link AparteConfigClass} for this chat. When set, aparté components\n * inside resolve THIS config instead of the global `AparteConfig` singleton, so\n * several independently-configured chats can coexist on one page. Omit for the\n * global config. Read once when the host mounts.\n */\n config?: AparteConfigClass;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n messages: () => [],\n placeholder: 'Type a message...',\n disabled: false,\n isTyping: false,\n typingText: 'Assistant is thinking...',\n submitOnEnter: true,\n layoutTransitionMs: 0,\n centerWhenEmpty: false,\n conversationId: null,\n});\n\nconst emit = defineEmits<{\n /**\n * User submitted a message from the composer. The message is **appended to\n * the thread automatically** (optimistic UI) before this fires — do NOT add\n * it again in the handler (uncontrolled → duplicates; controlled → mirror it\n * into your own `messages`). For side-effects: scroll, analytics, send.\n */\n messageSent: [event: AparteSendEventDetail];\n /** A custom bubble action (registerBubbleAction) was clicked — typed aparte-action. */\n action: [detail: AparteActionEventDetail];\n /** Active path changed (branch nav/edit/retry/streaming) — bind back to `messages`. */\n messagesChange: [messages: AparteMessage[]];\n /** Same payload as `messagesChange`, enabling `v-model:messages`. */\n 'update:messages': [messages: AparteMessage[]];\n messageAppended: [message: AparteMessage];\n /** The typing/\"thinking\" indicator toggled (the host flips it off on the first streamed token). */\n typingChange: [isTyping: boolean];\n conversationCreated: [id: string];\n}>();\n\n// useId() (Vue 3.5+) is SSR-stable — server and client agree, no hydration mismatch.\nconst hostId = `aparte-chat-${useId()}`;\nconst rootRef = ref<HTMLElement>();\nconst viewportRef = ref<HTMLElement>();\nconst composerRef = ref<HTMLElement>();\nconst internalMessages = ref<AparteMessage[]>([...props.messages]);\nconst typingActive = ref(props.isTyping);\n\nlet host: AparteChatHost | null = null;\nlet teardown: (() => void) | null = null;\n\nfunction onSend(e: Event) {\n (viewportRef.value as unknown as { requestSmoothScroll?: () => void })?.requestSmoothScroll?.();\n emit('messageSent', (e as CustomEvent<AparteSendEventDetail>).detail);\n}\n\n// Custom bubble actions bubble to the root as `aparte-action` — surface them typed.\nfunction onAction(e: Event) {\n emit('action', (e as CustomEvent<AparteActionEventDetail>).detail);\n}\n\nonMounted(() => {\n const binding: AparteChatHostBinding = {\n hostId,\n host: rootRef.value as HTMLElement,\n viewport: viewportRef.value ?? null,\n getMessages: () => internalMessages.value,\n setMessages: (m) => { internalMessages.value = m as AparteMessage[]; },\n onMessagesChange: (m) => { emit('messagesChange', m as AparteMessage[]); emit('update:messages', m as AparteMessage[]); },\n onMessageAppended: (m) => emit('messageAppended', m as AparteMessage),\n onTypingChange: (t) => { typingActive.value = t; emit('typingChange', t); },\n onStreamingChange: () => { /* exposed via isStreaming() */ },\n afterRender: (cb) => { void nextTick(cb); },\n resetComposer: () => (composerRef.value as unknown as { reset?: () => void })?.reset?.(),\n };\n host = new AparteChatHost(binding, {\n layoutTransitionMs: props.layoutTransitionMs,\n conversationId: props.conversationId ?? null,\n onConversationCreated: (id) => emit('conversationCreated', id),\n // Unwrap Vue's reactive proxy: the config is a plain class with internal\n // Map registries the host/components must operate on directly, not through a\n // deep reactive proxy (which would wrap those Maps and break lookups).\n config: props.config ? toRaw(props.config) : undefined,\n });\n teardown = host.bind();\n host.syncBubbles();\n composerRef.value?.addEventListener('aparte-send', onSend);\n rootRef.value?.addEventListener('aparte-action', onAction);\n});\n\nonBeforeUnmount(() => {\n composerRef.value?.removeEventListener('aparte-send', onSend);\n rootRef.value?.removeEventListener('aparte-action', onAction);\n teardown?.();\n teardown = null;\n host = null;\n});\n\n// Parent push → internal list (guarded against the host's own emit round-trip).\nwatch(() => props.messages, (m) => {\n if (m === internalMessages.value) return;\n internalMessages.value = [...m];\n if (m.length === 0) host?.clearRenderCache();\n});\n\n// Reconcile bubbles after the rendered list changes (host queries the DOM).\nwatch(internalMessages, () => { void nextTick(() => host?.syncBubbles()); });\n\nwatch(() => props.isTyping, (t) => { typingActive.value = t; });\nwatch(() => props.conversationId, (id) => { void host?.setConversationId(id ?? null); });\n\n// ── Imperative API (forwards to the host) ──\nconst appendMessage = (m: AparteMessage) => host?.appendMessage(m);\nconst updateMessage = (id: string, u: Partial<AparteMessage>) => host?.updateMessage(id, u);\nconst updateLastMessage = (c: string, o?: { append?: boolean }) => host?.updateLastMessage(c, o);\nconst addSegment = (s: AparteSegment) => host?.addSegment(s);\nconst updateSegment = (id: string, u: Partial<AparteSegment>) => host?.updateSegment(id, u);\nconst removeSegment = (id: string) => host?.removeSegment(id);\nconst appendToSegment = (id: string, c: string) => host?.appendToSegment(id, c);\nconst getMessages = () => host?.getMessages() ?? internalMessages.value;\nconst clearMessages = () => host?.clearMessages();\nconst addBranch = (id: string) => host?.addBranch(id) ?? 0;\nconst addSiblingOf = (id: string, m: AparteMessage) => host?.addSiblingOf(id, m) ?? null;\nconst truncateFrom = (id: string) => host?.truncateFrom(id);\nconst truncateResponsesAfter = (id: string) => host?.truncateResponsesAfter(id);\nconst injectTokenStream = (id: string, tokens: AsyncIterable<string>) =>\n host?.streamTokens(id, tokens) ?? Promise.resolve();\nconst stopTokenStream = () => host?.stopTokenStream();\nconst setConversationId = (id: string | null) => host?.setConversationId(id) ?? Promise.resolve();\nconst scrollToBottom = () => (viewportRef.value as unknown as { scrollToBottom?: () => void })?.scrollToBottom?.();\nconst focusInput = () => (composerRef.value as unknown as { focus?: () => void })?.focus?.();\nconst isStreaming = () => host?.isStreaming ?? false;\n\n// `getViewport()` (not a raw `viewport` ref): the same accessor on all four\n// wrappers — Svelte 4 can only expose functions, so the shared name is one.\nconst getViewport = () => viewportRef.value ?? null;\n\n// `satisfies` makes a dropped or mistyped method a compile error — the canonical\n// AparteChatImperativeApi is enforced here, not just aliased for consumers.\ndefineExpose({\n appendMessage, updateMessage, updateLastMessage, addSegment, updateSegment, removeSegment,\n appendToSegment, getMessages, clearMessages, addBranch, addSiblingOf, truncateFrom,\n truncateResponsesAfter, injectTokenStream, stopTokenStream, setConversationId,\n scrollToBottom, focusInput, isStreaming, getViewport,\n} satisfies AparteChatImperativeApi);\n</script>\n\n<template>\n <div\n :class=\"['aparte-chat-container', { 'aparte-chat-container--auto-center': centerWhenEmpty }]\"\n data-aparte-chat\n :data-aparte-empty=\"centerWhenEmpty && internalMessages.length === 0 ? '' : null\"\n :id=\"hostId\"\n ref=\"rootRef\"\n >\n <aparte-chat-viewport ref=\"viewportRef\" framework-managed=\"\">\n <!-- Welcome / placeholder shown inside the viewport while there are no\n messages (a real empty-state region). -->\n <slot v-if=\"internalMessages.length === 0\" name=\"empty-state\" />\n <!-- `bubble` scoped slot renders your OWN element per message in place of\n <aparte-chat-bubble>; driven by the reactive list so it streams live. -->\n <template v-for=\"m in internalMessages\" :key=\"m.id\">\n <slot name=\"bubble\" :message=\"m\">\n <aparte-chat-bubble\n :message-id=\"m.id\"\n :data-role=\"m.role\"\n :timestamp=\"m.timestamp\"\n :content=\"m.content\"\n :streaming=\"(m.status === 'streaming' || m.status === 'pending') ? '' : null\"\n />\n </slot>\n </template>\n <aparte-chat-status :visible=\"typingActive ? '' : null\" :text=\"typingText\" />\n </aparte-chat-viewport>\n\n <!-- Content above the composer (banner, disclaimer, context chip). -->\n <slot name=\"above-composer\" />\n\n <!-- `.attr` forces attribute-setting: core's <aparte-composer> exposes\n `placeholder`/`disabled` as getter-only accessors, so Vue's default\n property-set (it prefers props on custom elements) would throw and the\n value would silently never apply. -->\n <aparte-composer\n ref=\"composerRef\"\n :target=\"hostId\"\n :placeholder.attr=\"placeholder\"\n :disabled.attr=\"disabled ? '' : null\"\n :submit-on-enter=\"submitOnEnter ? null : 'false'\"\n >\n <!-- Custom composer via the `composer` slot; falls back to the default\n shell (add-attachment · input · send). Compose the headless\n aparte-composer-* primitives freely for a skin-specific layout. -->\n <slot name=\"composer\">\n <div class=\"aparte-composer-shell\">\n <aparte-composer-attachments></aparte-composer-attachments>\n <div class=\"aparte-composer-row\">\n <aparte-composer-add-attachment></aparte-composer-add-attachment>\n <aparte-composer-input></aparte-composer-input>\n <aparte-composer-send></aparte-composer-send>\n </div>\n <!-- Footer slots (model selector, token counter…). The row is\n removed from view by .aparte-composer-footer:empty when unused. -->\n <div\n v-if=\"$slots['footer-left'] || $slots['footer-center'] || $slots['footer-right']\"\n class=\"aparte-composer-footer\"\n >\n <slot name=\"footer-left\" />\n <slot name=\"footer-center\" />\n <slot name=\"footer-right\" />\n </div>\n </div>\n </slot>\n </aparte-composer>\n </div>\n</template>\n","import { ref, type Ref } from 'vue';\nimport type { AparteChatImperativeApi } from '@aparte/core';\nimport type { AparteMessage, AparteSegment } from '../types.js';\n\n/**\n * The imperative surface `<AparteChat>` exposes via `defineExpose` — the\n * canonical contract shared by all four wrappers (`AparteChatImperativeApi`).\n */\nexport type AparteChatInstance = AparteChatImperativeApi;\n\n/**\n * Idiomatic Vue ergonomics for `<AparteChat>`. Owns the `messages` ref and a\n * component template ref so the consumer skips the manual\n * `@messages-change` → `messages` round-trip.\n *\n * @example\n * const chat = useAparteChat();\n * // template:\n * // <AparteChat :ref=\"chat.chatRef\" :messages=\"chat.messages.value\"\n * // @messages-change=\"chat.onMessagesChange\" />\n */\nexport function useAparteChat(initial: AparteMessage[] = []) {\n const messages = ref<AparteMessage[]>([...initial]) as Ref<AparteMessage[]>;\n const chatRef = ref<AparteChatInstance | null>(null);\n const c = () => chatRef.value;\n const onMessagesChange = (m: AparteMessage[]) => { messages.value = m; };\n\n return {\n messages,\n chatRef,\n onMessagesChange,\n appendMessage: (m: AparteMessage) => c()?.appendMessage(m),\n updateMessage: (id: string, u: Partial<AparteMessage>) => c()?.updateMessage(id, u),\n updateLastMessage: (content: string, o?: { append?: boolean }) => c()?.updateLastMessage(content, o),\n addSegment: (s: AparteSegment) => c()?.addSegment(s),\n updateSegment: (id: string, u: Partial<AparteSegment>) => c()?.updateSegment(id, u),\n removeSegment: (id: string) => c()?.removeSegment(id),\n appendToSegment: (id: string, content: string) => c()?.appendToSegment(id, content),\n clearMessages: () => c()?.clearMessages(),\n addBranch: (id: string) => c()?.addBranch(id) ?? 0,\n addSiblingOf: (id: string, m: AparteMessage) => c()?.addSiblingOf(id, m) ?? null,\n truncateFrom: (id: string) => c()?.truncateFrom(id),\n truncateResponsesAfter: (id: string) => c()?.truncateResponsesAfter(id),\n injectTokenStream: (id: string, tokens: AsyncIterable<string>) =>\n c()?.injectTokenStream(id, tokens) ?? Promise.resolve(),\n stopTokenStream: () => c()?.stopTokenStream(),\n setConversationId: (id: string | null) => c()?.setConversationId(id) ?? Promise.resolve(),\n isStreaming: () => c()?.isStreaming() ?? false,\n };\n}\n","import { onMounted, onBeforeUnmount } from 'vue';\nimport { AparteClient, type AparteClientOptions } from '@aparte/core';\n\n/**\n * Mounts an `AparteClient` that bridges `aparte-send` events to the configured AI\n * providers. Starts on mount, stops on unmount. Vue equivalent of Angular's\n * `AparteAiService`.\n */\nexport function useAparteClient(options?: AparteClientOptions) {\n const client = new AparteClient(options ?? {});\n onMounted(() => client.start());\n onBeforeUnmount(() => client.stop());\n return { client, abort: () => client.abort() };\n}\n","import { ref, computed, onBeforeUnmount, type Ref } from 'vue';\nimport {\n AparteConfig,\n ConversationManager,\n type AparteConversation,\n type AparteStorageAdapter,\n} from '@aparte/core';\nimport type { AparteMessage } from '../types.js';\n\n/**\n * Vue-reactive wrapper around the core `ConversationManager`. The active\n * conversation is owned by the chat component's controller; switch by binding\n * `conversationId` on `<AparteChat>`. Vue equivalent of Angular's\n * `ConversationManagerService`.\n */\nexport function useConversationManager() {\n let manager: ConversationManager | null = null;\n let unsub: (() => void) | null = null;\n\n const conversations = ref<AparteConversation[]>([]) as Ref<AparteConversation[]>;\n const activeId = ref<string | null>(null);\n\n const activeConversations = computed(() =>\n conversations.value.filter((c) => !c.archivedAt).sort((a, b) => b.updatedAt - a.updatedAt),\n );\n const archivedConversations = computed(() =>\n conversations.value.filter((c) => !!c.archivedAt).sort((a, b) => b.updatedAt - a.updatedAt),\n );\n const activeConversation = computed(() =>\n activeId.value ? conversations.value.find((c) => c.id === activeId.value) ?? null : null,\n );\n\n onBeforeUnmount(() => unsub?.());\n\n const assert = (): ConversationManager => {\n if (!manager) throw new Error('[useConversationManager] Not initialised. Call init(adapter) first.');\n return manager;\n };\n\n const init = async (adapter: AparteStorageAdapter): Promise<void> => {\n const m = new ConversationManager(adapter);\n manager = m;\n unsub = m.subscribe((convs) => {\n conversations.value = [...convs];\n activeId.value = m.activeId;\n });\n await m.init();\n activeId.value = m.activeId;\n AparteConfig.setConversationManager(m);\n };\n\n return {\n conversations,\n activeConversations,\n archivedConversations,\n activeId,\n activeConversation,\n init,\n createNew: (title?: string) => assert().createNew(title),\n addMessage: (convId: string, message: AparteMessage) => assert().addMessage(convId, message),\n updateMessages: (convId: string, messages: AparteMessage[]) => assert().updateMessages(convId, messages),\n delete: (id: string) => assert().delete(id),\n archive: (id: string) => assert().archive(id),\n unarchive: (id: string) => assert().unarchive(id),\n };\n}\n","<script setup lang=\"ts\">\nimport { ref, computed, watch, onMounted, onBeforeUnmount, toRaw } from 'vue';\nimport { applyElementProps, DEFAULT_UI_EVENTS } from '@aparte/core';\n\nconst p = defineProps<{\n /** The custom element tag name (e.g. 'aparte-model-selector'). */\n name: string;\n /** Props to apply. Keys starting with `--` become CSS variables. */\n props?: Record<string, unknown>;\n /**\n * Which custom events to forward through `elementEvent`. Defaults to the\n * interactive aparté surface (DEFAULT_UI_EVENTS); pass your own list to listen to\n * other events (e.g. ['aparte-composer-change'] for attachments).\n */\n events?: string[];\n}>();\n\nconst emit = defineEmits<{ elementEvent: [event: CustomEvent] }>();\n\n// A stable key so a fresh inline `:events` array doesn't thrash the element —\n// only a real change to the event names rebinds (mirrors React's evtsKey).\nconst evtsKey = computed(() => (p.events ?? DEFAULT_UI_EVENTS).join('|'));\n\nconst hostRef = ref<HTMLElement>();\nlet el: HTMLElement | null = null;\nlet cleanups: Array<() => void> = [];\n\n// Vue passes `toRaw` so objects are unwrapped from the reactive proxy before\n// reaching the plain custom element (a deep proxy breaks Maps/class internals).\nfunction applyProps() {\n if (el) applyElementProps(el, p.props ?? {}, toRaw);\n}\n\nfunction create() {\n if (!hostRef.value) return;\n el = document.createElement(p.name);\n applyProps();\n for (const ev of evtsKey.value.split('|').filter(Boolean)) {\n const listener = (e: Event) => emit('elementEvent', e as CustomEvent);\n el.addEventListener(ev, listener);\n cleanups.push(() => el?.removeEventListener(ev, listener));\n }\n hostRef.value.appendChild(el);\n}\n\nfunction destroy() {\n for (const c of cleanups) c();\n cleanups = [];\n el?.remove();\n el = null;\n}\n\nonMounted(create);\nonBeforeUnmount(destroy);\nwatch(() => p.name, () => { destroy(); create(); });\nwatch(evtsKey, () => { destroy(); create(); });\nwatch(() => p.props, applyProps, { deep: true });\n\ndefineExpose({\n getElement: () => el,\n callMethod: (methodName: string, ...args: unknown[]) => {\n const fn = (el as unknown as Record<string, unknown>)?.[methodName];\n return typeof fn === 'function' ? (fn as (...a: unknown[]) => unknown).apply(el, args) : undefined;\n },\n});\n</script>\n\n<template>\n <span ref=\"hostRef\" style=\"display: contents\"></span>\n</template>\n"],"names":["_createElementBlock","_createElementVNode","_renderSlot","_Fragment","_renderList","$slots","_openBlock"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Public types for the Vue wrapper — all re-exported from `@aparte/core`, the
3
+ * single source of truth. `AparteSendEventDetail` used to be re-declared here
4
+ * WITHOUT `targetId`, which the composer actually sends (multi-instance scoping);
5
+ * re-export the canonical one so the field isn't silently dropped from the type.
6
+ */
7
+ export type { AparteMessage, AparteSegment, AparteTextSegment, AparteCodeSegment, AparteThinkingSegment, AparteTerminalSegment, AparteSendEventDetail, AparteActionEventDetail, } from '@aparte/core';
8
+ /** Props of the `<AparteUi>` universal pass-through proxy. */
9
+ export interface AparteUiProps {
10
+ /** The custom element tag name (e.g. 'aparte-model-selector'). */
11
+ name: string;
12
+ /** Props to apply. Keys starting with `--` become CSS variables. */
13
+ props?: Record<string, unknown>;
14
+ /**
15
+ * Which custom events to forward through `elementEvent`. Defaults to the
16
+ * interactive aparté surface (`DEFAULT_UI_EVENTS` from `@aparte/core`).
17
+ */
18
+ events?: string[];
19
+ }
20
+ /**
21
+ * The imperative surface `<AparteUi>` exposes (template ref) — the same
22
+ * `getElement`/`callMethod` contract on all four wrappers.
23
+ */
24
+ export interface AparteUiHandle {
25
+ getElement<T extends HTMLElement = HTMLElement>(): T | null;
26
+ callMethod<T = unknown>(methodName: string, ...args: unknown[]): T | undefined;
27
+ }
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,YAAY,EACR,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,GAC1B,MAAM,cAAc,CAAC;AAEtB,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC1B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC3B,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,KAAK,CAAC,GAAG,IAAI,CAAC;IAC5D,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;CAClF"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@aparte/vue",
3
+ "version": "0.2.0-alpha.0",
4
+ "description": "Vue 3 wrapper for aparté — an ergonomic <AparteChat> component plus composables over the framework-agnostic web components.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "@aparte-workspace/source": "./src/index.ts",
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "peerDependencies": {
27
+ "vue": "^3.5.0",
28
+ "@aparte/core": "0.2.0-alpha.0"
29
+ },
30
+ "devDependencies": {
31
+ "@vitejs/plugin-vue": "^5.0.0",
32
+ "@vue/test-utils": "^2.4.6",
33
+ "jsdom": "^22.1.0",
34
+ "typescript": "^5.4.0",
35
+ "vite": "^6.0.0",
36
+ "vue": "^3.5.0",
37
+ "vue-tsc": "^2.0.0",
38
+ "@aparte/core": "0.2.0-alpha.0"
39
+ },
40
+ "keywords": [
41
+ "vue",
42
+ "vue3",
43
+ "aparte",
44
+ "chat",
45
+ "ai",
46
+ "web-components"
47
+ ],
48
+ "license": "MIT",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/apartejs/aparte.git",
52
+ "directory": "packages/wrappers/vue"
53
+ },
54
+ "bugs": {
55
+ "url": "https://github.com/apartejs/aparte/issues"
56
+ },
57
+ "scripts": {
58
+ "dev": "vite",
59
+ "build": "vite build && vue-tsc -b --emitDeclarationOnly --force",
60
+ "typecheck": "vue-tsc -b --emitDeclarationOnly --force",
61
+ "preview": "vite preview",
62
+ "test": "vitest",
63
+ "test:run": "vitest run",
64
+ "test:coverage": "vitest run --coverage"
65
+ }
66
+ }