@meistrari/chat-nuxt 1.2.3 → 1.3.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.
Files changed (80) hide show
  1. package/README.md +70 -38
  2. package/dist/module.d.mts +24 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +38 -29
  5. package/dist/runtime/components/MeistrariChatEmbed.d.vue.ts +3 -1
  6. package/dist/runtime/components/MeistrariChatEmbed.vue +5 -3
  7. package/dist/runtime/components/MeistrariChatEmbed.vue.d.ts +3 -1
  8. package/dist/runtime/components/chat/thinking-indicator.vue +28 -17
  9. package/dist/runtime/composables/useEmbedConfig.d.ts +5 -0
  10. package/dist/runtime/composables/useEmbedConfig.js +11 -3
  11. package/dist/runtime/embed/components/ChatConfigurationModal.vue +8 -6
  12. package/dist/runtime/embed/components/ChatEmbed.d.vue.ts +3 -1
  13. package/dist/runtime/embed/components/ChatEmbed.vue +13 -12
  14. package/dist/runtime/embed/components/ChatEmbed.vue.d.ts +3 -1
  15. package/dist/runtime/embed/components/ChatEmbedInner.vue +5 -1
  16. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +7 -4
  17. package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +7 -4
  18. package/dist/runtime/server/api/conversations/[id]/usage.get.js +1 -1
  19. package/dist/runtime/server/api/conversations/generate-title.post.js +18 -5
  20. package/dist/runtime/server/api/workspace/usage.get.js +1 -1
  21. package/dist/runtime/server/db/index.d.ts +20 -3
  22. package/dist/runtime/server/db/index.js +87 -10
  23. package/dist/runtime/server/db/schema/_schema.d.ts +2 -0
  24. package/dist/runtime/server/db/schema/_schema.js +3 -0
  25. package/dist/runtime/server/db/schema/conversation-usage.d.ts +1 -1
  26. package/dist/runtime/server/db/schema/conversation-usage.js +3 -2
  27. package/dist/runtime/server/db/schema/conversations.d.ts +1 -1
  28. package/dist/runtime/server/db/schema/conversations.js +3 -2
  29. package/dist/runtime/server/db/schema/external-skills.d.ts +1 -1
  30. package/dist/runtime/server/db/schema/external-skills.js +3 -2
  31. package/dist/runtime/server/db/schema/index.d.ts +1 -0
  32. package/dist/runtime/server/db/schema/index.js +1 -0
  33. package/dist/runtime/server/db/schema/messages.d.ts +1 -1
  34. package/dist/runtime/server/db/schema/messages.js +3 -2
  35. package/dist/runtime/server/db/schema/workspace-settings.d.ts +1 -1
  36. package/dist/runtime/server/db/schema/workspace-settings.js +3 -2
  37. package/dist/runtime/server/plugins/init-db.d.ts +2 -0
  38. package/dist/runtime/server/plugins/init-db.js +11 -0
  39. package/dist/runtime/server/utils/billing.d.ts +26 -4
  40. package/dist/runtime/server/utils/billing.js +52 -13
  41. package/dist/runtime/server/utils/chat-workspace-settings.js +1 -1
  42. package/dist/runtime/utils/chat-loading-messages.d.ts +12 -0
  43. package/dist/runtime/utils/chat-loading-messages.js +40 -0
  44. package/dist/runtime/utils/features.d.ts +6 -0
  45. package/dist/runtime/utils/features.js +15 -0
  46. package/dist/types.d.mts +1 -1
  47. package/drizzle/0000_material_hiroim.sql +28 -0
  48. package/drizzle/0001_famous_scalphunter.sql +1 -0
  49. package/drizzle/0002_gorgeous_big_bertha.sql +1 -0
  50. package/drizzle/0003_massive_forge.sql +2 -0
  51. package/drizzle/0004_absent_black_tom.sql +2 -0
  52. package/drizzle/0005_first_kid_colt.sql +1 -0
  53. package/drizzle/0006_sloppy_mauler.sql +13 -0
  54. package/drizzle/0007_grey_rhino.sql +1 -0
  55. package/drizzle/0008_chilly_blue_blade.sql +27 -0
  56. package/drizzle/0009_cultured_maddog.sql +16 -0
  57. package/drizzle/0010_small_barracuda.sql +1 -0
  58. package/drizzle/0011_fat_lady_mastermind.sql +6 -0
  59. package/drizzle/0012_puzzling_rick_jones.sql +1 -0
  60. package/drizzle/0013_blue_gwen_stacy.sql +11 -0
  61. package/drizzle/meta/0000_snapshot.json +239 -0
  62. package/drizzle/meta/0001_snapshot.json +245 -0
  63. package/drizzle/meta/0002_snapshot.json +251 -0
  64. package/drizzle/meta/0003_snapshot.json +263 -0
  65. package/drizzle/meta/0004_snapshot.json +275 -0
  66. package/drizzle/meta/0005_snapshot.json +281 -0
  67. package/drizzle/meta/0006_snapshot.json +320 -0
  68. package/drizzle/meta/0007_snapshot.json +326 -0
  69. package/drizzle/meta/0008_snapshot.json +539 -0
  70. package/drizzle/meta/0009_snapshot.json +646 -0
  71. package/drizzle/meta/0010_snapshot.json +653 -0
  72. package/drizzle/meta/0011_snapshot.json +689 -0
  73. package/drizzle/meta/0012_snapshot.json +695 -0
  74. package/drizzle/meta/0013_snapshot.json +699 -0
  75. package/drizzle/meta/_journal.json +104 -0
  76. package/package.json +5 -3
  77. package/dist/runtime/server/tasks/send-daily-usage-report.d.ts +0 -2
  78. package/dist/runtime/server/tasks/send-daily-usage-report.js +0 -74
  79. package/dist/runtime/server/utils/auth-api.d.ts +0 -1
  80. package/dist/runtime/server/utils/auth-api.js +0 -27
