@meistrari/chat-nuxt 1.6.0 → 1.8.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 +25 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +23 -2
- package/dist/runtime/components/MeistrariChatEmbed.vue +8 -1
- package/dist/runtime/components/chat/bash-widget.vue +3 -3
- package/dist/runtime/components/chat/doc-sdk-widget.vue +3 -3
- package/dist/runtime/components/chat/files-view.vue +3 -64
- package/dist/runtime/components/chat/generic-tool-widget.vue +3 -3
- package/dist/runtime/components/chat/grep-widget.vue +3 -3
- package/dist/runtime/components/chat/message-bubble.vue +1 -1
- package/dist/runtime/components/chat/message-input.d.vue.ts +2 -0
- package/dist/runtime/components/chat/message-input.vue +21 -5
- package/dist/runtime/components/chat/message-input.vue.d.ts +2 -0
- package/dist/runtime/components/chat/message-list.vue +1 -1
- package/dist/runtime/components/chat/mobile/files/file-preview.d.vue.ts +11 -0
- package/dist/runtime/components/chat/mobile/files/file-preview.vue +112 -0
- package/dist/runtime/components/chat/mobile/files/file-preview.vue.d.ts +11 -0
- package/dist/runtime/components/chat/mobile/files/files-view.d.vue.ts +10 -0
- package/dist/runtime/components/chat/mobile/files/files-view.vue +384 -0
- package/dist/runtime/components/chat/mobile/files/files-view.vue.d.ts +10 -0
- package/dist/runtime/components/chat/mobile/home/header.d.vue.ts +19 -0
- package/dist/runtime/components/chat/mobile/home/header.vue +55 -0
- package/dist/runtime/components/chat/mobile/home/header.vue.d.ts +19 -0
- package/dist/runtime/components/chat/mobile/home/recents.d.vue.ts +14 -0
- package/dist/runtime/components/chat/mobile/home/recents.vue +48 -0
- package/dist/runtime/components/chat/mobile/home/recents.vue.d.ts +14 -0
- package/dist/runtime/components/chat/mobile/home/suggestions.d.vue.ts +7 -0
- package/dist/runtime/components/chat/mobile/home/suggestions.vue +31 -0
- package/dist/runtime/components/chat/mobile/home/suggestions.vue.d.ts +7 -0
- package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.d.vue.ts +21 -0
- package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue +48 -0
- package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue.d.ts +21 -0
- package/dist/runtime/components/chat/mobile/primitives/info-bubble.d.vue.ts +6 -0
- package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue +50 -0
- package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue.d.ts +6 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +22 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.vue +143 -0
- package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +22 -0
- package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +40 -0
- package/dist/runtime/components/chat/mobile/shell/header.vue +139 -0
- package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +40 -0
- package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.d.vue.ts +14 -0
- package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue +47 -0
- package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue.d.ts +14 -0
- package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +122 -0
- package/dist/runtime/components/chat/mobile/shell/shell.vue +283 -0
- package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +122 -0
- package/dist/runtime/components/chat/mobile/shell/workspace-sheet.d.vue.ts +19 -0
- package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue +51 -0
- package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue.d.ts +19 -0
- package/dist/runtime/components/chat/mobile/usage/usage-view.d.vue.ts +8 -0
- package/dist/runtime/components/chat/mobile/usage/usage-view.vue +243 -0
- package/dist/runtime/components/chat/mobile/usage/usage-view.vue.d.ts +8 -0
- package/dist/runtime/components/chat/model-selector.vue +6 -0
- package/dist/runtime/components/chat/read-widget.vue +3 -3
- package/dist/runtime/components/chat/reasoning-step-item.vue +1 -1
- package/dist/runtime/components/chat/reasoning-steps.vue +1 -1
- package/dist/runtime/components/chat/reasoning-thought.vue +2 -3
- package/dist/runtime/components/chat/tela-skill-widget.vue +3 -3
- package/dist/runtime/components/chat/thinking-widget.vue +3 -3
- package/dist/runtime/components/chat/usage-view.vue +20 -101
- package/dist/runtime/components/chat/web-search-widget.vue +3 -3
- package/dist/runtime/components/chat/workstation-query-widget.vue +6 -6
- package/dist/runtime/components/chat/write-widget.vue +3 -3
- package/dist/runtime/composables/useChat.js +2 -1
- package/dist/runtime/composables/useChatApi.js +4 -0
- package/dist/runtime/composables/useConversationFileDownloads.d.ts +6 -0
- package/dist/runtime/composables/useConversationFileDownloads.js +50 -0
- package/dist/runtime/composables/useConversationUsage.d.ts +13 -0
- package/dist/runtime/composables/useConversationUsage.js +121 -0
- package/dist/runtime/composables/useConversations.js +3 -1
- package/dist/runtime/composables/useEmbedConfig.d.ts +4 -0
- package/dist/runtime/composables/useEmbedConfig.js +9 -0
- package/dist/runtime/composables/useMentionAutocomplete.d.ts +6 -0
- package/dist/runtime/composables/useMentionAutocomplete.js +71 -23
- package/dist/runtime/composables/useWorkspaceSwitchModal.d.ts +5 -0
- package/dist/runtime/composables/useWorkspaceSwitchModal.js +14 -0
- package/dist/runtime/embed/components/ChatConfigurationModal.vue +119 -13
- package/dist/runtime/embed/components/ChatEmbed.vue +9 -2
- package/dist/runtime/embed/components/ChatEmbedInner.d.vue.ts +2 -0
- package/dist/runtime/embed/components/ChatEmbedInner.vue +201 -11
- package/dist/runtime/embed/components/ChatEmbedInner.vue.d.ts +2 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +34 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue +95 -0
- package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +34 -0
- package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +1 -0
- package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +3 -1
- package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +29 -5
- package/dist/runtime/server/api/conversations/index.post.js +1 -0
- package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
- package/dist/runtime/server/db/schema/conversations.js +1 -0
- package/dist/runtime/server/utils/chat-context.d.ts +2 -0
- package/dist/runtime/server/utils/chat-context.js +15 -1
- package/dist/runtime/server/utils/conversation-message-sync.d.ts +8 -2
- package/dist/runtime/server/utils/conversation-message-sync.js +52 -6
- package/dist/runtime/server/utils/conversation-scope.js +2 -1
- package/dist/runtime/types/chat-auth.d.ts +9 -8
- package/dist/runtime/types/embed.d.ts +21 -0
- package/dist/runtime/utils/breakpoints.d.ts +1 -0
- package/dist/runtime/utils/breakpoints.js +1 -0
- package/dist/runtime/utils/tela-chat.d.ts +3 -1
- package/dist/runtime/utils/tela-chat.js +11 -2
- package/drizzle/0015_illegal_blindfold.sql +1 -0
- package/drizzle/meta/0015_snapshot.json +738 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -161,11 +161,18 @@ The chat workspace is resolved from the authenticated `activeOrganization` provi
|
|
|
161
161
|
<MeistrariChatEmbed tela-agent-id="agent_123" />
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
`conversationScope` works in both runtimes. In default chat mode, it isolates conversations inside the authenticated workspace without requiring a Tela agent:
|
|
165
|
+
|
|
166
|
+
```vue
|
|
167
|
+
<MeistrariChatEmbed :conversation-scope="`user:${userId}`" />
|
|
168
|
+
```
|
|
169
|
+
|
|
164
170
|
Host apps can also provide Tela agent inputs directly. When `telaAgentInputs` is non-null, the chat sends those inputs with each agent run and does not render the agent variables panel.
|
|
165
171
|
|
|
166
172
|
```vue
|
|
167
173
|
<MeistrariChatEmbed
|
|
168
174
|
tela-agent-id="agent_123"
|
|
175
|
+
:conversation-scope="`customer:${customerId}`"
|
|
169
176
|
:tela-agent-inputs="[
|
|
170
177
|
{ type: 'text', name: 'customer_id', content: customerId },
|
|
171
178
|
]"
|
|
@@ -203,6 +210,7 @@ Key props for embedding:
|
|
|
203
210
|
- **`:hide-sidebar="true"`** — removes the conversation list, keeps just the chat
|
|
204
211
|
- **`v-model:conversation-id`** — sync the active conversation with your app's state
|
|
205
212
|
- **`:initial-conversation-id`** — open a specific conversation on mount
|
|
213
|
+
- **`conversation-scope`** — isolate conversation history inside the same workspace or Tela agent
|
|
206
214
|
|
|
207
215
|
## `<MeistrariChatEmbed>` Props
|
|
208
216
|
|
|
@@ -211,6 +219,7 @@ Key props for embedding:
|
|
|
211
219
|
| `hideSidebar` | `boolean` | `false` | Hide conversation list sidebar |
|
|
212
220
|
| `conversationId` | `string \| null` | `null` | Controlled conversation (supports v-model) |
|
|
213
221
|
| `initialConversationId` | `string \| null` | `null` | Open this conversation on mount |
|
|
222
|
+
| `conversationScope` | `string \| null` | `null` | Isolate conversation history by technical scope within the current workspace and runtime |
|
|
214
223
|
| `telaAgentId` | `string` | — | Use Tela agent mode for this embed |
|
|
215
224
|
| `telaAgentInputs` | `TelaAgentExecutionInput[] \| null` | `undefined` | Inputs sent with each Tela agent run; hides the variables panel when non-null |
|
|
216
225
|
| `workspaceSettings` | `WorkspaceSettings \| null` | `null` | Override settings from host app |
|
|
@@ -221,6 +230,22 @@ Key props for embedding:
|
|
|
221
230
|
|
|
222
231
|
`loadingMessagesMode` only applies when `loadingMessages` has at least one non-empty message.
|
|
223
232
|
|
|
233
|
+
Use `conversationScope` when you need multiple chat surfaces to share the same authenticated workspace but keep separate histories. This is useful for per-user inboxes, record detail pages, customer workspaces, template previews, workflow steps, or any app route where the same chat should only show conversations for that route's domain object. The scope is applied together with the workspace and, when present, the `telaAgentId`.
|
|
234
|
+
|
|
235
|
+
Scoped embeds only see conversations created with the same scope. Unscoped embeds only see unscoped conversations, so adding a scope will not mix with existing workspace-level history. Scope values are technical identifiers: they are trimmed, blank strings become `null`, and server requests accept 1-200 characters from `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `/`, and `-`.
|
|
236
|
+
|
|
237
|
+
```vue
|
|
238
|
+
<!-- Default chat mode: workspace + scope -->
|
|
239
|
+
<MeistrariChatEmbed :conversation-scope="`user:${userId}`" />
|
|
240
|
+
<MeistrariChatEmbed :conversation-scope="`customer:${customerId}`" />
|
|
241
|
+
|
|
242
|
+
<!-- Tela agent mode: workspace + telaAgentId + scope -->
|
|
243
|
+
<MeistrariChatEmbed
|
|
244
|
+
tela-agent-id="agent_123"
|
|
245
|
+
:conversation-scope="`template:${templateId}`"
|
|
246
|
+
/>
|
|
247
|
+
```
|
|
248
|
+
|
|
224
249
|
**Events:**
|
|
225
250
|
|
|
226
251
|
| Event | Payload | Description |
|
package/dist/module.json
CHANGED
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, addTemplate, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
|
|
4
|
+
import { defineNuxtModule, createResolver, addTemplate, addTypeTemplate, addPlugin, addImportsDir, addComponent, addComponentsDir } from 'nuxt/kit';
|
|
5
5
|
|
|
6
6
|
const agentEnvironmentResolverCandidates = [
|
|
7
7
|
"server/chat/resolve-agent-environment.ts",
|
|
@@ -179,6 +179,7 @@ const DEFAULT_FEATURES = {
|
|
|
179
179
|
generateTitle: false,
|
|
180
180
|
credentials: false
|
|
181
181
|
};
|
|
182
|
+
const VUE_COMPONENT_EXTENSIONS = [".vue"];
|
|
182
183
|
function resolveChatAuthMode(runtimeConfig) {
|
|
183
184
|
const publicConfig = runtimeConfig.public;
|
|
184
185
|
const telaAuth = publicConfig?.telaAuth;
|
|
@@ -195,6 +196,7 @@ export function useChatAuth() {
|
|
|
195
196
|
return {
|
|
196
197
|
user: auth.user,
|
|
197
198
|
activeOrganization: auth.activeOrganization,
|
|
199
|
+
logout: auth.logout,
|
|
198
200
|
getToken: auth.getToken,
|
|
199
201
|
getAvailableOrganizations: auth.getAvailableOrganizations,
|
|
200
202
|
switchOrganization: auth.switchOrganization,
|
|
@@ -240,6 +242,7 @@ export function useChatAuth() {
|
|
|
240
242
|
return {
|
|
241
243
|
user: session.user,
|
|
242
244
|
activeOrganization: organization.activeOrganization,
|
|
245
|
+
logout: session.signOut,
|
|
243
246
|
getToken: session.getToken,
|
|
244
247
|
getAvailableOrganizations: organization.listOrganizations,
|
|
245
248
|
switchOrganization: organization.setActiveOrganization,
|
|
@@ -342,6 +345,22 @@ const module$1 = defineNuxtModule({
|
|
|
342
345
|
filename: "chat-nuxt/auth.mjs",
|
|
343
346
|
getContents: () => buildChatAuthTemplate(chatAuthMode)
|
|
344
347
|
});
|
|
348
|
+
const chatAuthTypeTemplate = addTypeTemplate({
|
|
349
|
+
filename: "chat-nuxt/chat-auth.d.ts",
|
|
350
|
+
src: join(runtimeDir, "types/chat-auth.d.ts")
|
|
351
|
+
});
|
|
352
|
+
addTypeTemplate({
|
|
353
|
+
filename: "chat-nuxt/chat-agent-environment-resolver.d.ts",
|
|
354
|
+
src: join(runtimeDir, "types/chat-agent-environment-resolver.d.ts")
|
|
355
|
+
}, {
|
|
356
|
+
nitro: true,
|
|
357
|
+
nuxt: true
|
|
358
|
+
});
|
|
359
|
+
nuxt.hook("prepare:types", ({ tsConfig }) => {
|
|
360
|
+
tsConfig.compilerOptions ||= {};
|
|
361
|
+
tsConfig.compilerOptions.paths ||= {};
|
|
362
|
+
tsConfig.compilerOptions.paths["#chat-auth"] = [chatAuthTypeTemplate.dst];
|
|
363
|
+
});
|
|
345
364
|
if (!Array.isArray(nuxt.options.extends)) {
|
|
346
365
|
nuxt.options.extends = nuxt.options.extends ? [nuxt.options.extends] : [];
|
|
347
366
|
}
|
|
@@ -401,10 +420,12 @@ const module$1 = defineNuxtModule({
|
|
|
401
420
|
priority: 1e3
|
|
402
421
|
});
|
|
403
422
|
addComponentsDir({
|
|
404
|
-
path: join(runtimeDir, "components")
|
|
423
|
+
path: join(runtimeDir, "components"),
|
|
424
|
+
extensions: VUE_COMPONENT_EXTENSIONS
|
|
405
425
|
});
|
|
406
426
|
addComponentsDir({
|
|
407
427
|
path: join(runtimeDir, "embed/components"),
|
|
428
|
+
extensions: VUE_COMPONENT_EXTENSIONS,
|
|
408
429
|
pathPrefix: false
|
|
409
430
|
});
|
|
410
431
|
nuxt.hook("vite:extendConfig", (config, { isServer }) => {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
defineProps({
|
|
2
|
+
const props = defineProps({
|
|
3
3
|
conversationId: { type: [String, null], required: false },
|
|
4
4
|
initialConversationId: { type: [String, null], required: false },
|
|
5
|
+
conversationScope: { type: [String, null], required: false },
|
|
5
6
|
hideSidebar: { type: Boolean, required: false },
|
|
7
|
+
hideSettings: { type: Boolean, required: false },
|
|
6
8
|
loadingMessages: { type: [Array, null], required: false },
|
|
7
9
|
loadingMessagesMode: { type: [String, null], required: false },
|
|
8
10
|
customComponents: { type: Object, required: false },
|
|
@@ -14,6 +16,7 @@ defineProps({
|
|
|
14
16
|
features: { type: Object, required: false }
|
|
15
17
|
});
|
|
16
18
|
defineEmits(["update:conversationId", "action"]);
|
|
19
|
+
const hideSettings = computed(() => props.hideSettings ?? false);
|
|
17
20
|
</script>
|
|
18
21
|
|
|
19
22
|
<template>
|
|
@@ -23,7 +26,9 @@ defineEmits(["update:conversationId", "action"]);
|
|
|
23
26
|
:tela-agent-inputs="telaAgentInputs"
|
|
24
27
|
:conversation-id="conversationId"
|
|
25
28
|
:initial-conversation-id="initialConversationId"
|
|
29
|
+
:conversation-scope="conversationScope"
|
|
26
30
|
:hide-sidebar="hideSidebar"
|
|
31
|
+
:hide-settings="hideSettings"
|
|
27
32
|
:features="features"
|
|
28
33
|
:loading-messages="loadingMessages"
|
|
29
34
|
:loading-messages-mode="loadingMessagesMode"
|
|
@@ -38,7 +43,9 @@ defineEmits(["update:conversationId", "action"]);
|
|
|
38
43
|
:user="user"
|
|
39
44
|
:conversation-id="conversationId"
|
|
40
45
|
:initial-conversation-id="initialConversationId"
|
|
46
|
+
:conversation-scope="conversationScope"
|
|
41
47
|
:hide-sidebar="hideSidebar"
|
|
48
|
+
:hide-settings="hideSettings"
|
|
42
49
|
:features="features"
|
|
43
50
|
:loading-messages="loadingMessages"
|
|
44
51
|
:loading-messages-mode="loadingMessagesMode"
|
|
@@ -31,15 +31,15 @@ const isOutputCode = computed(() => {
|
|
|
31
31
|
<template>
|
|
32
32
|
<div class="bg-subtle rounded-16px border border-gray-200">
|
|
33
33
|
<!-- Header -->
|
|
34
|
-
<div class="px-
|
|
35
|
-
<h3 class="text-
|
|
34
|
+
<div class="px-16px py-16px flex items-center justify-between">
|
|
35
|
+
<h3 class="text-16px font-580 leading-20px tracking-[-0.3px] text-primary truncate">
|
|
36
36
|
Bash
|
|
37
37
|
</h3>
|
|
38
38
|
<TelaStatus :variant="statusVariant" :label="statusLabel" />
|
|
39
39
|
</div>
|
|
40
40
|
|
|
41
41
|
<!-- Content -->
|
|
42
|
-
<div v-if="toolResult" class="px-
|
|
42
|
+
<div v-if="toolResult" class="px-16px pb-16px space-y-16px">
|
|
43
43
|
<!-- Command section -->
|
|
44
44
|
<div>
|
|
45
45
|
<div class="text-14px font-580 leading-16px tracking-[-0.15px] text-gray-700 mb-2">
|
|
@@ -31,15 +31,15 @@ const isOutputCode = computed(() => {
|
|
|
31
31
|
<template>
|
|
32
32
|
<div class="bg-subtle rounded-16px border border-gray-200">
|
|
33
33
|
<!-- Header -->
|
|
34
|
-
<div class="px-
|
|
35
|
-
<h3 class="text-
|
|
34
|
+
<div class="px-16px py-16px flex items-center justify-between">
|
|
35
|
+
<h3 class="text-16px font-580 leading-20px tracking-[-0.3px] text-primary">
|
|
36
36
|
{{ skillName }}
|
|
37
37
|
</h3>
|
|
38
38
|
<TelaStatus :variant="statusVariant" :label="statusLabel" />
|
|
39
39
|
</div>
|
|
40
40
|
|
|
41
41
|
<!-- Content -->
|
|
42
|
-
<div v-if="toolResult" class="px-
|
|
42
|
+
<div v-if="toolResult" class="px-16px pb-16px space-y-16px">
|
|
43
43
|
<!-- Result -->
|
|
44
44
|
<div>
|
|
45
45
|
<div class="text-14px font-580 leading-16px tracking-[-0.15px] text-gray-700 mb-2">
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { useClipboard } from "@vueuse/core";
|
|
3
2
|
import { formatFileSize, formatUploadDate, getFileIcon, getFileTypeLabel } from "../../utils/file";
|
|
4
3
|
const props = defineProps({
|
|
5
4
|
conversationId: { type: String, required: true }
|
|
@@ -8,7 +7,7 @@ const conversationIdRef = computed(() => props.conversationId);
|
|
|
8
7
|
const { files, loading } = useConversationFiles(conversationIdRef);
|
|
9
8
|
const { openPreview, updatePreview, setError } = useFilePreviewPanel();
|
|
10
9
|
const { getCached, fetchUrlAsBase64, setCache } = useMediaCache();
|
|
11
|
-
const {
|
|
10
|
+
const { downloadFile, copyFileLink } = useConversationFileDownloads();
|
|
12
11
|
const searchQuery = ref("");
|
|
13
12
|
const fileTypeFilter = ref(null);
|
|
14
13
|
const currentRequestId = ref(0);
|
|
@@ -144,77 +143,17 @@ async function handleFileClick(file) {
|
|
|
144
143
|
});
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
|
-
const statusToast = useStatusToast();
|
|
148
|
-
async function handleDownload(file) {
|
|
149
|
-
statusToast.update({
|
|
150
|
-
text: "Baixando arquivo...",
|
|
151
|
-
loading: true
|
|
152
|
-
});
|
|
153
|
-
try {
|
|
154
|
-
let downloadUrl = file.url;
|
|
155
|
-
if (file.url.startsWith("vault://")) {
|
|
156
|
-
const { url } = await $fetch("/api/vault/resolve", {
|
|
157
|
-
method: "POST",
|
|
158
|
-
body: { reference: file.url }
|
|
159
|
-
});
|
|
160
|
-
downloadUrl = url;
|
|
161
|
-
}
|
|
162
|
-
const response = await fetch(downloadUrl);
|
|
163
|
-
const blob = await response.blob();
|
|
164
|
-
const blobUrl = URL.createObjectURL(blob);
|
|
165
|
-
const link = document.createElement("a");
|
|
166
|
-
link.href = blobUrl;
|
|
167
|
-
link.download = file.name;
|
|
168
|
-
document.body.appendChild(link);
|
|
169
|
-
link.click();
|
|
170
|
-
document.body.removeChild(link);
|
|
171
|
-
URL.revokeObjectURL(blobUrl);
|
|
172
|
-
statusToast.update({
|
|
173
|
-
text: "Download conclu\xEDdo",
|
|
174
|
-
icon: "i-ph-check"
|
|
175
|
-
});
|
|
176
|
-
} catch (error) {
|
|
177
|
-
console.error("Failed to download file:", error);
|
|
178
|
-
statusToast.update({
|
|
179
|
-
text: "Erro ao baixar arquivo",
|
|
180
|
-
icon: "i-ph-warning"
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
async function handleCopyLink(file) {
|
|
185
|
-
try {
|
|
186
|
-
let publicUrl = file.url;
|
|
187
|
-
if (file.url.startsWith("vault://")) {
|
|
188
|
-
const { url } = await $fetch("/api/vault/resolve", {
|
|
189
|
-
method: "POST",
|
|
190
|
-
body: { reference: file.url }
|
|
191
|
-
});
|
|
192
|
-
publicUrl = url;
|
|
193
|
-
}
|
|
194
|
-
await copy(publicUrl);
|
|
195
|
-
statusToast.update({
|
|
196
|
-
text: "Link copiado",
|
|
197
|
-
icon: "i-ph-check"
|
|
198
|
-
});
|
|
199
|
-
} catch (error) {
|
|
200
|
-
console.error("Failed to copy link:", error);
|
|
201
|
-
statusToast.update({
|
|
202
|
-
text: "Erro ao copiar link",
|
|
203
|
-
icon: "i-ph-warning"
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
146
|
function getFileMenuItems(file) {
|
|
208
147
|
return [
|
|
209
148
|
{
|
|
210
149
|
label: "Copiar link",
|
|
211
150
|
icon: "i-ph-link",
|
|
212
|
-
click: () =>
|
|
151
|
+
click: () => copyFileLink(file)
|
|
213
152
|
},
|
|
214
153
|
{
|
|
215
154
|
label: "Baixar",
|
|
216
155
|
icon: "i-ph-download-simple",
|
|
217
|
-
click: () =>
|
|
156
|
+
click: () => downloadFile(file)
|
|
218
157
|
}
|
|
219
158
|
];
|
|
220
159
|
}
|
|
@@ -84,15 +84,15 @@ const isEmptyMessage = computed(() => {
|
|
|
84
84
|
<template>
|
|
85
85
|
<div class="bg-subtle rounded-16px border border-gray-200">
|
|
86
86
|
<!-- Header -->
|
|
87
|
-
<div class="px-
|
|
88
|
-
<h3 class="text-
|
|
87
|
+
<div class="px-16px py-16px flex items-center justify-between">
|
|
88
|
+
<h3 class="text-16px font-580 leading-20px tracking-[-0.3px] text-primary truncate">
|
|
89
89
|
{{ name }}
|
|
90
90
|
</h3>
|
|
91
91
|
<TelaStatus :variant="statusVariant" :label="statusLabel" />
|
|
92
92
|
</div>
|
|
93
93
|
|
|
94
94
|
<!-- Content -->
|
|
95
|
-
<div v-if="hasContent" class="px-
|
|
95
|
+
<div v-if="hasContent" class="px-16px pb-16px space-y-16px">
|
|
96
96
|
<!-- Parameters section -->
|
|
97
97
|
<div v-if="input && Object.keys(input).length > 0">
|
|
98
98
|
<!-- Special handling for TodoWrite tool -->
|
|
@@ -122,9 +122,9 @@ function highlightMatch(content) {
|
|
|
122
122
|
<template>
|
|
123
123
|
<div class="bg-subtle rounded-16px border border-gray-200">
|
|
124
124
|
<!-- Header -->
|
|
125
|
-
<div class="px-
|
|
125
|
+
<div class="px-16px py-16px flex items-center justify-between">
|
|
126
126
|
<div class="flex-1 min-w-0">
|
|
127
|
-
<h3 class="text-
|
|
127
|
+
<h3 class="text-16px font-580 leading-20px tracking-[-0.3px] text-primary truncate">
|
|
128
128
|
Grep
|
|
129
129
|
</h3>
|
|
130
130
|
<p class="text-12px text-secondary truncate mt-2px">
|
|
@@ -136,7 +136,7 @@ function highlightMatch(content) {
|
|
|
136
136
|
</div>
|
|
137
137
|
|
|
138
138
|
<!-- Content -->
|
|
139
|
-
<div v-if="toolResult" class="px-
|
|
139
|
+
<div v-if="toolResult" class="px-16px pb-16px space-y-16px">
|
|
140
140
|
<!-- Search info -->
|
|
141
141
|
<div>
|
|
142
142
|
<div class="text-14px font-580 leading-16px tracking-[-0.15px] text-gray-700 mb-2">
|
|
@@ -201,7 +201,7 @@ const hasDetectedFiles = computed(() => contentSegments.value.some((s) => s.type
|
|
|
201
201
|
v-if="hasReasoning || isPending"
|
|
202
202
|
:reasoning-data="message.reasoningData ?? []"
|
|
203
203
|
:animate="wasEverPending"
|
|
204
|
-
:completed="
|
|
204
|
+
:completed="!isPending"
|
|
205
205
|
:created-at="message.createdAt"
|
|
206
206
|
:updated-at="message.updatedAt"
|
|
207
207
|
/>
|
|
@@ -20,12 +20,14 @@ type __VLS_Props = {
|
|
|
20
20
|
showCancelButton?: boolean;
|
|
21
21
|
};
|
|
22
22
|
declare function addFiles(files: File[], inputName?: string): void;
|
|
23
|
+
declare function setDraft(text: string): void;
|
|
23
24
|
type __VLS_ModelProps = {
|
|
24
25
|
'model'?: ModelOption;
|
|
25
26
|
};
|
|
26
27
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
27
28
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
28
29
|
addFiles: typeof addFiles;
|
|
30
|
+
setDraft: typeof setDraft;
|
|
29
31
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
30
32
|
"update:model": (value: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6") => any;
|
|
31
33
|
} & {
|
|
@@ -395,14 +395,31 @@ function addFiles(files, inputName) {
|
|
|
395
395
|
});
|
|
396
396
|
queueFiles(validFiles, inputName);
|
|
397
397
|
}
|
|
398
|
+
function setDraft(text) {
|
|
399
|
+
content.value = text;
|
|
400
|
+
if (editorRef.value) {
|
|
401
|
+
editorRef.value.textContent = text;
|
|
402
|
+
const range = document.createRange();
|
|
403
|
+
range.selectNodeContents(editorRef.value);
|
|
404
|
+
range.collapse(false);
|
|
405
|
+
const selection = window.getSelection();
|
|
406
|
+
selection?.removeAllRanges();
|
|
407
|
+
if (range)
|
|
408
|
+
selection?.addRange(range);
|
|
409
|
+
}
|
|
410
|
+
nextTick(() => {
|
|
411
|
+
editorRef.value?.focus();
|
|
412
|
+
});
|
|
413
|
+
}
|
|
398
414
|
defineExpose({
|
|
399
|
-
addFiles
|
|
415
|
+
addFiles,
|
|
416
|
+
setDraft
|
|
400
417
|
});
|
|
401
418
|
</script>
|
|
402
419
|
|
|
403
420
|
<template>
|
|
404
|
-
<div :class="centered ? '' : hasDisclaimer ? 'px-24px' : 'px-24px pb-24px'" bg-transparent>
|
|
405
|
-
<div :class="centered ? 'w-700px' : 'max-w-800px'" mx-auto>
|
|
421
|
+
<div :class="centered ? 'w-full' : hasDisclaimer ? 'px-24px' : 'px-24px pb-24px'" bg-transparent>
|
|
422
|
+
<div :class="centered ? 'w-full max-w-700px' : 'max-w-800px'" mx-auto>
|
|
406
423
|
<Transition name="agent-inputs-panel-motion" mode="out-in" appear>
|
|
407
424
|
<div
|
|
408
425
|
v-if="hasAgentInputs"
|
|
@@ -511,7 +528,6 @@ defineExpose({
|
|
|
511
528
|
</Transition>
|
|
512
529
|
</div>
|
|
513
530
|
</Transition>
|
|
514
|
-
|
|
515
531
|
<div v-if="pendingFiles.length > 0" flex flex-wrap gap-8px mb-12px>
|
|
516
532
|
<div
|
|
517
533
|
v-for="pendingFile in pendingFiles"
|
|
@@ -668,5 +684,5 @@ defineExpose({
|
|
|
668
684
|
</template>
|
|
669
685
|
|
|
670
686
|
<style scoped>
|
|
671
|
-
.input-container{border:.5px solid #e5e7eb;box-shadow:0 3px 10px 0 hsla(0,0%,64%,.08),0 12px 20px 0 hsla(0,0%,64%,.06);position:relative;z-index:2}.input-locked{background:var(--Base-Gray-gray-100,#f3f4f6);border:.5px solid var(--Base-Neutral-neutral-300,#d9d9d9);box-shadow:0 12px 20px 0 hsla(0,0%,64%,.06)}.agent-inputs-panel{box-shadow:0 3px 10px 0 hsla(0,0%,64%,.06);position:relative;transform-origin:bottom center;will-change:max-height,opacity,transform,filter;z-index:1}.agent-inputs-panel-motion-enter-active,.agent-inputs-panel-motion-leave-active{overflow:hidden;transition:max-height .26s cubic-bezier(.2,.8,.2,1),opacity .18s ease,transform .26s cubic-bezier(.2,.8,.2,1),filter .22s ease}.agent-inputs-panel-motion-enter-from,.agent-inputs-panel-motion-leave-to{filter:blur(2px);max-height:0;opacity:0;transform:translateY(46px) scale(.985)}.agent-inputs-panel-motion-enter-to,.agent-inputs-panel-motion-leave-from{filter:blur(0);max-height:560px;opacity:1;transform:translateY(0) scale(1)}.agent-inputs-content{transform-origin:bottom center;will-change:max-height,opacity,transform,filter}.agent-inputs-content-enter-active,.agent-inputs-content-leave-active{overflow:hidden;transition:max-height .22s cubic-bezier(.2,.8,.2,1),opacity .16s ease,transform .22s cubic-bezier(.2,.8,.2,1),filter .22s ease}.agent-inputs-content-enter-from,.agent-inputs-content-leave-to{filter:blur(2px);max-height:0;opacity:0;transform:translateY(14px) scale(.985)}.agent-inputs-content-enter-to,.agent-inputs-content-leave-from{filter:blur(0);max-height:900px;opacity:1;transform:translateY(0) scale(1)}.agent-text-input{border:.5px solid #e5e7eb;border-radius:8px;color:#111827;font-size:13px;line-height:18px;min-height:64px;outline:none;padding:8px 10px;resize:vertical;width:100%}.agent-text-input:focus{border-color:#d1d5db}.agent-text-input:disabled{background-color:#f3f4f6;cursor:not-allowed}.agent-file-variable-select{background:#fff;border:.5px solid #d1d5db;border-radius:6px;color:#374151;font-size:12px;height:28px;max-width:160px;outline:none;padding:0 8px}.editor-content{caret-color:#111827;color:#111827;max-height:200px;outline:none;overflow-y:auto;white-space:pre-wrap;word-break:break-word}.editor-content.is-empty:before{color:#bdc4cc;content:attr(data-placeholder);float:left;height:0;pointer-events:none}.icon-button:disabled{cursor:not-allowed;opacity:.5}.custom-scrollbar{scrollbar-color:#d1d5db transparent;scrollbar-width:thin}.custom-scrollbar::-webkit-scrollbar{width:6px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#d1d5db;border-radius:3px}.stop-button{align-items:center;background:transparent;border:none;cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;position:relative;transition:opacity .15s ease;width:32px}.stop-button:hover{opacity:.8}.stop-button-ring{border:2px solid #e5e7eb;border-radius:50%;inset:0;position:absolute}.stop-button-ring:before{animation:spin 1s linear infinite;border:2px solid transparent;border-radius:50%;border-top-color:#111827;content:"";inset:-2px;position:absolute}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}
|
|
687
|
+
.input-container{border:.5px solid #e5e7eb;box-shadow:0 3px 10px 0 hsla(0,0%,64%,.08),0 12px 20px 0 hsla(0,0%,64%,.06);position:relative;z-index:2}.input-locked{background:var(--Base-Gray-gray-100,#f3f4f6);border:.5px solid var(--Base-Neutral-neutral-300,#d9d9d9);box-shadow:0 12px 20px 0 hsla(0,0%,64%,.06)}.agent-inputs-panel{box-shadow:0 3px 10px 0 hsla(0,0%,64%,.06);position:relative;transform-origin:bottom center;will-change:max-height,opacity,transform,filter;z-index:1}.agent-inputs-panel-motion-enter-active,.agent-inputs-panel-motion-leave-active{overflow:hidden;transition:max-height .26s cubic-bezier(.2,.8,.2,1),opacity .18s ease,transform .26s cubic-bezier(.2,.8,.2,1),filter .22s ease}.agent-inputs-panel-motion-enter-from,.agent-inputs-panel-motion-leave-to{filter:blur(2px);max-height:0;opacity:0;transform:translateY(46px) scale(.985)}.agent-inputs-panel-motion-enter-to,.agent-inputs-panel-motion-leave-from{filter:blur(0);max-height:560px;opacity:1;transform:translateY(0) scale(1)}.agent-inputs-content{transform-origin:bottom center;will-change:max-height,opacity,transform,filter}.agent-inputs-content-enter-active,.agent-inputs-content-leave-active{overflow:hidden;transition:max-height .22s cubic-bezier(.2,.8,.2,1),opacity .16s ease,transform .22s cubic-bezier(.2,.8,.2,1),filter .22s ease}.agent-inputs-content-enter-from,.agent-inputs-content-leave-to{filter:blur(2px);max-height:0;opacity:0;transform:translateY(14px) scale(.985)}.agent-inputs-content-enter-to,.agent-inputs-content-leave-from{filter:blur(0);max-height:900px;opacity:1;transform:translateY(0) scale(1)}.agent-text-input{border:.5px solid #e5e7eb;border-radius:8px;color:#111827;font-size:13px;line-height:18px;min-height:64px;outline:none;padding:8px 10px;resize:vertical;width:100%}.agent-text-input:focus{border-color:#d1d5db}.agent-text-input:disabled{background-color:#f3f4f6;cursor:not-allowed}.agent-file-variable-select{background:#fff;border:.5px solid #d1d5db;border-radius:6px;color:#374151;font-size:12px;height:28px;max-width:160px;outline:none;padding:0 8px}.editor-content{caret-color:#111827;color:#111827;max-height:200px;outline:none;overflow-y:auto;white-space:pre-wrap;word-break:break-word}.editor-content.is-empty:before{color:#bdc4cc;content:attr(data-placeholder);float:left;height:0;pointer-events:none}.icon-button:disabled{cursor:not-allowed;opacity:.5}.custom-scrollbar{scrollbar-color:#d1d5db transparent;scrollbar-width:thin}.custom-scrollbar::-webkit-scrollbar{width:6px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#d1d5db;border-radius:3px}@media (max-width:480px){.custom-scrollbar{scrollbar-width:none}.custom-scrollbar::-webkit-scrollbar{display:none}}.stop-button{align-items:center;background:transparent;border:none;cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;position:relative;transition:opacity .15s ease;width:32px}.stop-button:hover{opacity:.8}.stop-button-ring{border:2px solid #e5e7eb;border-radius:50%;inset:0;position:absolute}.stop-button-ring:before{animation:spin 1s linear infinite;border:2px solid transparent;border-radius:50%;border-top-color:#111827;content:"";inset:-2px;position:absolute}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}
|
|
672
688
|
</style>
|
|
@@ -20,12 +20,14 @@ type __VLS_Props = {
|
|
|
20
20
|
showCancelButton?: boolean;
|
|
21
21
|
};
|
|
22
22
|
declare function addFiles(files: File[], inputName?: string): void;
|
|
23
|
+
declare function setDraft(text: string): void;
|
|
23
24
|
type __VLS_ModelProps = {
|
|
24
25
|
'model'?: ModelOption;
|
|
25
26
|
};
|
|
26
27
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
27
28
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
28
29
|
addFiles: typeof addFiles;
|
|
30
|
+
setDraft: typeof setDraft;
|
|
29
31
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
30
32
|
"update:model": (value: "claude-sonnet-4-5" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-opus-4-5" | "claude-opus-4-6") => any;
|
|
31
33
|
} & {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConversationFile } from '#chat-runtime/types/chat';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
file: ConversationFile | null;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
6
|
+
close: () => any;
|
|
7
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
8
|
+
onClose?: (() => any) | undefined;
|
|
9
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
|
+
declare const _default: typeof __VLS_export;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { onKeyStroke } from "@vueuse/core";
|
|
3
|
+
import { formatFileSize, getFileTypeLabel, normalizeMimeType } from "#chat-runtime/utils/file";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
file: { type: null, required: true }
|
|
6
|
+
});
|
|
7
|
+
const emit = defineEmits(["close"]);
|
|
8
|
+
const { resolveDownloadUrl, downloadFile } = useConversationFileDownloads();
|
|
9
|
+
const normalizedMimeType = computed(() => normalizeMimeType(props.file?.mimeType ?? ""));
|
|
10
|
+
const isImage = computed(() => normalizedMimeType.value.startsWith("image/"));
|
|
11
|
+
const resolvedUrl = ref(null);
|
|
12
|
+
const resolving = ref(false);
|
|
13
|
+
watch(() => props.file, async (file) => {
|
|
14
|
+
resolvedUrl.value = null;
|
|
15
|
+
if (!file) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!file.url.startsWith("vault://")) {
|
|
19
|
+
resolvedUrl.value = file.url;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
resolving.value = true;
|
|
23
|
+
try {
|
|
24
|
+
const url = await resolveDownloadUrl(file);
|
|
25
|
+
if (props.file?.id === file.id) {
|
|
26
|
+
resolvedUrl.value = url;
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error("Failed to resolve preview URL", err);
|
|
30
|
+
} finally {
|
|
31
|
+
resolving.value = false;
|
|
32
|
+
}
|
|
33
|
+
}, { immediate: true });
|
|
34
|
+
onKeyStroke("Escape", () => {
|
|
35
|
+
if (props.file) {
|
|
36
|
+
emit("close");
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
function handleDownload() {
|
|
40
|
+
if (props.file) {
|
|
41
|
+
void downloadFile(props.file);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<Transition name="preview">
|
|
48
|
+
<div
|
|
49
|
+
v-if="file"
|
|
50
|
+
class="preview"
|
|
51
|
+
:class="isImage ? 'preview--image' : 'preview--surface'"
|
|
52
|
+
role="dialog"
|
|
53
|
+
aria-modal="true"
|
|
54
|
+
>
|
|
55
|
+
<div class="preview__topbar" :class="isImage ? 'preview__topbar--dark' : 'preview__topbar--light'">
|
|
56
|
+
<button
|
|
57
|
+
class="preview__icon-btn"
|
|
58
|
+
aria-label="Fechar"
|
|
59
|
+
@click="emit('close')"
|
|
60
|
+
>
|
|
61
|
+
<span i-ph-x />
|
|
62
|
+
</button>
|
|
63
|
+
|
|
64
|
+
<div flex="~ col 1" min-w-0 px-8px>
|
|
65
|
+
<span class="preview__title" truncate>{{ file.name }}</span>
|
|
66
|
+
<span class="preview__meta" truncate>
|
|
67
|
+
{{ getFileTypeLabel(file.mimeType) }}<template v-if="file.size">
|
|
68
|
+
· {{ formatFileSize(file.size) }}
|
|
69
|
+
</template>
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<button
|
|
74
|
+
class="preview__icon-btn"
|
|
75
|
+
aria-label="Baixar"
|
|
76
|
+
@click="handleDownload"
|
|
77
|
+
>
|
|
78
|
+
<span i-ph-download-simple />
|
|
79
|
+
</button>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="preview__body">
|
|
83
|
+
<div v-if="resolving && !resolvedUrl" flex items-center justify-center h-full w-full>
|
|
84
|
+
<span
|
|
85
|
+
i-ph-spinner animate-spin text-32px
|
|
86
|
+
:class="isImage ? 'text-white' : 'text-neutral-400'"
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<img
|
|
91
|
+
v-else-if="isImage && resolvedUrl"
|
|
92
|
+
:src="resolvedUrl"
|
|
93
|
+
:alt="file.name"
|
|
94
|
+
class="preview__image"
|
|
95
|
+
>
|
|
96
|
+
|
|
97
|
+
<RenderersArtifactDisplay
|
|
98
|
+
v-else-if="resolvedUrl"
|
|
99
|
+
:url="resolvedUrl"
|
|
100
|
+
:mime-type="normalizedMimeType"
|
|
101
|
+
:name="file.name"
|
|
102
|
+
class="preview__renderer"
|
|
103
|
+
@download="handleDownload"
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</Transition>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<style scoped>
|
|
111
|
+
.preview{display:flex;flex-direction:column;inset:0;position:absolute;z-index:50}.preview--image{background:#0a0a0b}.preview--surface{background:#fff}.preview-enter-active,.preview-leave-active{transition:transform .26s cubic-bezier(.2,.8,.2,1)}.preview-enter-from,.preview-leave-to{transform:translateY(100%)}.preview__topbar{align-items:center;display:flex;flex-shrink:0;gap:4px;padding:10px 10px 8px}.preview__topbar--dark{background:rgba(0,0,0,.4);color:#fff}.preview__topbar--light{border-bottom:.5px solid #e8e8e8;color:#171717}.preview__icon-btn{align-items:center;background:transparent;border-radius:8px;color:inherit;display:inline-flex;font-size:20px;height:32px;justify-content:center;transition:background .15s;width:32px}.preview__icon-btn:hover{background:hsla(0,0%,100%,.08)}.preview--surface .preview__icon-btn:hover{background:rgba(0,0,0,.05)}.preview__title{display:block;font-size:14px;font-weight:600;letter-spacing:-.15px;line-height:18px}.preview__meta{display:block;font-size:11px;line-height:14px;opacity:.8}.preview__body{align-items:center;display:flex;flex:1;justify-content:center;min-height:0;overflow:auto}.preview--image .preview__body{overflow:hidden}.preview__image{max-height:100%;max-width:100%;-o-object-fit:contain;object-fit:contain}.preview__renderer{display:block;min-height:100%;width:100%}
|
|
112
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ConversationFile } from '#chat-runtime/types/chat';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
file: ConversationFile | null;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
6
|
+
close: () => any;
|
|
7
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
8
|
+
onClose?: (() => any) | undefined;
|
|
9
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
10
|
+
declare const _default: typeof __VLS_export;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
conversationId: string;
|
|
3
|
+
};
|
|
4
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
5
|
+
viewInChat: () => any;
|
|
6
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
7
|
+
onViewInChat?: (() => any) | undefined;
|
|
8
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|