@meistrari/chat-nuxt 1.1.3 → 1.2.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 +70 -4
- package/dist/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +58 -19
- package/dist/runtime/components/chat/conversation-list.d.vue.ts +1 -0
- package/dist/runtime/components/chat/conversation-list.vue +22 -2
- package/dist/runtime/components/chat/conversation-list.vue.d.ts +1 -0
- package/dist/runtime/components/chat/message-list.d.vue.ts +1 -0
- package/dist/runtime/components/chat/message-list.vue +23 -2
- package/dist/runtime/components/chat/message-list.vue.d.ts +1 -0
- package/dist/runtime/composables/useChat.d.ts +2 -1
- package/dist/runtime/composables/useChat.js +32 -3
- package/dist/runtime/composables/useChatApi.js +2 -1
- package/dist/runtime/composables/useConversations.d.ts +3 -2
- package/dist/runtime/composables/useConversations.js +99 -11
- package/dist/runtime/composables/useEmbedConfig.d.ts +11 -6
- package/dist/runtime/composables/useEmbedConfig.js +35 -7
- package/dist/runtime/composables/useIDB.js +4 -0
- package/dist/runtime/composables/usePdf.d.ts +840 -1
- package/dist/runtime/composables/usePdf.js +21 -3
- package/dist/runtime/composables/useWorkspaceContextFiles.d.ts +3 -3
- package/dist/runtime/composables/useWorkspaceContextFiles.js +19 -10
- package/dist/runtime/composables/useWorkspaceExternalSkills.d.ts +1 -1
- package/dist/runtime/composables/useWorkspaceExternalSkills.js +5 -6
- package/dist/runtime/composables/useWorkspacePersonalization.d.ts +1 -1
- package/dist/runtime/composables/useWorkspacePersonalization.js +4 -5
- package/dist/runtime/composables/useWorkspaceSettings.js +11 -8
- package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +25 -0
- package/dist/runtime/embed/components/ChatConfigurationModal.vue +373 -0
- package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +25 -0
- package/dist/runtime/embed/components/ChatEmbed.vue +12 -1
- package/dist/runtime/embed/components/ChatEmbedInner.vue +31 -16
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.d.vue.ts +22 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue +133 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationContextFilesTab.vue.d.ts +22 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationCredentialsTab.d.vue.ts +10 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationCredentialsTab.vue +220 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationCredentialsTab.vue.d.ts +10 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationExternalSkillsTab.d.vue.ts +32 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationExternalSkillsTab.vue +222 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationExternalSkillsTab.vue.d.ts +32 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationKnowledgeSourcesTab.d.vue.ts +18 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationKnowledgeSourcesTab.vue +142 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationKnowledgeSourcesTab.vue.d.ts +18 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationPersonalizationTab.d.vue.ts +10 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationPersonalizationTab.vue +37 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationPersonalizationTab.vue.d.ts +10 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationTelaToolsTab.d.vue.ts +20 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationTelaToolsTab.vue +146 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationTelaToolsTab.vue.d.ts +20 -0
- package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +45 -12
- package/dist/runtime/types/chat.d.ts +2 -1
- package/dist/runtime/types/workspace-settings.d.ts +3 -0
- package/dist/runtime/types/workspace-settings.js +0 -0
- package/dist/runtime/utils/conversation-list-skeleton.d.ts +31 -0
- package/dist/runtime/utils/conversation-list-skeleton.js +12 -0
- package/dist/runtime/utils/file.js +42 -42
- package/dist/types.d.mts +2 -0
- package/package.json +23 -27
- package/dist/runtime/composables/PageNavigation.d.ts +0 -13
- package/dist/runtime/composables/PageNavigation.js +0 -18
- /package/dist/runtime/components/{pdf-preview.d.vue.ts → pdf-preview.client.d.vue.ts} +0 -0
- /package/dist/runtime/components/{pdf-preview.vue → pdf-preview.client.vue} +0 -0
- /package/dist/runtime/components/{pdf-preview.vue.d.ts → pdf-preview.client.vue.d.ts} +0 -0
- /package/dist/runtime/components/{pdf-viewer.d.vue.ts → pdf-viewer.client.d.vue.ts} +0 -0
- /package/dist/runtime/components/{pdf-viewer.vue → pdf-viewer.client.vue} +0 -0
- /package/dist/runtime/components/{pdf-viewer.vue.d.ts → pdf-viewer.client.vue.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Nuxt module that adds a complete AI chat interface to your app. Powered by Tela'
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pnpm add @meistrari/chat-nuxt
|
|
8
|
+
pnpm add @meistrari/chat-nuxt
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
### Required: Vue dedupe via pnpm overrides
|
|
@@ -33,8 +33,6 @@ Keep this entry until `@meistrari/tela-build` moves Vue to `peerDependencies`
|
|
|
33
33
|
```typescript
|
|
34
34
|
// nuxt.config.ts
|
|
35
35
|
export default defineNuxtConfig({
|
|
36
|
-
extends: ['@meistrari/tela-build'],
|
|
37
|
-
|
|
38
36
|
modules: [
|
|
39
37
|
'@meistrari/auth-nuxt', // must come before chat-nuxt
|
|
40
38
|
'@meistrari/chat-nuxt',
|
|
@@ -42,6 +40,10 @@ export default defineNuxtConfig({
|
|
|
42
40
|
|
|
43
41
|
ssr: false,
|
|
44
42
|
|
|
43
|
+
chatNuxt: {
|
|
44
|
+
validateConfig: 'warn',
|
|
45
|
+
},
|
|
46
|
+
|
|
45
47
|
telaAuth: {
|
|
46
48
|
apiUrl: process.env.AUTH_API_URL ?? '',
|
|
47
49
|
application: {
|
|
@@ -76,6 +78,9 @@ The module injects these into Nuxt's `runtimeConfig` automatically — just set
|
|
|
76
78
|
| `AGENT_API_URL` | Agent API endpoint |
|
|
77
79
|
| `VAULT_URL` | Vault service URL |
|
|
78
80
|
| `VAULT_S3_BUCKET` | Vault S3 bucket name |
|
|
81
|
+
| `CHAT_APP_SERVICE_KEY` | Service key used when resolving workspace credentials |
|
|
82
|
+
|
|
83
|
+
Set `chatNuxt.validateConfig` to `'warn'` to report missing server config during Nuxt setup, or `'error'` to fail fast in CI or production builds. The check is disabled by default because many deployments inject runtime config after build.
|
|
79
84
|
|
|
80
85
|
### 3. Setup `app.vue`
|
|
81
86
|
|
|
@@ -272,6 +277,49 @@ export default async function resolveWorkspaceSettings(
|
|
|
272
277
|
}
|
|
273
278
|
```
|
|
274
279
|
|
|
280
|
+
## `<ChatConfigurationModal>`
|
|
281
|
+
|
|
282
|
+
A reusable settings modal (system prompt, context files, Tela workstations, Tela skills, external skills, credentials) that any host app can open from any button. By default, the modal uses `useWorkspaceSettings()` to fetch and persist settings. If the host passes the `settings` prop, the host owns workspace settings state and persistence.
|
|
283
|
+
|
|
284
|
+
```vue
|
|
285
|
+
<script setup lang="ts">
|
|
286
|
+
import type { ChatConfigurationTab } from '@meistrari/chat-nuxt/types/workspace-settings'
|
|
287
|
+
|
|
288
|
+
const open = ref(false)
|
|
289
|
+
const initialTab = ref<ChatConfigurationTab>('personalization')
|
|
290
|
+
|
|
291
|
+
function openSettings(tab: ChatConfigurationTab = 'personalization') {
|
|
292
|
+
initialTab.value = tab
|
|
293
|
+
open.value = true
|
|
294
|
+
}
|
|
295
|
+
</script>
|
|
296
|
+
|
|
297
|
+
<template>
|
|
298
|
+
<button @click="openSettings()">
|
|
299
|
+
Configurações
|
|
300
|
+
</button>
|
|
301
|
+
|
|
302
|
+
<ChatConfigurationModal
|
|
303
|
+
v-model:open="open"
|
|
304
|
+
:initial-tab="initialTab"
|
|
305
|
+
/>
|
|
306
|
+
</template>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
| Prop | Type | Default | Description |
|
|
310
|
+
|------|------|---------|-------------|
|
|
311
|
+
| `open` | `boolean` | — | Modal visibility (supports `v-model:open`) |
|
|
312
|
+
| `settings` | `WorkspaceSettings \| null` | `undefined` | Optional host-controlled settings. Omit this prop to let the modal fetch and persist via `useWorkspaceSettings()`; pass `null` or a settings object to make the host own state and persistence |
|
|
313
|
+
| `initialTab` | `ChatConfigurationTab` | `'personalization'` | Tab to open on |
|
|
314
|
+
| `saving` | `boolean` | `false` | Host-controlled spinner/disable while persistence is in flight |
|
|
315
|
+
|
|
316
|
+
| Event | Payload | Description |
|
|
317
|
+
|-------|---------|-------------|
|
|
318
|
+
| `update:open` | `boolean` | Modal open state changed |
|
|
319
|
+
| `save` | `ChatConfigurationSavePayload` | Emitted only when the host passes `settings`; diff of the changed fields plus `onSaved`; host persists (e.g. via `useWorkspaceSettings().updateSettings()`), calls `onSaved` after persistence succeeds, and closes the modal |
|
|
320
|
+
|
|
321
|
+
The `credentials` tab manages its own data via the module's `/api/workspace/credentials` endpoints and is never part of the `save` payload.
|
|
322
|
+
|
|
275
323
|
## What's Included
|
|
276
324
|
|
|
277
325
|
The module auto-registers everything — no manual imports needed:
|
|
@@ -281,10 +329,28 @@ The module auto-registers everything — no manual imports needed:
|
|
|
281
329
|
- **Server routes**: conversation CRUD, message streaming, workspace settings, vault file storage, GitHub integration
|
|
282
330
|
- **Database**: conversations, messages, usage tracking (Drizzle ORM + PostgreSQL)
|
|
283
331
|
|
|
284
|
-
##
|
|
332
|
+
## Package Dependencies
|
|
333
|
+
|
|
334
|
+
Installing `@meistrari/chat-nuxt` installs the runtime packages used by the layer. The host Nuxt app still owns the Nuxt and Vue versions.
|
|
285
335
|
|
|
286
336
|
| Package | Version | Purpose |
|
|
287
337
|
|---------|---------|---------|
|
|
288
338
|
| `@meistrari/auth-nuxt` | `^3.4.1` | Authentication (required) |
|
|
289
339
|
| `@meistrari/tela-build` | `^1.30.0` | UI component library (Nuxt layer) |
|
|
340
|
+
| `@sentry/nuxt` | `^10.0.0` | Client-side error capture used by chat composables |
|
|
341
|
+
| `@iconify/vue` | `^5.0.0` | Icon rendering support |
|
|
342
|
+
| `@vueuse/core` | `^12.8.0` | Runtime composables used by chat UI |
|
|
343
|
+
| `@vueuse/components` | `^12.8.0` | VueUse component peer expected by the shared runtime |
|
|
344
|
+
| `h3` | `^1.15.0` | Server route utilities provided by Nuxt/Nitro |
|
|
290
345
|
| `markstream-vue` | `0.0.3-beta.6` | Markdown streaming renderer |
|
|
346
|
+
| `motion` | `^11.13.0` | Animation component runtime |
|
|
347
|
+
| `nitropack` | `^2.10.0` | Runtime config and task utilities provided by Nuxt/Nitro |
|
|
348
|
+
| `virtua` | `^0.42.0` | Virtualized list runtime expected by shared UI |
|
|
349
|
+
| `vue-i18n` | `^11.0.0` | Internationalization runtime expected by shared UI |
|
|
350
|
+
|
|
351
|
+
Peer dependencies:
|
|
352
|
+
|
|
353
|
+
| Package | Version | Purpose |
|
|
354
|
+
|---------|---------|---------|
|
|
355
|
+
| `nuxt` | `^3.17.0` | Host framework |
|
|
356
|
+
| `vue` | `^3.5.0` | Host Vue runtime |
|
package/dist/module.d.mts
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -37,7 +37,7 @@ function isExpectedResolutionError(error) {
|
|
|
37
37
|
}
|
|
38
38
|
return error.code === "MODULE_NOT_FOUND" || error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED";
|
|
39
39
|
}
|
|
40
|
-
function resolvePackageRoot(resolver, packageName) {
|
|
40
|
+
function resolvePackageRoot$1(resolver, packageName) {
|
|
41
41
|
try {
|
|
42
42
|
return dirname(resolver.resolve(`${packageName}/package.json`));
|
|
43
43
|
} catch (error) {
|
|
@@ -81,7 +81,7 @@ function resolveTelaBuildLayer() {
|
|
|
81
81
|
return localPath;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
const telaBuildRoot = resolvePackageRoot(localRequire, "@meistrari/tela-build");
|
|
84
|
+
const telaBuildRoot = resolvePackageRoot$1(localRequire, "@meistrari/tela-build");
|
|
85
85
|
if (!telaBuildRoot) {
|
|
86
86
|
throw new Error(
|
|
87
87
|
"[@meistrari/chat-nuxt] Cannot resolve @meistrari/tela-build. Ensure it is installed."
|
|
@@ -95,19 +95,19 @@ function resolveRuntimeAliases(telaBuildPath) {
|
|
|
95
95
|
const telaBuildRequire = createRequire(join(telaBuildPath, "package.json"));
|
|
96
96
|
const vueRuntimeEntry = resolvePackageEntry(nuxtRequire, "vue/dist/vue.runtime.esm-bundler.js");
|
|
97
97
|
const aliases = [
|
|
98
|
-
["vue", vueRuntimeEntry ?? resolvePackageRoot(nuxtRequire, "vue")],
|
|
98
|
+
["vue", vueRuntimeEntry ?? resolvePackageRoot$1(nuxtRequire, "vue")],
|
|
99
99
|
["vue/dist/vue.runtime.esm-bundler.js", vueRuntimeEntry],
|
|
100
|
-
["@vue/runtime-core", resolvePackageEntry(nuxtRequire, "@vue/runtime-core/dist/runtime-core.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/runtime-core")],
|
|
101
|
-
["@vue/runtime-dom", resolvePackageEntry(nuxtRequire, "@vue/runtime-dom/dist/runtime-dom.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/runtime-dom")],
|
|
102
|
-
["@vue/reactivity", resolvePackageEntry(nuxtRequire, "@vue/reactivity/dist/reactivity.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/reactivity")],
|
|
103
|
-
["@vue/shared", resolvePackageEntry(nuxtRequire, "@vue/shared/dist/shared.esm-bundler.js") ?? resolvePackageRoot(nuxtRequire, "@vue/shared")],
|
|
104
|
-
["vue-router", resolvePackageEntry(nuxtRequire, "vue-router/dist/vue-router.mjs") ?? resolvePackageRoot(nuxtRequire, "vue-router")],
|
|
105
|
-
["@vueuse/core", resolvePackageRoot(localRequire, "@vueuse/core")],
|
|
106
|
-
["@vueuse/components", resolvePackageRoot(localRequire, "@vueuse/components")],
|
|
107
|
-
["@vueuse/shared", resolvePackageRoot(vueUseCoreRequire, "@vueuse/shared")],
|
|
108
|
-
["reka-ui", resolvePackageRoot(telaBuildRequire, "reka-ui")],
|
|
109
|
-
["radix-vue", resolvePackageRoot(telaBuildRequire, "radix-vue")],
|
|
110
|
-
["@floating-ui/vue", resolvePackageRoot(telaBuildRequire, "@floating-ui/vue")]
|
|
100
|
+
["@vue/runtime-core", resolvePackageEntry(nuxtRequire, "@vue/runtime-core/dist/runtime-core.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/runtime-core")],
|
|
101
|
+
["@vue/runtime-dom", resolvePackageEntry(nuxtRequire, "@vue/runtime-dom/dist/runtime-dom.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/runtime-dom")],
|
|
102
|
+
["@vue/reactivity", resolvePackageEntry(nuxtRequire, "@vue/reactivity/dist/reactivity.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/reactivity")],
|
|
103
|
+
["@vue/shared", resolvePackageEntry(nuxtRequire, "@vue/shared/dist/shared.esm-bundler.js") ?? resolvePackageRoot$1(nuxtRequire, "@vue/shared")],
|
|
104
|
+
["vue-router", resolvePackageEntry(nuxtRequire, "vue-router/dist/vue-router.mjs") ?? resolvePackageRoot$1(nuxtRequire, "vue-router")],
|
|
105
|
+
["@vueuse/core", resolvePackageRoot$1(localRequire, "@vueuse/core")],
|
|
106
|
+
["@vueuse/components", resolvePackageRoot$1(localRequire, "@vueuse/components")],
|
|
107
|
+
["@vueuse/shared", resolvePackageRoot$1(vueUseCoreRequire, "@vueuse/shared")],
|
|
108
|
+
["reka-ui", resolvePackageRoot$1(telaBuildRequire, "reka-ui")],
|
|
109
|
+
["radix-vue", resolvePackageRoot$1(telaBuildRequire, "radix-vue")],
|
|
110
|
+
["@floating-ui/vue", resolvePackageRoot$1(telaBuildRequire, "@floating-ui/vue")]
|
|
111
111
|
];
|
|
112
112
|
const resolvedAliases = aliases.reduce((acc, [name, resolvedPath]) => {
|
|
113
113
|
if (resolvedPath) {
|
|
@@ -148,6 +148,20 @@ function resolveWorkspaceSettingsResolverEntry(srcDir, fallbackResolverBase) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
const _chatNuxtRequire = createRequire(import.meta.url);
|
|
151
|
+
const requiredRuntimeConfigKeys = [
|
|
152
|
+
"databaseUrl",
|
|
153
|
+
"telaApiUrl",
|
|
154
|
+
"telaApiKey",
|
|
155
|
+
"authApiUrl",
|
|
156
|
+
"authApiSecret",
|
|
157
|
+
"agentApiUrl",
|
|
158
|
+
"authJwksUrl",
|
|
159
|
+
"vaultUrl",
|
|
160
|
+
"chatAppServiceKey"
|
|
161
|
+
];
|
|
162
|
+
function resolvePackageRoot(packageName) {
|
|
163
|
+
return dirname(_chatNuxtRequire.resolve(`${packageName}/package.json`));
|
|
164
|
+
}
|
|
151
165
|
function ensureIconifyPackages(consumerRoot) {
|
|
152
166
|
const packages = ["@iconify-json/ph"];
|
|
153
167
|
for (const pkg of packages) {
|
|
@@ -156,14 +170,27 @@ function ensureIconifyPackages(consumerRoot) {
|
|
|
156
170
|
continue;
|
|
157
171
|
}
|
|
158
172
|
try {
|
|
159
|
-
const realPath = dirname(_chatNuxtRequire.resolve(`${pkg}/package.json`));
|
|
160
173
|
mkdirSync(dirname(consumerPath), { recursive: true });
|
|
161
|
-
symlinkSync(
|
|
174
|
+
symlinkSync(resolvePackageRoot(pkg), consumerPath);
|
|
162
175
|
} catch (err) {
|
|
163
|
-
console.warn(`[@meistrari/chat-nuxt] Failed to
|
|
176
|
+
console.warn(`[@meistrari/chat-nuxt] Failed to link ${pkg} into the consumer app. Icons may not render.`, err);
|
|
164
177
|
}
|
|
165
178
|
}
|
|
166
179
|
}
|
|
180
|
+
function validateRuntimeConfig(runtimeConfig, mode) {
|
|
181
|
+
if (!mode) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const missingKeys = requiredRuntimeConfigKeys.filter((key) => !runtimeConfig[key]);
|
|
185
|
+
if (missingKeys.length === 0) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const message = `[@meistrari/chat-nuxt] Missing runtime config: ${missingKeys.join(", ")}. Set these values in runtimeConfig or the matching environment variables before using chat server routes.`;
|
|
189
|
+
if (mode === "error") {
|
|
190
|
+
throw new Error(message);
|
|
191
|
+
}
|
|
192
|
+
console.warn(message);
|
|
193
|
+
}
|
|
167
194
|
function isAuthModuleEntry(entry) {
|
|
168
195
|
if (typeof entry === "string") {
|
|
169
196
|
return entry === "@meistrari/auth-nuxt";
|
|
@@ -181,7 +208,10 @@ const module$1 = defineNuxtModule({
|
|
|
181
208
|
name: "@meistrari/chat-nuxt",
|
|
182
209
|
configKey: "chatNuxt"
|
|
183
210
|
},
|
|
184
|
-
|
|
211
|
+
defaults: {
|
|
212
|
+
validateConfig: false
|
|
213
|
+
},
|
|
214
|
+
setup(options, nuxt) {
|
|
185
215
|
const hasAuthModule = (nuxt.options.modules ?? []).some(isAuthModuleEntry);
|
|
186
216
|
if (!hasAuthModule) {
|
|
187
217
|
throw new Error(
|
|
@@ -204,7 +234,8 @@ const module$1 = defineNuxtModule({
|
|
|
204
234
|
nuxt.options.alias = {
|
|
205
235
|
...nuxt.options.alias,
|
|
206
236
|
"#chat-workspace-settings-resolver": workspaceSettingsResolverEntry,
|
|
207
|
-
"#chat-runtime": runtimeDir
|
|
237
|
+
"#chat-runtime": runtimeDir,
|
|
238
|
+
"@iconify-json/ph": resolvePackageRoot("@iconify-json/ph")
|
|
208
239
|
};
|
|
209
240
|
if (nuxt.options.imports !== false) {
|
|
210
241
|
nuxt.options.imports = nuxt.options.imports || {};
|
|
@@ -226,6 +257,7 @@ const module$1 = defineNuxtModule({
|
|
|
226
257
|
rc.chatAppServiceKey = rc.chatAppServiceKey || process.env.CHAT_APP_SERVICE_KEY || "";
|
|
227
258
|
rc.public = rc.public || {};
|
|
228
259
|
rc.public.agentApiUrl = rc.public.agentApiUrl || process.env.AGENT_API_URL || "";
|
|
260
|
+
validateRuntimeConfig(rc, options.validateConfig ?? false);
|
|
229
261
|
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
230
262
|
addImportsDir(resolver.resolve("./runtime/utils"));
|
|
231
263
|
addComponent({
|
|
@@ -279,6 +311,7 @@ export default globalThis.ELK;
|
|
|
279
311
|
});
|
|
280
312
|
});
|
|
281
313
|
const serverDir = resolver.resolve("./runtime/server");
|
|
314
|
+
const publicFilesDir = resolver.resolve("./runtime/public/files");
|
|
282
315
|
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
283
316
|
nitroConfig.scanDirs = nitroConfig.scanDirs || [];
|
|
284
317
|
nitroConfig.scanDirs.push(serverDir);
|
|
@@ -287,6 +320,12 @@ export default globalThis.ELK;
|
|
|
287
320
|
"#chat-runtime": runtimeDir,
|
|
288
321
|
"#chat-workspace-settings-resolver": workspaceSettingsResolverEntry
|
|
289
322
|
};
|
|
323
|
+
nitroConfig.publicAssets = nitroConfig.publicAssets || [];
|
|
324
|
+
nitroConfig.publicAssets.push({
|
|
325
|
+
dir: publicFilesDir,
|
|
326
|
+
baseURL: "/chat-files",
|
|
327
|
+
maxAge: 60 * 60 * 24 * 7
|
|
328
|
+
});
|
|
290
329
|
});
|
|
291
330
|
}
|
|
292
331
|
});
|
|
@@ -4,6 +4,7 @@ type __VLS_Props = {
|
|
|
4
4
|
conversations: DeepReadonly<Conversation[]>;
|
|
5
5
|
currentId?: string;
|
|
6
6
|
getMemberImage?: (userId: string) => string | undefined;
|
|
7
|
+
loading?: boolean;
|
|
7
8
|
};
|
|
8
9
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
10
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { groupConversationsByDate } from "../../utils/format-time";
|
|
3
3
|
import { exportConversationToMarkdown } from "../../utils/export-conversation";
|
|
4
|
+
import { conversationListSkeletonRows } from "../../utils/conversation-list-skeleton";
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
conversations: { type: null, required: true },
|
|
6
7
|
currentId: { type: String, required: false },
|
|
7
|
-
getMemberImage: { type: Function, required: false }
|
|
8
|
+
getMemberImage: { type: Function, required: false },
|
|
9
|
+
loading: { type: Boolean, required: false }
|
|
8
10
|
});
|
|
9
11
|
const chatApi = useChatApi();
|
|
10
12
|
const { deleteConversation, renameConversation, duplicateConversation } = useConversations();
|
|
13
|
+
const { setConversation } = useChat();
|
|
11
14
|
const router = useRouter();
|
|
12
15
|
const groupedConversations = computed(() => {
|
|
13
16
|
return groupConversationsByDate(props.conversations);
|
|
@@ -25,6 +28,7 @@ async function handleRename(id, title) {
|
|
|
25
28
|
async function handleDuplicate(id) {
|
|
26
29
|
const duplicated = await duplicateConversation(id);
|
|
27
30
|
if (duplicated) {
|
|
31
|
+
setConversation(duplicated);
|
|
28
32
|
router.push(`/${duplicated.id}`);
|
|
29
33
|
}
|
|
30
34
|
}
|
|
@@ -43,7 +47,23 @@ async function handleExport(id) {
|
|
|
43
47
|
|
|
44
48
|
<template>
|
|
45
49
|
<div flex="~ col" gap-16px>
|
|
46
|
-
<div v-if="conversations.length === 0"
|
|
50
|
+
<div v-if="loading && conversations.length === 0" flex="~ col" gap-4px>
|
|
51
|
+
<div h-20px flex items-center pl-12px pr-4px mb-4px>
|
|
52
|
+
<TelaSkeleton rounded-4px bg-neutral-200 style="width: 68px; height: 12px;" />
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div flex="~ col" gap-1px>
|
|
56
|
+
<div
|
|
57
|
+
v-for="row in conversationListSkeletonRows"
|
|
58
|
+
:key="row.id"
|
|
59
|
+
flex items-center h-32px px-8px rounded-8px
|
|
60
|
+
>
|
|
61
|
+
<TelaSkeleton rounded-6px bg-neutral-200 :style="{ width: row.width, height: '13px' }" />
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div v-else-if="conversations.length === 0" p-16px text-center>
|
|
47
67
|
<p text-14px text-neutral-400>
|
|
48
68
|
Nenhuma conversa ainda
|
|
49
69
|
</p>
|
|
@@ -4,6 +4,7 @@ type __VLS_Props = {
|
|
|
4
4
|
conversations: DeepReadonly<Conversation[]>;
|
|
5
5
|
currentId?: string;
|
|
6
6
|
getMemberImage?: (userId: string) => string | undefined;
|
|
7
|
+
loading?: boolean;
|
|
7
8
|
};
|
|
8
9
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
10
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Message } from '../../types/chat.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
messages: readonly Message[];
|
|
4
|
+
loading?: boolean;
|
|
4
5
|
};
|
|
5
6
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
6
7
|
retry: (messageId: string) => any;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
const props = defineProps({
|
|
3
|
-
messages: { type: Array, required: true }
|
|
3
|
+
messages: { type: Array, required: true },
|
|
4
|
+
loading: { type: Boolean, required: false }
|
|
4
5
|
});
|
|
5
6
|
const emit = defineEmits(["retry"]);
|
|
6
7
|
const containerRef = ref(null);
|
|
8
|
+
const skeletonMessages = [
|
|
9
|
+
{ id: "user-1", align: "end", width: "58%", height: "44px" },
|
|
10
|
+
{ id: "assistant-1", align: "start", width: "92%", height: "116px" },
|
|
11
|
+
{ id: "user-2", align: "end", width: "46%", height: "44px" },
|
|
12
|
+
{ id: "assistant-2", align: "start", width: "84%", height: "92px" }
|
|
13
|
+
];
|
|
7
14
|
function scrollToBottom() {
|
|
8
15
|
nextTick(() => {
|
|
9
16
|
if (containerRef.value) {
|
|
@@ -23,7 +30,21 @@ onMounted(scrollToBottom);
|
|
|
23
30
|
pt-88px pb-24px px-24px
|
|
24
31
|
>
|
|
25
32
|
<div max-w-800px mx-auto>
|
|
26
|
-
<div flex="~ col" gap-16px>
|
|
33
|
+
<div v-if="loading" flex="~ col" gap-16px>
|
|
34
|
+
<div
|
|
35
|
+
v-for="message in skeletonMessages"
|
|
36
|
+
:key="message.id"
|
|
37
|
+
flex
|
|
38
|
+
:class="message.align === 'end' ? 'justify-end' : 'justify-start'"
|
|
39
|
+
>
|
|
40
|
+
<TelaSkeleton
|
|
41
|
+
rounded-16px
|
|
42
|
+
:style="{ width: message.width, height: message.height }"
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div v-else flex="~ col" gap-16px>
|
|
27
48
|
<ChatMessageBubble
|
|
28
49
|
v-for="message in messages"
|
|
29
50
|
:key="message.id"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Message } from '../../types/chat.js';
|
|
2
2
|
type __VLS_Props = {
|
|
3
3
|
messages: readonly Message[];
|
|
4
|
+
loading?: boolean;
|
|
4
5
|
};
|
|
5
6
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
6
7
|
retry: (messageId: string) => any;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Message } from '../types/chat.js';
|
|
1
|
+
import type { Conversation, ConversationWithMessages, Message } from '../types/chat.js';
|
|
2
2
|
export declare function useChat(): {
|
|
3
3
|
currentConversation: any;
|
|
4
4
|
messages: any;
|
|
@@ -11,6 +11,7 @@ export declare function useChat(): {
|
|
|
11
11
|
generatingTitle: any;
|
|
12
12
|
animatedTitle: any;
|
|
13
13
|
loadConversation: (id: string) => Promise<void>;
|
|
14
|
+
setConversation: (conv: Conversation | ConversationWithMessages) => void;
|
|
14
15
|
sendMessage: (content: string, files?: {
|
|
15
16
|
url: string;
|
|
16
17
|
name: string;
|
|
@@ -144,8 +144,29 @@ function parseReasoningFromPolling(reasoning) {
|
|
|
144
144
|
}
|
|
145
145
|
export function useChat() {
|
|
146
146
|
const chatApi = useChatApi();
|
|
147
|
-
const
|
|
148
|
-
const
|
|
147
|
+
const embedConfig = useEmbedConfig();
|
|
148
|
+
const { user: authUser, activeOrganization } = useTelaApplicationAuth();
|
|
149
|
+
const chatScope = computed(() => embedConfig?.workspaceId.value.trim() || activeOrganization.value?.id?.trim() || "default");
|
|
150
|
+
const currentConversationBuckets = useState("chat-current-conversation", () => ({}));
|
|
151
|
+
const messageBuckets = useState("chat-messages", () => ({}));
|
|
152
|
+
const currentConversation = computed({
|
|
153
|
+
get: () => currentConversationBuckets.value[chatScope.value] ?? null,
|
|
154
|
+
set: (value) => {
|
|
155
|
+
currentConversationBuckets.value = {
|
|
156
|
+
...currentConversationBuckets.value,
|
|
157
|
+
[chatScope.value]: value
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
const messages = computed({
|
|
162
|
+
get: () => messageBuckets.value[chatScope.value] ?? [],
|
|
163
|
+
set: (value) => {
|
|
164
|
+
messageBuckets.value = {
|
|
165
|
+
...messageBuckets.value,
|
|
166
|
+
[chatScope.value]: value
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
});
|
|
149
170
|
const loading = ref(false);
|
|
150
171
|
const sending = ref(false);
|
|
151
172
|
const error = ref(null);
|
|
@@ -213,6 +234,13 @@ export function useChat() {
|
|
|
213
234
|
interval: 500,
|
|
214
235
|
stopCondition
|
|
215
236
|
});
|
|
237
|
+
const setConversation = (conv) => {
|
|
238
|
+
const conversationMessages = "messages" in conv ? conv.messages : [];
|
|
239
|
+
currentConversation.value = { ...conv, messages: conversationMessages };
|
|
240
|
+
messages.value = conversationMessages;
|
|
241
|
+
titleGenerated.value = false;
|
|
242
|
+
error.value = null;
|
|
243
|
+
};
|
|
216
244
|
const loadConversation = async (id) => {
|
|
217
245
|
loading.value = true;
|
|
218
246
|
error.value = null;
|
|
@@ -258,7 +286,7 @@ export function useChat() {
|
|
|
258
286
|
files: filesWithSource,
|
|
259
287
|
externalUuid: null,
|
|
260
288
|
ttft: null,
|
|
261
|
-
createdBy: null,
|
|
289
|
+
createdBy: authUser.value?.email ?? null,
|
|
262
290
|
createdAt: /* @__PURE__ */ new Date(),
|
|
263
291
|
updatedAt: null
|
|
264
292
|
};
|
|
@@ -500,6 +528,7 @@ export function useChat() {
|
|
|
500
528
|
generatingTitle: readonly(generatingTitle),
|
|
501
529
|
animatedTitle: readonly(animatedTitle),
|
|
502
530
|
loadConversation,
|
|
531
|
+
setConversation,
|
|
503
532
|
sendMessage,
|
|
504
533
|
retryMessage,
|
|
505
534
|
cancelConversation,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export function useChatApi() {
|
|
2
2
|
const embedConfig = useEmbedConfig();
|
|
3
|
+
const { activeOrganization } = useTelaApplicationAuth();
|
|
3
4
|
function buildHeaders(headers) {
|
|
4
5
|
const mergedHeaders = {
|
|
5
6
|
...headers ?? {}
|
|
6
7
|
};
|
|
7
|
-
const workspaceId = embedConfig?.workspaceId.value.trim();
|
|
8
|
+
const workspaceId = embedConfig?.workspaceId.value.trim() || activeOrganization.value?.id?.trim();
|
|
8
9
|
if (workspaceId) {
|
|
9
10
|
mergedHeaders["x-chat-workspace-id"] = workspaceId;
|
|
10
11
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DeepReadonly } from 'vue';
|
|
2
|
-
import type { Conversation, ModelOption } from '../types/chat.js';
|
|
2
|
+
import type { Conversation, ConversationWithMessages, ModelOption } from '../types/chat.js';
|
|
3
|
+
export declare function mergeFetchedConversations(fetchedConversations: Conversation[], localConversations: readonly Conversation[], deletedConversationIds?: readonly string[], locallyMutatedConversationIds?: readonly string[]): Conversation[];
|
|
3
4
|
export declare function useConversations(): {
|
|
4
5
|
conversations: any;
|
|
5
6
|
loading: any;
|
|
@@ -10,6 +11,6 @@ export declare function useConversations(): {
|
|
|
10
11
|
}) => Promise<Conversation | null>;
|
|
11
12
|
deleteConversation: (id: string) => Promise<boolean>;
|
|
12
13
|
renameConversation: (id: string, title: string) => Promise<boolean>;
|
|
13
|
-
duplicateConversation: (id: string) => Promise<
|
|
14
|
+
duplicateConversation: (id: string) => Promise<ConversationWithMessages | null>;
|
|
14
15
|
updateConversationInList: (updated: Conversation | DeepReadonly<Conversation>) => void;
|
|
15
16
|
};
|