package/README.md CHANGED
@@ -41,6 +41,16 @@ export default defineNuxtConfig({
41
41
  ssr: false,
42
42
 
43
43
  chatNuxt: {
44
+ telaApiUrl: process.env.TELA_API_URL,
45
+ agentApiUrl: process.env.AGENT_API_URL,
46
+ authApiUrl: process.env.AUTH_API_URL,
47
+ vaultUrl: process.env.VAULT_URL,
48
+ databaseUrl: '', // leave empty to use the bundled pglite database
49
+ features: {
50
+ usage: true,
51
+ generateTitle: false,
52
+ credentials: false,
53
+ },
44
54
  validateConfig: 'warn',
45
55
  },
46
56
 
@@ -53,36 +63,60 @@ export default defineNuxtConfig({
53
63
  dashboardUrl: process.env.AUTH_DASHBOARD_URL ?? '',
54
64
  },
55
65
  },
56
-
57
- // Prevents duplicate Vue instances with pnpm
58
- vite: {
59
- resolve: {
60
- dedupe: ['vue', '@vue/runtime-core', '@vue/reactivity', '@vue/runtime-dom', '@vue/shared', 'reka-ui', 'radix-vue', '@floating-ui/vue', '@vueuse/core'],
61
- },
62
- },
63
66
  })
64
67
  ```
65
68
 
66
- ### 2. Set environment variables
69
+ ### 2. Module options
70
+
71
+ Every `chatNuxt.*` URL accepts the value inline, or you can leave the option empty and set the matching env var. Resolution order: **module option → existing `runtimeConfig` → env var → empty (or pglite default for `databaseUrl`)**.
72
+
73
+ | Option | Required | Env fallback | Description |
74
+ |--------|----------|--------------|-------------|
75
+ | `telaApiUrl` | ✅ Always | `TELA_API_URL` | Tela platform API base URL. |
76
+ | `agentApiUrl` | ✅ Always | `AGENT_API_URL` | Agent runtime base URL the chat streams from. |
77
+ | `authApiUrl` | ✅ Always | `AUTH_API_URL` | Auth API base URL used to verify session tokens. |
78
+ | `vaultUrl` | ✅ Always | `VAULT_URL` | Vault API base URL used for file uploads and resolution. |
79
+ | `databaseUrl` | 🔧 Optional | `DATABASE_URL` | Postgres connection string. Falls back to a file-backed pglite database for local dev. |
80
+ | `features` | 🔧 Optional | — | Feature toggles (see below). `usage` defaults to `true`; the rest default to `false`. |
81
+ | `validateConfig` | 🔧 Optional | — | `false` (default), `'warn'`, or `'error'`. Warns or fails fast when required runtime config is missing for the enabled features. |
82
+
83
+ ### 3. Set environment-only secrets
84
+
85
+ These never appear in module options — set them via `.env`, Infisical, etc. Whether each one is required depends on which features are enabled.
86
+
87
+ | Variable | Required when | Description |
88
+ |----------|---------------|-------------|
89
+ | `AUTH_API_SECRET` | `features.usage: true` | Bearer token chat-nuxt uses to call the Auth API to resolve workspace billing info for the usage feature. |
90
+ | `TELA_API_KEY` | `features.generateTitle: true` | Authenticates the auto-title-generation endpoint, which executes a Meistrari-owned Tela canvas server-side. |
91
+ | `CHAT_APP_SERVICE_KEY` | `features.credentials: true` | Server-to-server `X-Service-Key` used when chat-nuxt resolves workspace credentials (`POST /workspaces/:id/credentials/resolve`). |
92
+ | `VAULT_S3_BUCKET` | 🔧 Optional | SSRF hardening: when set, `/api/vault/permalink-metadata` only follows S3 redirects to `<bucket>.s3*.amazonaws.com`. Recommended in production. |
93
+
94
+ ### 4. Features
67
95
 
68
- The module injects these into Nuxt's `runtimeConfig` automatically just set them in your environment (Infisical, `.env`, etc.):
96
+ `usage` defaults to `true` the chat experience always tracks per-conversation usage out of the box. `generateTitle` and `credentials` default to `false` and layer on optional behavior with their own runtime requirements.
69
97
 
70
- | Variable | Description |
71
- |----------|-------------|
72
- | `DATABASE_URL` | PostgreSQL connection string |
73
- | `TELA_API_URL` | Tela API endpoint |
74
- | `TELA_API_KEY` | Tela API key |
75
- | `AUTH_API_URL` | Auth service URL |
76
- | `AUTH_API_SECRET` | Auth service secret |
77
- | `AUTH_JWKS_URL` | Auth JWKS endpoint |
78
- | `AGENT_API_URL` | Agent API endpoint |
79
- | `VAULT_URL` | Vault service URL |
80
- | `VAULT_S3_BUCKET` | Vault S3 bucket name |
81
- | `CHAT_APP_SERVICE_KEY` | Service key used when resolving workspace credentials |
98
+ | Feature | What it does | Adds to required config |
99
+ |---------|--------------|-------------------------|
100
+ | `usage` | Per-conversation token & cost tracking, daily aggregated reports, and the usage tab UI surface. | `AUTH_API_SECRET` |
101
+ | `generateTitle` | Auto-generates conversation titles from the first user message via a Tela canvas. Without this, titles fall back to a truncated prefix of the message. | `TELA_API_KEY` |
102
+ | `credentials` | Resolves workspace-level credentials and injects them into agent calls so tools can authenticate as the workspace. | `CHAT_APP_SERVICE_KEY` |
82
103
 
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.
104
+ Set `chatNuxt.validateConfig` to `'warn'` to log a warning when keys for an enabled feature are missing, or `'error'` to fail fast at module setup. The check is off by default since some deployments inject runtime config after build.
84
105
 
85
- ### 3. Setup `app.vue`
106
+ ### Local mode (no database)
107
+
108
+ When `databaseUrl` is empty, chat-nuxt runs an embedded [pglite](https://pglite.dev) instance — Postgres compiled to WASM, in-process, file-backed at `<rootDir>/.data/chat-nuxt.db`. Migrations are applied automatically on first boot. Tables live under a dedicated `chat` Postgres schema so the database can be safely shared with the host app's own Drizzle setup.
109
+
110
+ This mode is intended for local development, demos, and single-instance prototypes. Caveats:
111
+
112
+ - **Single-process only.** Multi-instance deployments (e.g. Railway with `replicas > 1`) will see divergent local databases — use real Postgres.
113
+ - **Node runtime only.** Pglite needs a filesystem. It won't run on Cloudflare Workers / Vercel Edge.
114
+ - **Roughly 4 MB of WASM** is loaded at server startup. Postgres consumers don't pay this cost (the import is dynamic).
115
+
116
+ To explicitly opt into pglite at a custom path: `databaseUrl: 'pglite:/var/data/chat.db'` or `databaseUrl: 'file:./mydata/chat.db'`.
117
+
118
+
119
+ ### 5. Setup `app.vue`
86
120
 
87
121
  ```vue
