@meistrari/chat-nuxt 1.7.0 → 1.8.1

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 (92) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +2 -0
  3. package/dist/runtime/components/MeistrariChatEmbed.vue +5 -1
  4. package/dist/runtime/components/chat/bash-widget.vue +3 -3
  5. package/dist/runtime/components/chat/doc-sdk-widget.vue +3 -3
  6. package/dist/runtime/components/chat/files-view.vue +3 -64
  7. package/dist/runtime/components/chat/generic-tool-widget.vue +3 -3
  8. package/dist/runtime/components/chat/grep-widget.vue +3 -3
  9. package/dist/runtime/components/chat/message-bubble.vue +1 -1
  10. package/dist/runtime/components/chat/message-input.d.vue.ts +2 -0
  11. package/dist/runtime/components/chat/message-input.vue +21 -5
  12. package/dist/runtime/components/chat/message-input.vue.d.ts +2 -0
  13. package/dist/runtime/components/chat/message-list.vue +1 -1
  14. package/dist/runtime/components/chat/mobile/files/file-preview.d.vue.ts +11 -0
  15. package/dist/runtime/components/chat/mobile/files/file-preview.vue +112 -0
  16. package/dist/runtime/components/chat/mobile/files/file-preview.vue.d.ts +11 -0
  17. package/dist/runtime/components/chat/mobile/files/files-view.d.vue.ts +10 -0
  18. package/dist/runtime/components/chat/mobile/files/files-view.vue +384 -0
  19. package/dist/runtime/components/chat/mobile/files/files-view.vue.d.ts +10 -0
  20. package/dist/runtime/components/chat/mobile/home/header.d.vue.ts +19 -0
  21. package/dist/runtime/components/chat/mobile/home/header.vue +55 -0
  22. package/dist/runtime/components/chat/mobile/home/header.vue.d.ts +19 -0
  23. package/dist/runtime/components/chat/mobile/home/recents.d.vue.ts +14 -0
  24. package/dist/runtime/components/chat/mobile/home/recents.vue +48 -0
  25. package/dist/runtime/components/chat/mobile/home/recents.vue.d.ts +14 -0
  26. package/dist/runtime/components/chat/mobile/home/suggestions.d.vue.ts +7 -0
  27. package/dist/runtime/components/chat/mobile/home/suggestions.vue +31 -0
  28. package/dist/runtime/components/chat/mobile/home/suggestions.vue.d.ts +7 -0
  29. package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.d.vue.ts +21 -0
  30. package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue +48 -0
  31. package/dist/runtime/components/chat/mobile/primitives/bottom-sheet.vue.d.ts +21 -0
  32. package/dist/runtime/components/chat/mobile/primitives/info-bubble.d.vue.ts +6 -0
  33. package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue +50 -0
  34. package/dist/runtime/components/chat/mobile/primitives/info-bubble.vue.d.ts +6 -0
  35. package/dist/runtime/components/chat/mobile/shell/drawer.d.vue.ts +22 -0
  36. package/dist/runtime/components/chat/mobile/shell/drawer.vue +143 -0
  37. package/dist/runtime/components/chat/mobile/shell/drawer.vue.d.ts +22 -0
  38. package/dist/runtime/components/chat/mobile/shell/header.d.vue.ts +40 -0
  39. package/dist/runtime/components/chat/mobile/shell/header.vue +139 -0
  40. package/dist/runtime/components/chat/mobile/shell/header.vue.d.ts +40 -0
  41. package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.d.vue.ts +14 -0
  42. package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue +47 -0
  43. package/dist/runtime/components/chat/mobile/shell/outdated-settings-sheet.vue.d.ts +14 -0
  44. package/dist/runtime/components/chat/mobile/shell/shell.d.vue.ts +122 -0
  45. package/dist/runtime/components/chat/mobile/shell/shell.vue +283 -0
  46. package/dist/runtime/components/chat/mobile/shell/shell.vue.d.ts +122 -0
  47. package/dist/runtime/components/chat/mobile/shell/workspace-sheet.d.vue.ts +19 -0
  48. package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue +51 -0
  49. package/dist/runtime/components/chat/mobile/shell/workspace-sheet.vue.d.ts +19 -0
  50. package/dist/runtime/components/chat/mobile/usage/usage-view.d.vue.ts +8 -0
  51. package/dist/runtime/components/chat/mobile/usage/usage-view.vue +243 -0
  52. package/dist/runtime/components/chat/mobile/usage/usage-view.vue.d.ts +8 -0
  53. package/dist/runtime/components/chat/model-selector.vue +6 -0
  54. package/dist/runtime/components/chat/read-widget.vue +3 -3
  55. package/dist/runtime/components/chat/reasoning-step-item.vue +1 -1
  56. package/dist/runtime/components/chat/reasoning-steps.vue +1 -1
  57. package/dist/runtime/components/chat/reasoning-thought.vue +2 -3
  58. package/dist/runtime/components/chat/tela-skill-widget.vue +3 -3
  59. package/dist/runtime/components/chat/thinking-widget.vue +3 -3
  60. package/dist/runtime/components/chat/usage-view.vue +20 -101
  61. package/dist/runtime/components/chat/web-search-widget.vue +3 -3
  62. package/dist/runtime/components/chat/workstation-query-widget.vue +6 -6
  63. package/dist/runtime/components/chat/write-widget.vue +3 -3
  64. package/dist/runtime/composables/useConversationFileDownloads.d.ts +6 -0
  65. package/dist/runtime/composables/useConversationFileDownloads.js +50 -0
  66. package/dist/runtime/composables/useConversationUsage.d.ts +13 -0
  67. package/dist/runtime/composables/useConversationUsage.js +121 -0
  68. package/dist/runtime/composables/useEmbedConfig.d.ts +2 -0
  69. package/dist/runtime/composables/useEmbedConfig.js +4 -0
  70. package/dist/runtime/composables/useMentionAutocomplete.d.ts +6 -0
  71. package/dist/runtime/composables/useMentionAutocomplete.js +71 -23
  72. package/dist/runtime/composables/usePdf.js +2 -2
  73. package/dist/runtime/composables/useWorkspaceSwitchModal.d.ts +5 -0
  74. package/dist/runtime/composables/useWorkspaceSwitchModal.js +14 -0
  75. package/dist/runtime/embed/components/ChatConfigurationModal.vue +119 -13
  76. package/dist/runtime/embed/components/ChatEmbed.vue +4 -0
  77. package/dist/runtime/embed/components/ChatEmbedInner.d.vue.ts +2 -0
  78. package/dist/runtime/embed/components/ChatEmbedInner.vue +201 -11
  79. package/dist/runtime/embed/components/ChatEmbedInner.vue.d.ts +2 -0
  80. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.d.vue.ts +34 -0
  81. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue +95 -0
  82. package/dist/runtime/embed/components/configuration/ChatConfigurationMobileShell.vue.d.ts +34 -0
  83. package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +3 -1
  84. package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +29 -5
  85. package/dist/runtime/server/utils/conversation-agent-turn.js +4 -0
  86. package/dist/runtime/server/utils/conversation-message-sync.d.ts +8 -2
  87. package/dist/runtime/server/utils/conversation-message-sync.js +52 -6
  88. package/dist/runtime/types/chat-auth.d.ts +1 -0
  89. package/dist/runtime/types/embed.d.ts +2 -0
  90. package/dist/runtime/utils/breakpoints.d.ts +1 -0
  91. package/dist/runtime/utils/breakpoints.js +1 -0
  92. package/package.json +1 -1
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "1.7.0",
4
+ "version": "1.8.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -196,6 +196,7 @@ export function useChatAuth() {
196
196
  return {
197
197
  user: auth.user,
198
198
  activeOrganization: auth.activeOrganization,
199
+ logout: auth.logout,
199
200
  getToken: auth.getToken,
200
201
  getAvailableOrganizations: auth.getAvailableOrganizations,
201
202
  switchOrganization: auth.switchOrganization,
@@ -241,6 +242,7 @@ export function useChatAuth() {
241
242
  return {
242
243
  user: session.user,
243
244
  activeOrganization: organization.activeOrganization,
245
+ logout: session.signOut,
244
246
  getToken: session.getToken,
245
247
  getAvailableOrganizations: organization.listOrganizations,
246
248
  switchOrganization: organization.setActiveOrganization,
@@ -1,9 +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
5
  conversationScope: { type: [String, null], required: false },
6
6
  hideSidebar: { type: Boolean, required: false },
7
+ hideSettings: { type: Boolean, required: false },
7
8
  loadingMessages: { type: [Array, null], required: false },
8
9
  loadingMessagesMode: { type: [String, null], required: false },
9
10
  customComponents: { type: Object, required: false },
@@ -15,6 +16,7 @@ defineProps({
15
16
  features: { type: Object, required: false }
16
17
  });
17
18
  defineEmits(["update:conversationId", "action"]);
19
+ const hideSettings = computed(() => props.hideSettings ?? false);
18
20
  </script>
19
21
 
20
22
  <template>
@@ -26,6 +28,7 @@ defineEmits(["update:conversationId", "action"]);
26
28
  :initial-conversation-id="initialConversationId"
27
29
  :conversation-scope="conversationScope"
28
30
  :hide-sidebar="hideSidebar"
31
+ :hide-settings="hideSettings"
29
32
  :features="features"
30
33
  :loading-messages="loadingMessages"
31
34
  :loading-messages-mode="loadingMessagesMode"
@@ -42,6 +45,7 @@ defineEmits(["update:conversationId", "action"]);
42
45
  :initial-conversation-id="initialConversationId"
43
46
  :conversation-scope="conversationScope"
44
47
  :hide-sidebar="hideSidebar"
48
+ :hide-settings="hideSettings"
45
49
  :features="features"
46
50
  :loading-messages="loadingMessages"
47
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-24px py-24px flex items-center justify-between">
35
- <h3 class="text-20px font-580 leading-24px tracking-[-0.5px] text-primary truncate">
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-24px pb-24px space-y-16px">
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-24px py-24px flex items-center justify-between">
35
- <h3 class="text-20px font-580 leading-24px tracking-[-0.5px] text-primary">
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-24px pb-24px space-y-16px">
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 { copy } = useClipboard();
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: () => handleCopyLink(file)
151
+ click: () => copyFileLink(file)
213
152
  },
214
153
  {
215
154
  label: "Baixar",
216
155
  icon: "i-ph-download-simple",
217
- click: () => handleDownload(file)
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-24px py-24px flex items-center justify-between">
88
- <h3 class="text-20px font-580 leading-24px tracking-[-0.5px] text-primary truncate">
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-24px pb-24px space-y-16px">
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-24px py-24px flex items-center justify-between">
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-20px font-580 leading-24px tracking-[-0.5px] text-primary truncate">
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-24px pb-24px space-y-16px">
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="message.status === '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
  } & {
@@ -27,7 +27,7 @@ onMounted(scrollToBottom);
27
27
  ref="containerRef"
28
28
  flex-1
29
29
  overflow-y-auto
30
- pt-88px pb-24px px-24px
30
+ pt-72px pb-24px px-16px
31
31
  >
32
32
  <div max-w-800px mx-auto>
33
33
  <div v-if="loading" flex="~ col" gap-16px>
@@ -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;