@elia-assistant/chatui 1.0.25 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -24
- package/dist/App.d.ts +0 -1
- package/dist/chat-store.js +141 -139
- package/dist/chatui.iife.js +32 -32
- package/dist/chunks/{middleware-Brf3TxqP.js → middleware-BG8DEvI8.js} +28 -23
- package/dist/chunks/{settingsStore-yU8h8KVJ.js → settingsStore-DzpdfGcd.js} +94 -81
- package/dist/chunks/translation-BL8dP5qm.js +94 -0
- package/dist/createChat.d.ts +17 -0
- package/dist/i18n.d.ts +8 -2
- package/dist/iife.d.ts +2 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +3695 -2278
- package/dist/lib/storage.d.ts +7 -3
- package/dist/store/StoreContext.d.ts +32 -0
- package/dist/store/chatStore.d.ts +8 -3
- package/dist/store/settingsStore.d.ts +10 -3
- package/dist/store.js +2 -2
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunks/i18n-Vi-f1TFq.js +0 -1320
package/README.md
CHANGED
|
@@ -15,20 +15,74 @@ Ships as a pre-built ESM bundle and a CDN-ready IIFE bundle. Config-compatible w
|
|
|
15
15
|
npm install @elia-assistant/chatui
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
```
|
|
19
|
-
// src/
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
18
|
+
```tsx
|
|
19
|
+
// src/ChatWidget.tsx
|
|
20
|
+
import { useEffect, useRef } from 'react'
|
|
21
|
+
import { createChat, type ChatInstance } from '@elia-assistant/chatui'
|
|
22
|
+
|
|
23
|
+
export function ChatWidget() {
|
|
24
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
25
|
+
const instanceRef = useRef<ChatInstance | null>(null)
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!containerRef.current || instanceRef.current) return
|
|
29
|
+
instanceRef.current = createChat({
|
|
30
|
+
target: containerRef.current,
|
|
31
|
+
webhookUrl: 'https://your-n8n.example.com/webhook/abc',
|
|
32
|
+
mode: 'window',
|
|
33
|
+
})
|
|
34
|
+
return () => {
|
|
35
|
+
// Deferred so StrictMode's double-invoke doesn't unmount mid-render.
|
|
36
|
+
const instance = instanceRef.current
|
|
37
|
+
instanceRef.current = null
|
|
38
|
+
setTimeout(() => instance?.unmount(), 0)
|
|
39
|
+
}
|
|
40
|
+
}, [])
|
|
41
|
+
|
|
42
|
+
return <div ref={containerRef} style={{ height: '100%' }} />
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`createChat()` mounts into a shadow root, so the widget's CSS is fully isolated from your app's.
|
|
47
|
+
|
|
48
|
+
The host's `vite.config.ts` needs `optimizeDeps.exclude: ['@elia-assistant/chatui']` so Vite doesn't re-bundle the already-bundled package. See [example.html](./example.html) for the full integration guide.
|
|
49
|
+
|
|
50
|
+
#### Raw component tree (no shadow root)
|
|
51
|
+
|
|
52
|
+
If you need the widget inside your own React tree rather than a shadow root, render `<App />` directly. It reads its stores and translations from context, so you must build them and supply both providers:
|
|
22
53
|
|
|
23
|
-
|
|
54
|
+
```tsx
|
|
55
|
+
import { I18nextProvider } from 'react-i18next'
|
|
56
|
+
import {
|
|
57
|
+
App,
|
|
58
|
+
StoreProvider,
|
|
59
|
+
createChatI18n,
|
|
60
|
+
createSettingsStore,
|
|
61
|
+
createChatStore,
|
|
62
|
+
} from '@elia-assistant/chatui'
|
|
63
|
+
|
|
64
|
+
// Create these once, outside the component - not on every render.
|
|
65
|
+
const i18n = createChatI18n()
|
|
66
|
+
const settingsStore = createSettingsStore(i18n)
|
|
67
|
+
const chatStore = createChatStore()
|
|
68
|
+
|
|
69
|
+
settingsStore.getState().setConfig({
|
|
24
70
|
webhookUrl: 'https://your-n8n.example.com/webhook/abc',
|
|
25
71
|
mode: 'window',
|
|
26
72
|
})
|
|
27
73
|
|
|
28
|
-
|
|
74
|
+
export function ChatWidget() {
|
|
75
|
+
return (
|
|
76
|
+
<I18nextProvider i18n={i18n}>
|
|
77
|
+
<StoreProvider settingsStore={settingsStore} chatStore={chatStore}>
|
|
78
|
+
<App />
|
|
79
|
+
</StoreProvider>
|
|
80
|
+
</I18nextProvider>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
29
83
|
```
|
|
30
84
|
|
|
31
|
-
|
|
85
|
+
Without both providers `<App />` throws `useSettingsStore must be used within a <StoreProvider>`. Note that this path does not inject the widget's CSS for you - prefer `createChat()` unless you specifically need to avoid the shadow root.
|
|
32
86
|
|
|
33
87
|
### Plain HTML (CDN, no bundler)
|
|
34
88
|
|
|
@@ -56,9 +110,24 @@ const instance = createChat({
|
|
|
56
110
|
webhookUrl: '...',
|
|
57
111
|
mode: 'window',
|
|
58
112
|
})
|
|
59
|
-
|
|
113
|
+
|
|
114
|
+
instance.setConfig({ botName: 'Support' }) // patch config without remounting
|
|
115
|
+
instance.setTheme('cosmos')
|
|
116
|
+
instance.setLanguage('sk')
|
|
117
|
+
instance.unmount() // remove
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Multiple widgets on one page
|
|
121
|
+
|
|
122
|
+
Every `createChat()` call gets its own config, theme, language and conversation state, so two widgets never fight over each other's settings. They do share `localStorage`, though - pass `storageNamespace` to keep their persisted state apart:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
createChat({ target: '#support', webhookUrl: '...' })
|
|
126
|
+
createChat({ target: '#demo', webhookUrl: '...', storageNamespace: 'demo' })
|
|
60
127
|
```
|
|
61
128
|
|
|
129
|
+
Omit `storageNamespace` for the single-widget case and the default storage keys are used, so existing installs keep their saved settings and history.
|
|
130
|
+
|
|
62
131
|
---
|
|
63
132
|
|
|
64
133
|
## Features
|
|
@@ -69,15 +138,16 @@ const instance = createChat({
|
|
|
69
138
|
- **Bottom-sheet style** — opt-in `fullscreenSheet` mode covers ~3/4 of the screen with a rounded top instead of going edge-to-edge
|
|
70
139
|
- **Optional tabs** — surface a Notifications feed (URL or inline JSON) and a searchable FAQ alongside the chat
|
|
71
140
|
- **SSE streaming** — optional word-by-word bot responses
|
|
72
|
-
- **
|
|
141
|
+
- **16 built-in themes** — Sunrise, Ivory, Cherry, Sky, Lavender, Nice, Navy, Amber, Slate, Graphite, Stone, Cosmos, Forest, Ocean, Cherry Dark, Midnight; switch at runtime
|
|
73
142
|
- **30 built-in avatars** + file upload (max 500 KB) or URL — same picker for the floating button icon
|
|
74
143
|
- **CTA popup** — timed speech-bubble with optional Web Audio notification, window mode only
|
|
75
144
|
- **Conversation history** — optional sidebar with persistent multi-session history
|
|
76
145
|
- **Per-language content** — initial messages, bot name, CTA text, welcome subtitle, tab titles, all configurable per language
|
|
77
|
-
- **Multilingual UI** — English and Slovak bundled; extend via `
|
|
146
|
+
- **Multilingual UI** — English and Slovak bundled; extend via `instance.i18n.addResourceBundle` at runtime
|
|
78
147
|
- **Markdown rendering** — bot messages render full GFM via `react-markdown`
|
|
79
148
|
- **Configurable "Powered by" footer** — change the link text/URL or hide it entirely
|
|
80
149
|
- **Persistent settings** — config + theme + language survive page reloads (`localStorage`)
|
|
150
|
+
- **Multi-instance safe** — every `createChat()` call is fully isolated; `storageNamespace` separates their persisted state too
|
|
81
151
|
- **Lockable UI** — `hideSettings: true` removes the gear, theme picker, and settings modal (default for npm consumers)
|
|
82
152
|
- **Export config** — generate ready-to-paste host code from the settings modal
|
|
83
153
|
|
|
@@ -85,7 +155,7 @@ const instance = createChat({
|
|
|
85
155
|
|
|
86
156
|
## Configuration
|
|
87
157
|
|
|
88
|
-
All options live on `ChatConfig`.
|
|
158
|
+
All options live on `ChatConfig`. Pass them to `createChat()`, or patch them later via `instance.setConfig()`.
|
|
89
159
|
|
|
90
160
|
### Required
|
|
91
161
|
|
|
@@ -160,7 +230,7 @@ A tab is shown only if its block has `feedUrl` or `items`. If neither tab is con
|
|
|
160
230
|
| `initialMessages` | `string[]` | Global fallback initial bot messages |
|
|
161
231
|
| `i18n[lang]` | `LangOverride` | Per-language content overrides |
|
|
162
232
|
|
|
163
|
-
To set the initial UI language,
|
|
233
|
+
To set the initial UI language, pass `language: 'sk'` to `createChat()` (or call `instance.setLanguage('sk')` later). On a fresh browser, the default falls back to `navigator.language` then `'en'`.
|
|
164
234
|
|
|
165
235
|
`LangOverride` fields: `initialMessages`, `ctaText`, `botName`, `welcomeSubtitle`, `tabs.{notifications,help,chat}.title`.
|
|
166
236
|
|
|
@@ -172,16 +242,22 @@ Resolution chain: `i18n[activeLang].X` -> `i18n['en'].X` -> global `config.X`.
|
|
|
172
242
|
|
|
173
243
|
| ID | Name | Style | Accent |
|
|
174
244
|
|---|---|---|---|
|
|
175
|
-
| `midnight` | Midnight | Dark | Indigo `#6366f1` |
|
|
176
|
-
| `ivory` | Ivory | Light | Indigo `#4338ca` |
|
|
177
245
|
| `sunrise` | Sunrise | Light | Orange `#f97316` |
|
|
178
|
-
| `
|
|
179
|
-
| `forest` | Forest | Dark | Green `#22c55e` |
|
|
180
|
-
| `ocean` | Ocean | Dark | Cyan `#06b6d4` |
|
|
246
|
+
| `ivory` | Ivory | Light | Indigo `#4338ca` |
|
|
181
247
|
| `cherry` | Cherry | Light | Red `#ef4444` |
|
|
182
|
-
| `
|
|
248
|
+
| `sky` | Sky | Light | Blue `#5ba1da` |
|
|
183
249
|
| `lavender` | Lavender | Light | Violet `#8b5cf6` |
|
|
250
|
+
| `nice` | Nice | Light | Cyan `#3f7e83` |
|
|
251
|
+
| `navy` | Navy | Light | Blue `#ebf3f2` |
|
|
184
252
|
| `amber` | Amber | Dark | Amber `#f59e0b` |
|
|
253
|
+
| `slate` | Slate | Light | Blue `#38bdf8` |
|
|
254
|
+
| `graphite` | Graphite | Blue `#4185eb` |
|
|
255
|
+
| `stone` | Stone | Light | Orange `#fbbf24` |
|
|
256
|
+
| `cosmos` | Cosmos | Dark | Purple `#a855f7` |
|
|
257
|
+
| `forest` | Forest | Dark | Green `#22c55e` |
|
|
258
|
+
| `ocean` | Ocean | Dark | Cyan `#06b6d4` |
|
|
259
|
+
| `cherryDark` | Cherry Dark | Dark | Red `#ef4444` |
|
|
260
|
+
| `midnight` | Midnight | Dark | Indigo `#6366f1` |
|
|
185
261
|
|
|
186
262
|
Defined in `src/themes.ts` — add your own by appending to the array.
|
|
187
263
|
|
|
@@ -256,19 +332,22 @@ The FAQ tab includes a search box that filters by case-insensitive substring acr
|
|
|
256
332
|
Bundled languages: `en`, `sk` (split into `dist/chunks/translation-*.js`, lazy-loaded).
|
|
257
333
|
To add another without rebuilding chatui, register the bundle yourself:
|
|
258
334
|
|
|
335
|
+
Each instance owns a private i18next instance, exposed as `instance.i18n`. Register the bundle there - a bundle added to the global `i18next` singleton will not reach the widget:
|
|
336
|
+
|
|
259
337
|
```ts
|
|
260
|
-
|
|
261
|
-
import { useSettingsStore } from '@elia-assistant/chatui/store'
|
|
338
|
+
const instance = createChat({ target: '#chat', webhookUrl: '...' })
|
|
262
339
|
|
|
263
|
-
i18n
|
|
340
|
+
instance.i18n?.addResourceBundle('fr', 'translation', {
|
|
264
341
|
welcome: { subtitle: 'Commencez une conversation.' },
|
|
265
342
|
input: { placeholder: 'Tapez un message...' },
|
|
266
343
|
// ...full keys: see node_modules/@elia-assistant/chatui/dist/chunks/translation-*.js
|
|
267
344
|
})
|
|
268
345
|
|
|
269
|
-
|
|
346
|
+
instance.setLanguage('fr')
|
|
270
347
|
```
|
|
271
348
|
|
|
349
|
+
`instance.i18n` is `undefined` only when `createChat()` was called before `DOMContentLoaded` and the widget has not mounted yet.
|
|
350
|
+
|
|
272
351
|
Then any per-language overrides live under `config.i18n.fr`.
|
|
273
352
|
|
|
274
353
|
---
|
|
@@ -278,13 +357,14 @@ Then any per-language overrides live under `config.i18n.fr`.
|
|
|
278
357
|
Hidden by default for npm consumers — configuration is expected to live in code. Flip `hideSettings: false` to expose the gear, theme picker, and settings modal (handy for admin dashboards, internal tools, live demos):
|
|
279
358
|
|
|
280
359
|
```ts
|
|
281
|
-
|
|
360
|
+
createChat({
|
|
361
|
+
target: '#chat',
|
|
282
362
|
webhookUrl: '...',
|
|
283
363
|
hideSettings: false,
|
|
284
364
|
})
|
|
285
365
|
```
|
|
286
366
|
|
|
287
|
-
The settings modal includes an **Export config** button that generates the exact `setConfig()` call for pasting into a host project.
|
|
367
|
+
The settings modal includes an **Export config** button that generates the exact `instance.setConfig()` call for pasting into a host project.
|
|
288
368
|
|
|
289
369
|
The chatui repo's own `npm run dev` unlocks the UI automatically.
|
|
290
370
|
|
|
@@ -333,6 +413,26 @@ React 19 is a peer dependency for the ESM bundle. The IIFE bundle includes React
|
|
|
333
413
|
|
|
334
414
|
---
|
|
335
415
|
|
|
416
|
+
## Migrating from 1.x
|
|
417
|
+
|
|
418
|
+
2.0 removed the module-level global stores. Each `createChat()` call now owns its config, theme, language and conversation state, which is what makes multiple widgets on one page possible. `createChat()` itself is unchanged, so **CDN / `Chatui.createChat()` integrations need no changes** - and the default `localStorage` keys are unchanged, so saved settings and chat history carry over.
|
|
419
|
+
|
|
420
|
+
What did change:
|
|
421
|
+
|
|
422
|
+
| 1.x | 2.0 |
|
|
423
|
+
|---|---|
|
|
424
|
+
| `import { useSettingsStore } from '@elia-assistant/chatui/store'` | removed - use the `createChat()` instance |
|
|
425
|
+
| `useSettingsStore.getState().setConfig({...})` | `instance.setConfig({...})`, or pass the options to `createChat()` |
|
|
426
|
+
| `useSettingsStore.getState().setTheme(id)` | `instance.setTheme(id)` |
|
|
427
|
+
| `useSettingsStore.getState().setLanguage(lang)` | `instance.setLanguage(lang)` |
|
|
428
|
+
| `useChatStore` from `@elia-assistant/chatui/chat-store` | removed - no global chat store exists |
|
|
429
|
+
| `i18next.addResourceBundle(...)` on the global singleton | `instance.i18n?.addResourceBundle(...)` |
|
|
430
|
+
| `<App />` rendered directly | still supported, but must be wrapped in `<I18nextProvider>` + `<StoreProvider>` (see [Install](#raw-component-tree-no-shadow-root)) |
|
|
431
|
+
|
|
432
|
+
The `./store` and `./chat-store` subpath exports now expose only the `createSettingsStore` / `createChatStore` factories and their state types. Importing `useSettingsStore` from them fails at import time rather than silently returning something without `.getState()`.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
336
436
|
## License
|
|
337
437
|
|
|
338
438
|
MIT - see [LICENSE](./LICENSE). Built by [Igor Demovic](https://github.com/idemovic).
|
package/dist/App.d.ts
CHANGED
package/dist/chat-store.js
CHANGED
|
@@ -1,149 +1,151 @@
|
|
|
1
|
-
import { n as e, t } from "./chunks/middleware-
|
|
1
|
+
import { n as e, r as t, t as n } from "./chunks/middleware-BG8DEvI8.js";
|
|
2
2
|
//#region src/store/chatStore.ts
|
|
3
|
-
function
|
|
3
|
+
function r() {
|
|
4
4
|
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
...e.messages,
|
|
24
|
-
[t]: []
|
|
25
|
-
}
|
|
26
|
-
})), t;
|
|
27
|
-
},
|
|
28
|
-
setToken(t, n, r) {
|
|
29
|
-
e((e) => ({ tokens: {
|
|
30
|
-
...e.tokens,
|
|
31
|
-
[t]: {
|
|
32
|
-
token: n,
|
|
33
|
-
expiresAt: r
|
|
34
|
-
}
|
|
35
|
-
} }));
|
|
36
|
-
},
|
|
37
|
-
setActiveSession(t) {
|
|
38
|
-
e({ activeSessionId: t });
|
|
39
|
-
},
|
|
40
|
-
addMessage(t, n) {
|
|
41
|
-
e((e) => {
|
|
42
|
-
let r = e.messages[t] ?? [];
|
|
43
|
-
if (r.some((e) => e.id === n.id)) return {};
|
|
44
|
-
let i = [...r, n];
|
|
45
|
-
return {
|
|
46
|
-
sessions: e.sessions.map((e) => e.id !== t || e.title !== "New conversation" || n.role !== "user" ? e : {
|
|
47
|
-
...e,
|
|
48
|
-
title: n.content.slice(0, 50)
|
|
49
|
-
}),
|
|
6
|
+
function i(i) {
|
|
7
|
+
return e()(n((e, t) => ({
|
|
8
|
+
sessions: [],
|
|
9
|
+
activeSessionId: null,
|
|
10
|
+
messages: {},
|
|
11
|
+
isStreaming: !1,
|
|
12
|
+
awaitingAgentReply: {},
|
|
13
|
+
tokens: {},
|
|
14
|
+
createSession() {
|
|
15
|
+
let t = r(), n = {
|
|
16
|
+
id: t,
|
|
17
|
+
title: "New conversation",
|
|
18
|
+
createdAt: Date.now()
|
|
19
|
+
};
|
|
20
|
+
return e((e) => ({
|
|
21
|
+
sessions: [n, ...e.sessions],
|
|
22
|
+
activeSessionId: t,
|
|
50
23
|
messages: {
|
|
51
24
|
...e.messages,
|
|
52
|
-
[t]:
|
|
25
|
+
[t]: []
|
|
53
26
|
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!i) return {};
|
|
97
|
-
let a = !1, o = i.map((e) => e.id !== n || e.status === r ? e : (a = !0, {
|
|
98
|
-
...e,
|
|
99
|
-
status: r
|
|
100
|
-
}));
|
|
101
|
-
return a ? { messages: {
|
|
102
|
-
...e.messages,
|
|
103
|
-
[t]: o
|
|
104
|
-
} } : {};
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
deleteSession(t) {
|
|
108
|
-
e((e) => {
|
|
109
|
-
let n = e.sessions.filter((e) => e.id !== t), r = { ...e.messages };
|
|
110
|
-
delete r[t];
|
|
111
|
-
let i = { ...e.awaitingAgentReply };
|
|
112
|
-
return delete i[t], {
|
|
113
|
-
sessions: n,
|
|
114
|
-
messages: r,
|
|
115
|
-
awaitingAgentReply: i,
|
|
116
|
-
activeSessionId: e.activeSessionId === t ? n[0]?.id ?? null : e.activeSessionId
|
|
117
|
-
};
|
|
118
|
-
});
|
|
119
|
-
},
|
|
120
|
-
renameSession(t, n) {
|
|
121
|
-
e((e) => ({ sessions: e.sessions.map((e) => e.id === t ? {
|
|
122
|
-
...e,
|
|
123
|
-
title: n
|
|
124
|
-
} : e) }));
|
|
125
|
-
},
|
|
126
|
-
clearMessages(n) {
|
|
127
|
-
e((e) => {
|
|
128
|
-
let t = { ...e.awaitingAgentReply };
|
|
129
|
-
return delete t[n], {
|
|
130
|
-
messages: {
|
|
27
|
+
})), t;
|
|
28
|
+
},
|
|
29
|
+
setToken(t, n, r) {
|
|
30
|
+
e((e) => ({ tokens: {
|
|
31
|
+
...e.tokens,
|
|
32
|
+
[t]: {
|
|
33
|
+
token: n,
|
|
34
|
+
expiresAt: r
|
|
35
|
+
}
|
|
36
|
+
} }));
|
|
37
|
+
},
|
|
38
|
+
setActiveSession(t) {
|
|
39
|
+
e({ activeSessionId: t });
|
|
40
|
+
},
|
|
41
|
+
addMessage(t, n) {
|
|
42
|
+
e((e) => {
|
|
43
|
+
let r = e.messages[t] ?? [];
|
|
44
|
+
if (r.some((e) => e.id === n.id)) return {};
|
|
45
|
+
let i = [...r, n];
|
|
46
|
+
return {
|
|
47
|
+
sessions: e.sessions.map((e) => e.id !== t || e.title !== "New conversation" || n.role !== "user" ? e : {
|
|
48
|
+
...e,
|
|
49
|
+
title: n.content.slice(0, 50)
|
|
50
|
+
}),
|
|
51
|
+
messages: {
|
|
52
|
+
...e.messages,
|
|
53
|
+
[t]: i
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
appendToLastBot(t, n) {
|
|
59
|
+
e((e) => {
|
|
60
|
+
let r = e.messages[t] ?? [];
|
|
61
|
+
if (r.length === 0) return {};
|
|
62
|
+
let i = r[r.length - 1];
|
|
63
|
+
if (i.role !== "bot") return {};
|
|
64
|
+
let a = [...r.slice(0, -1), {
|
|
65
|
+
...i,
|
|
66
|
+
content: i.content + n
|
|
67
|
+
}];
|
|
68
|
+
return { messages: {
|
|
131
69
|
...e.messages,
|
|
132
|
-
[
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
})
|
|
70
|
+
[t]: a
|
|
71
|
+
} };
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
removeLastBotIfEmpty(t) {
|
|
75
|
+
e((e) => {
|
|
76
|
+
let n = e.messages[t] ?? [];
|
|
77
|
+
if (n.length === 0) return {};
|
|
78
|
+
let r = n[n.length - 1];
|
|
79
|
+
return r.role !== "bot" || r.content !== "" ? {} : { messages: {
|
|
80
|
+
...e.messages,
|
|
81
|
+
[t]: n.slice(0, -1)
|
|
82
|
+
} };
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
setStreaming(t) {
|
|
86
|
+
e({ isStreaming: t });
|
|
87
|
+
},
|
|
88
|
+
setAwaitingAgentReply(t, n) {
|
|
89
|
+
e((e) => {
|
|
90
|
+
let r = { ...e.awaitingAgentReply };
|
|
91
|
+
return n ? r[t] = !0 : delete r[t], { awaitingAgentReply: r };
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
updateMessageStatus(t, n, r) {
|
|
95
|
+
e((e) => {
|
|
96
|
+
let i = e.messages[t];
|
|
97
|
+
if (!i) return {};
|
|
98
|
+
let a = !1, o = i.map((e) => e.id !== n || e.status === r ? e : (a = !0, {
|
|
99
|
+
...e,
|
|
100
|
+
status: r
|
|
101
|
+
}));
|
|
102
|
+
return a ? { messages: {
|
|
103
|
+
...e.messages,
|
|
104
|
+
[t]: o
|
|
105
|
+
} } : {};
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
deleteSession(t) {
|
|
109
|
+
e((e) => {
|
|
110
|
+
let n = e.sessions.filter((e) => e.id !== t), r = { ...e.messages };
|
|
111
|
+
delete r[t];
|
|
112
|
+
let i = { ...e.awaitingAgentReply };
|
|
113
|
+
return delete i[t], {
|
|
114
|
+
sessions: n,
|
|
115
|
+
messages: r,
|
|
116
|
+
awaitingAgentReply: i,
|
|
117
|
+
activeSessionId: e.activeSessionId === t ? n[0]?.id ?? null : e.activeSessionId
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
renameSession(t, n) {
|
|
122
|
+
e((e) => ({ sessions: e.sessions.map((e) => e.id === t ? {
|
|
123
|
+
...e,
|
|
124
|
+
title: n
|
|
125
|
+
} : e) }));
|
|
126
|
+
},
|
|
127
|
+
clearMessages(n) {
|
|
128
|
+
e((e) => {
|
|
129
|
+
let t = { ...e.awaitingAgentReply };
|
|
130
|
+
return delete t[n], {
|
|
131
|
+
messages: {
|
|
132
|
+
...e.messages,
|
|
133
|
+
[n]: []
|
|
134
|
+
},
|
|
135
|
+
awaitingAgentReply: t
|
|
136
|
+
};
|
|
137
|
+
}), t().renameSession(n, "New conversation");
|
|
138
|
+
}
|
|
139
|
+
}), {
|
|
140
|
+
name: t("chatui-chat", i),
|
|
141
|
+
partialize: (e) => ({
|
|
142
|
+
sessions: e.sessions,
|
|
143
|
+
activeSessionId: e.activeSessionId,
|
|
144
|
+
messages: e.messages
|
|
145
|
+
})
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
146
148
|
//#endregion
|
|
147
|
-
export {
|
|
149
|
+
export { i as createChatStore };
|
|
148
150
|
|
|
149
151
|
//# sourceMappingURL=chat-store.js.map
|