88
122
  <!-- app.vue -->
@@ -94,23 +128,18 @@ Set `chatNuxt.validateConfig` to `'warn'` to report missing server config during
94
128
 
95
129
  `AppStatusToast`, Markstream styles, code block styles, and math delimiter defaults are auto-registered by the module.
96
130
 
97
- ### 4. Use it
131
+ ### 6. Use it
98
132
 
99
133
  ```vue
100
134
  <!-- pages/index.vue -->
101
- <script setup lang="ts">
102
- const { activeOrganization } = useTelaApplicationAuth()
103
- const workspaceId = computed(() => activeOrganization.value?.id ?? null)
104
- </script>
105
-
106
135
  <template>
107
- <div v-if="workspaceId" h-screen>
108
- <MeistrariChatEmbed :workspace-id="workspaceId" />
136
+ <div h-screen>
137
+ <MeistrariChatEmbed />
109
138
  </div>
110
139
  </template>
111
140
  ```
112
141
 
113
- The `workspaceId` is typically the organization ID from `@meistrari/auth-nuxt`.
142
+ The chat workspace is resolved from the authenticated `activeOrganization` provided by `@meistrari/auth-nuxt`.
114
143
 
115
144
  ## Embedding in Your Layout
116
145
 
@@ -126,8 +155,9 @@ The main use case: drop the chat inside your existing app UI.
126
155
  <div style="height: 600px" class="rounded-2xl overflow-hidden border">
127
156
  <MeistrariChatEmbed
128
157
  v-model:conversation-id="activeConversation"
129
- :workspace-id="workspaceId"
130
158
  :hide-sidebar="true"
159
+ :loading-messages="['Buscando contratos', 'Lendo documentos', 'Preparando resposta']"
160
+ loading-messages-mode="ordered"
131
161
  />
132
162
  </div>
133
163
  </main>
@@ -145,13 +175,16 @@ Key props for embedding:
145
175
 
146
176
  | Prop | Type | Default | Description |
147
177
  |------|------|---------|-------------|
148
- | `workspaceId` | `string` | — | **Required.** Workspace/org identifier |
149
178
  | `hideSidebar` | `boolean` | `false` | Hide conversation list sidebar |
150
179
  | `conversationId` | `string \| null` | `null` | Controlled conversation (supports v-model) |
151
180
  | `initialConversationId` | `string \| null` | `null` | Open this conversation on mount |
152
181
  | `workspaceSettings` | `WorkspaceSettings \| null` | `null` | Override settings from host app |
153
182
  | `user` | `ChatActor \| null` | `null` | Override current user display info |
154
183
  | `features` | `Partial<ChatFeatureConfig>` | `{}` | Toggle optional features |
184
+ | `loadingMessages` | `string[] \| null` | Default chat messages | Override pending-response messages |
185
+ | `loadingMessagesMode` | `'ordered' \| 'random' \| null` | `'ordered'` | Show custom messages sequentially or randomly |
186
+
187
+ `loadingMessagesMode` only applies when `loadingMessages` has at least one non-empty message.
155
188
 
156
189
  **Events:**
157
190
 
@@ -208,14 +241,14 @@ const features = computed(() => ({
208
241
  </script>
209
242
 
210
243
  <template>
211
- <MeistrariChatEmbed :workspace-id="workspaceId" :features="features" />
244
+ <MeistrariChatEmbed :features="features" />
212
245
  </template>
213
246
  ```
214
247
 
215
248
  Without PostHog, you can pass features statically:
216
249
 
217
250
  ```vue
218
- <MeistrariChatEmbed :workspace-id="workspaceId" :features="{ showUsageTab: true }" />
251
+ <MeistrariChatEmbed :features="{ showUsageTab: true }" />
219
252
  ```
220
253
 
221
254
  ## Optional: Custom Workspace Settings
@@ -230,7 +263,6 @@ Pass settings directly from your host app:
230
263
 
231
264
  ```vue
232
265
  <MeistrariChatEmbed
233
- :workspace-id="workspaceId"
234
266
  :workspace-settings="{
235
267
  workspaceId: 'ws_123',
236
268
  systemMessage: 'You are a helpful assistant for Acme Corp.',
@@ -326,7 +358,7 @@ Installing `@meistrari/chat-nuxt` installs the runtime packages used by the laye
326
358
 
327
359
  | Package | Version | Purpose |
328
360
  |---------|---------|---------|
329
- | `@meistrari/auth-nuxt` | `^3.4.1` | Authentication (required) |
361
+ | `@meistrari/auth-nuxt` | `3.5.0` | Authentication (required) |
330
362
  | `@meistrari/tela-build` | `^1.30.0` | UI component library (Nuxt layer) |
331
363
  | `@sentry/nuxt` | `^10.0.0` | Client-side error capture used by chat composables |
332
364
  | `@iconify/vue` | `^5.0.0` | Icon rendering support |
package/dist/module.d.mts CHANGED
@@ -1,8 +1,31 @@
1
1
  type ChatNuxtConfigValidationMode = false | 'warn' | 'error';
2
+ interface ChatNuxtFeatures {
3
+ /** Track and report per-conversation token/cost usage. */
4
+ usage?: boolean;
5
+ /** Auto-generate conversation titles from the first user message. Requires `telaApiKey`. */
6
+ generateTitle?: boolean;
7
+ /** Resolve workspace-level credentials and inject them into agent calls. Requires `chatAppServiceKey`. */
8
+ credentials?: boolean;
9
+ }
2
10
  interface ChatNuxtModuleOptions {
11
+ /** Tela platform API base URL (e.g. https://api.tela.com). */
12
+ telaApiUrl?: string;
13
+ /** Agent API base URL — the LLM/agent runtime the chat talks to. */
14
+ agentApiUrl?: string;
15
+ /** Vault API base URL used to upload and resolve user-attached files. */
16
+ vaultUrl?: string;
17
+ /** Postgres connection string. Falls back to a file-backed pglite database for local dev. */
18
+ databaseUrl?: string;
19
+ /** Optional feature toggles. `usage` defaults to `true`; `generateTitle` and `credentials` default to `false`. */
20
+ features?: ChatNuxtFeatures;
21
+ /**
22
+ * Whether to fail fast (`'error'`), warn (`'warn'`), or skip (`false`) when
23
+ * required runtime config keys are unset. Required keys depend on which
24
+ * features are enabled.
25
+ */
3
26
  validateConfig?: ChatNuxtConfigValidationMode;
4
27
  }
5
28
  declare const _default: any;
6
29
 
7
30
  export { _default as default };
8
- export type { ChatNuxtModuleOptions };
31
+ export type { ChatNuxtFeatures, ChatNuxtModuleOptions };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "1.2.3",
4
+ "version": "1.3.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync, mkdirSync, symlinkSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { join, dirname } from 'node:path';
4
- import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
4
+ import { defineNuxtModule, createResolver, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
5
5
 
6
6
  const localRequire = createRequire(import.meta.url);
7
7
  const runtimeAliasPackages = [
@@ -148,17 +148,26 @@ function resolveWorkspaceSettingsResolverEntry(srcDir, fallbackResolverBase) {
148
148
  }
149
149
 
150
150
  const _chatNuxtRequire = createRequire(import.meta.url);
151
- const requiredRuntimeConfigKeys = [
152
- "databaseUrl",
151
+ const DEFAULT_FEATURES = {
152
+ usage: true,
153
+ generateTitle: false,
154
+ credentials: false
155
+ };
156
+ const ALWAYS_REQUIRED_KEYS = [
153
157
  "telaApiUrl",
154
- "telaApiKey",
155
- "authApiUrl",
156
- "authApiSecret",
157
158
  "agentApiUrl",
158
- "authJwksUrl",
159
- "vaultUrl",
160
- "chatAppServiceKey"
159
+ "vaultUrl"
161
160
  ];
161
+ function buildRequiredKeys(features) {
162
+ const required = [...ALWAYS_REQUIRED_KEYS];
163
+ if (features.generateTitle) {
164
+ required.push("telaApiKey");
165
+ }
166
+ if (features.credentials) {
167
+ required.push("chatAppServiceKey");
168
+ }
169
+ return required;
170
+ }
162
171
  function resolvePackageRoot(packageName) {
163
172
  return dirname(_chatNuxtRequire.resolve(`${packageName}/package.json`));
164
173
  }
@@ -177,11 +186,12 @@ function ensureIconifyPackages(consumerRoot) {
177
186
  }
178
187
  }
179
188
  }
180
- function validateRuntimeConfig(runtimeConfig, mode) {
189
+ function validateRuntimeConfig(runtimeConfig, mode, features) {
181
190
  if (!mode) {
182
191
  return;
183
192
  }
184
- const missingKeys = requiredRuntimeConfigKeys.filter((key) => !runtimeConfig[key]);
193
+ const required = buildRequiredKeys(features);
194
+ const missingKeys = required.filter((key) => !runtimeConfig[key]);
185
195
  if (missingKeys.length === 0) {
186
196
  return;
187
197
  }
@@ -246,29 +256,23 @@ const module$1 = defineNuxtModule({
246
256
  const runtimeDirRegExp = new RegExp(runtimeDir.replace(/\\/g, "/").replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
247
257
  nuxt.options.imports.transform.include.push(runtimeDirRegExp);
248
258
  }
259
+ const features = { ...DEFAULT_FEATURES, ...options.features ?? {} };
260
+ const migrationsDir = resolver.resolve("../drizzle");
249
261
  const rc = nuxt.options.runtimeConfig;
250
- rc.databaseUrl = rc.databaseUrl || process.env.DATABASE_URL || "";
251
- rc.telaApiUrl = rc.telaApiUrl || process.env.TELA_API_URL || "";
262
+ rc.databaseUrl = options.databaseUrl || rc.databaseUrl || process.env.DATABASE_URL || `pglite:${join(nuxt.options.rootDir, ".data/chat-nuxt.db")}`;
263
+ rc.telaApiUrl = options.telaApiUrl || rc.telaApiUrl || process.env.TELA_API_URL || "";
252
264
  rc.telaApiKey = rc.telaApiKey || process.env.TELA_API_KEY || "";
253
- rc.authApiUrl = rc.authApiUrl || process.env.AUTH_API_URL || "";
254
- rc.authApiSecret = rc.authApiSecret || process.env.AUTH_API_SECRET || "";
255
- rc.agentApiUrl = rc.agentApiUrl || process.env.AGENT_API_URL || "";
256
- rc.authJwksUrl = rc.authJwksUrl || process.env.AUTH_JWKS_URL || "";
257
- rc.vaultUrl = rc.vaultUrl || process.env.VAULT_URL || "";
265
+ rc.agentApiUrl = options.agentApiUrl || rc.agentApiUrl || process.env.AGENT_API_URL || "";
266
+ rc.vaultUrl = options.vaultUrl || rc.vaultUrl || process.env.VAULT_URL || "";
258
267
  rc.vaultS3Bucket = rc.vaultS3Bucket || process.env.VAULT_S3_BUCKET || "";
259
268
  rc.chatAppServiceKey = rc.chatAppServiceKey || process.env.CHAT_APP_SERVICE_KEY || "";
260
269
  rc.public = rc.public || {};
261
- rc.public.agentApiUrl = rc.public.agentApiUrl || process.env.AGENT_API_URL || "";
262
- validateRuntimeConfig(rc, options.validateConfig ?? false);
263
- nuxt.options.css = nuxt.options.css || [];
264
- nuxt.options.css.push(
265
- _chatNuxtRequire.resolve("markstream-vue/index.css"),
266
- join(runtimeDir, "assets/css/markstream.css"),
267
- join(runtimeDir, "assets/css/code-theme.css")
268
- );
269
- addPlugin({
270
- src: join(runtimeDir, "plugins/markstream")
271
- });
270
+ rc.public.agentApiUrl = rc.agentApiUrl || rc.public.agentApiUrl || process.env.AGENT_API_URL || "";
271
+ rc.public.chatNuxtFeatures = features;
272
+ rc.chatNuxt = rc.chatNuxt || {};
273
+ rc.chatNuxt.migrationsDir = rc.chatNuxt.migrationsDir || migrationsDir;
274
+ rc.chatNuxt.features = features;
275
+ validateRuntimeConfig(rc, options.validateConfig ?? false, features);
272
276
  addImportsDir(join(runtimeDir, "composables"));
273
277
  addImportsDir(join(runtimeDir, "utils"));
274
278
  addComponent({
@@ -324,6 +328,7 @@ export default globalThis.ELK;
324
328
  const serverDir = join(runtimeDir, "server");
325
329
  const publicAssetsDir = join(runtimeDir, "public/assets");
326
330
  const publicFilesDir = join(runtimeDir, "public/files");
331
+ const dbInitPluginPath = join(runtimeDir, "server/plugins/init-db");
327
332
  nuxt.hook("nitro:config", (nitroConfig) => {
328
333
  nitroConfig.scanDirs = nitroConfig.scanDirs || [];
329
334
  nitroConfig.scanDirs.push(serverDir);
@@ -332,6 +337,10 @@ export default globalThis.ELK;
332
337
  "#chat-runtime": runtimeDir,
333
338
  "#chat-workspace-settings-resolver": workspaceSettingsResolverEntry
334
339
  };
340
+ nitroConfig.plugins = nitroConfig.plugins || [];
341
+ if (!nitroConfig.plugins.includes(dbInitPluginPath)) {
342
+ nitroConfig.plugins.push(dbInitPluginPath);
343
+ }
335
344
  nitroConfig.publicAssets = nitroConfig.publicAssets || [];
336
345
  nitroConfig.publicAssets.push({
337
346
  dir: publicAssetsDir,
@@ -1,14 +1,16 @@
1
1
  import type { DeepReadonly } from 'vue';
2
2
  import type { WorkspaceSettings } from '../composables/useWorkspaceSettings.js';
3
+ import type { ChatLoadingMessageMode } from '../utils/chat-loading-messages.js';
3
4
  import type { ChatActor, ChatFeatureConfig } from '../utils/tela-chat.js';
4
5
  type __VLS_Props = {
5
- workspaceId: string;
6
6
  workspaceSettings?: DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null;
7
7
  user?: ChatActor | null;
8
8
  conversationId?: string | null;
9
9
  initialConversationId?: string | null;
10
10
  hideSidebar?: boolean;
11
11
  features?: Partial<ChatFeatureConfig>;
12
+ loadingMessages?: readonly string[] | null;
13
+ loadingMessagesMode?: ChatLoadingMessageMode | null;
12
14
  };
13
15
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
14
16
  "update:conversationId": (value: string | null) => any;
@@ -1,25 +1,27 @@
1
1
  <script setup>
2
2
  defineProps({
3
- workspaceId: { type: String, required: true },
4
3
  workspaceSettings: { type: null, required: false },
5
4
  user: { type: [Object, null], required: false },
6
5
  conversationId: { type: [String, null], required: false },
7
6
  initialConversationId: { type: [String, null], required: false },
8
7
  hideSidebar: { type: Boolean, required: false },
9
- features: { type: Object, required: false }
8
+ features: { type: Object, required: false },
9
+ loadingMessages: { type: [Array, null], required: false },
10
+ loadingMessagesMode: { type: [String, null], required: false }
10
11
  });
11
12
  defineEmits(["update:conversationId"]);
12
13
  </script>
13
14
 
14
15
  <template>
15
16
  <ChatEmbed
16
- :workspace-id="workspaceId"
17
17
  :workspace-settings="workspaceSettings"
18
18
  :user="user"
19
19
  :conversation-id="conversationId"
20
20
  :initial-conversation-id="initialConversationId"
21
21
  :hide-sidebar="hideSidebar"
22
22
  :features="features"
23
+ :loading-messages="loadingMessages"
24
+ :loading-messages-mode="loadingMessagesMode"
23
25
  @update:conversation-id="$emit('update:conversationId', $event)"
24
26
  />
25
27
  </template>
@@ -1,14 +1,16 @@
1
1
  import type { DeepReadonly } from 'vue';
2
2
  import type { WorkspaceSettings } from '../composables/useWorkspaceSettings.js';
3
+ import type { ChatLoadingMessageMode } from '../utils/chat-loading-messages.js';
3
4
  import type { ChatActor, ChatFeatureConfig } from '../utils/tela-chat.js';
4
5
  type __VLS_Props = {
5
- workspaceId: string;
6
6
  workspaceSettings?: DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null;
7
7
  user?: ChatActor | null;
8
8
  conversationId?: string | null;
9
9
  initialConversationId?: string | null;
10
10
  hideSidebar?: boolean;
11
11
  features?: Partial<ChatFeatureConfig>;
12
+ loadingMessages?: readonly string[] | null;
13
+ loadingMessagesMode?: ChatLoadingMessageMode | null;
12
14
  };
13
15
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
14
16
  "update:conversationId": (value: string | null) => any;
@@ -1,34 +1,45 @@
1
1
  <script setup>
2
- const loadingTexts = [
3
- "Analisando sua pergunta",
4
- "Processando informa\xE7\xF5es",
5
- "Consultando base de conhecimento",
6
- "Organizando ideias",
7
- "Elaborando resposta",
8
- "Refinando detalhes",
9
- "Verificando contexto",
10
- "Conectando conceitos",
11
- "Preparando resultado",
12
- "Quase l\xE1",
13
- "Finalizando an\xE1lise",
14
- "Estruturando resposta"
15
- ];
2
+ import {
3
+ DEFAULT_CHAT_LOADING_MESSAGES,
4
+ getNextChatLoadingMessageIndex,
5
+ resolveChatLoadingMessages
6
+ } from "../../utils/chat-loading-messages";
7
+ const embedConfig = useEmbedConfig();
16
8
  const currentTextIndex = ref(0);
17
9
  const dotCount = ref(0);
10
+ const loadingState = computed(() => resolveChatLoadingMessages({
11
+ loadingMessages: embedConfig?.loadingMessages.value,
12
+ loadingMessagesMode: embedConfig?.loadingMessagesMode.value
13
+ }));
18
14
  const currentText = computed(() => {
19
- const base = loadingTexts[currentTextIndex.value];
15
+ const base = loadingState.value.messages[currentTextIndex.value] ?? loadingState.value.messages[0] ?? DEFAULT_CHAT_LOADING_MESSAGES[0];
20
16
  const dots = ".".repeat(dotCount.value);
21
17
  return `${base}${dots}`;
22
18
  });
19
+ watch(loadingState, (state) => {
20
+ if (currentTextIndex.value >= state.messages.length) {
21
+ currentTextIndex.value = 0;
22
+ dotCount.value = 0;
23
+ }
24
+ });
23
25
  let dotInterval;
24
26
  let textInterval;
27
+ function advanceText() {
28
+ currentTextIndex.value = getNextChatLoadingMessageIndex(
29
+ loadingState.value.mode,
30
+ loadingState.value.messages.length,
31
+ currentTextIndex.value
32
+ );
33
+ dotCount.value = 0;
34
+ }
25
35
  onMounted(() => {
36
+ if (loadingState.value.mode === "random")
37
+ advanceText();
26
38
  dotInterval = setInterval(() => {
27
39
  dotCount.value = (dotCount.value + 1) % 4;
28
40
  }, 500);
29
41
  textInterval = setInterval(() => {
30
- currentTextIndex.value = (currentTextIndex.value + 1) % loadingTexts.length;
31
- dotCount.value = 0;
42
+ advanceText();
32
43
  }, 7e3);
33
44
  });
34
45
  onUnmounted(() => {
@@ -1,14 +1,19 @@
1
1
  import type { DeepReadonly, MaybeRefOrGetter, Ref } from 'vue';
2
+ import type { ChatLoadingMessageMode } from '../utils/chat-loading-messages.js';
2
3
  import type { WorkspaceSettings } from './useWorkspaceSettings.js';
3
4
  export type EmbedConfig = {
4
5
  workspaceId: Ref<string>;
5
6
  workspaceSettings: Ref<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
6
7
  hideSidebar: Ref<boolean>;
8
+ loadingMessages: Ref<readonly string[] | null | undefined>;
9
+ loadingMessagesMode: Ref<ChatLoadingMessageMode | null | undefined>;
7
10
  };
8
11
  type EmbedConfigInput = {
9
12
  workspaceId: MaybeRefOrGetter<string>;
10
13
  workspaceSettings?: MaybeRefOrGetter<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
11
14
  hideSidebar?: MaybeRefOrGetter<boolean | undefined>;
15
+ loadingMessages?: MaybeRefOrGetter<readonly string[] | null | undefined>;
16
+ loadingMessagesMode?: MaybeRefOrGetter<ChatLoadingMessageMode | null | undefined>;
12
17
  };
13
18
  type ProvideEmbedConfigOptions = {
14
19
  syncGlobal?: boolean;
@@ -4,14 +4,18 @@ function useGlobalEmbedConfigState() {
4
4
  provided: useState("chat-embed-config-provided", () => false),
5
5
  workspaceId: useState("chat-embed-config-workspace-id", () => ""),
6
6
  workspaceSettings: useState("chat-embed-config-workspace-settings", () => void 0),
7
- hideSidebar: useState("chat-embed-config-hide-sidebar", () => void 0)
7
+ hideSidebar: useState("chat-embed-config-hide-sidebar", () => void 0),
8
+ loadingMessages: useState("chat-embed-config-loading-messages", () => void 0),
9
+ loadingMessagesMode: useState("chat-embed-config-loading-messages-mode", () => void 0)
8
10
  };
9
11
  }
10
12
  export function provideEmbedConfig(input, options = {}) {
11
13
  const config = {
12
14
  workspaceId: computed(() => toValue(input.workspaceId)),
13
15
  workspaceSettings: computed(() => toValue(input.workspaceSettings)),
14
- hideSidebar: computed(() => toValue(input.hideSidebar) ?? true)
16
+ hideSidebar: computed(() => toValue(input.hideSidebar) ?? true),
17
+ loadingMessages: computed(() => toValue(input.loadingMessages)),
18
+ loadingMessagesMode: computed(() => toValue(input.loadingMessagesMode))
15
19
  };
16
20
  const globalConfig = useGlobalEmbedConfigState();
17
21
  if (options.syncGlobal ?? true) {
@@ -20,6 +24,8 @@ export function provideEmbedConfig(input, options = {}) {
20
24
  globalConfig.workspaceId.value = config.workspaceId.value;
21
25
  globalConfig.workspaceSettings.value = config.workspaceSettings.value;
22
26
  globalConfig.hideSidebar.value = config.hideSidebar.value;
27
+ globalConfig.loadingMessages.value = config.loadingMessages.value;
28
+ globalConfig.loadingMessagesMode.value = config.loadingMessagesMode.value;
23
29
  });
24
30
  }
25
31
  provide(EMBED_CONFIG_KEY, config);
@@ -34,6 +40,8 @@ export function useEmbedConfig() {
34
40
  return {
35
41
  workspaceId: computed(() => globalConfig.workspaceId.value),
36
42
  workspaceSettings: computed(() => globalConfig.workspaceSettings.value),
37
- hideSidebar: computed(() => globalConfig.hideSidebar.value ?? true)
43
+ hideSidebar: computed(() => globalConfig.hideSidebar.value ?? true),
44
+ loadingMessages: computed(() => globalConfig.loadingMessages.value),
45
+ loadingMessagesMode: computed(() => globalConfig.loadingMessagesMode.value)
38
46
  };
39
47
  }
@@ -18,6 +18,7 @@ const {
18
18
  fetchSettings,
19
19
  updateSettings
20
20
  } = useWorkspaceSettings();
21
+ const features = useChatNuxtFeatures();
21
22
  const searchQuery = ref("");
22
23
  const activeTab = ref(props.initialTab);
23
24
  const showDiscardConfirm = ref(false);
@@ -34,14 +35,14 @@ const telaTools = useWorkspaceTelaTools(canvas, projects, searchQuery, currentCa
34
35
  const knowledgeSources = useWorkspaceKnowledgeSources(workstations, canvas, projects, searchQuery, currentKnowledgeSources);
35
36
  const contextFiles = useWorkspaceContextFiles(searchQuery, currentContextFiles);
36
37
  const externalSkills = useWorkspaceExternalSkills(currentExternalSkills, searchQuery);
37
- const settingsTabs = [
38
+ const settingsTabs = computed(() => [
38
39
  { value: "personalization", label: "Personaliza\xE7\xE3o", icon: "i-ph-sliders" },
39
40
  { value: "context-files", label: "Arquivos de contexto", icon: "i-ph-file-text" },
40
41
  { value: "knowledge-sources", label: "Tela workstations", icon: "i-ph-database" },
41
42
  { value: "tela-tools", label: "Skills Tela", icon: "i-ph-puzzle-piece" },
42
43
  { value: "external-skills", label: "Skills Externas", icon: "i-ph-wrench" },
43
- { value: "credentials", label: "Credenciais", icon: "i-ph-password-bold" }
44
- ];
44
+ ...features.credentials ? [{ value: "credentials", label: "Credenciais", icon: "i-ph-password-bold" }] : []
45
+ ]);
45
46
  const hasChanges = computed(
46
47
  () => personalization.hasChanges.value || contextFiles.hasChanges.value || telaTools.hasChanges.value || knowledgeSources.hasChanges.value || externalSkills.hasChanges.value
47
48
  );
@@ -84,9 +85,10 @@ function initializeConfigurationState() {
84
85
  externalSkills.initialize();
85
86
  }
86
87
  function activateTab(tab) {
87
- activeTab.value = tab;
88
+ const isVisible = settingsTabs.value.some((t) => t.value === tab);
89
+ activeTab.value = isVisible ? tab : "personalization";
88
90
  searchQuery.value = "";
89
- if (tab === "external-skills")
91
+ if (activeTab.value === "external-skills")
90
92
  void externalSkills.fetchGitHubConnection();
91
93
  }
92
94
  async function loadSettings() {
@@ -336,7 +338,7 @@ function cancelDiscard() {
336
338
  />
337
339
 
338
340
  <ChatConfigurationCredentialsTab
339
- v-else-if="activeTab === 'credentials'"
341
+ v-else-if="activeTab === 'credentials' && features.credentials"
340
342
  :search-query="searchQuery"
341
343
  @update:search-query="searchQuery = $event"
342
344
  />
@@ -1,15 +1,17 @@
1
1
  import type { DeepReadonly } from 'vue';
2
2
  import type { WorkspaceSettings } from '../../composables/useWorkspaceSettings.js';
3
+ import type { ChatLoadingMessageMode } from '../../utils/chat-loading-messages.js';
3
4
  import type { ChatActor, ChatFeatureConfig } from '../../utils/tela-chat.js';
4
5
  type EmbedUser = ChatActor;
5
6
  type __VLS_Props = {
6
- workspaceId: string;
7
7
  workspaceSettings?: DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null;
8
8
  conversationId?: string | null;
9
9
  initialConversationId?: string | null;
10
10
  hideSidebar?: boolean;
11
11
  user?: EmbedUser | null;
12
12
  features?: Partial<ChatFeatureConfig>;
13
+ loadingMessages?: readonly string[] | null;
14
+ loadingMessagesMode?: ChatLoadingMessageMode | null;
13
15
  };
14
16
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
15
17
  "update:conversationId": (value: string | null) => any;