@mirodan/ai-insight 0.1.3 → 0.1.5
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/dist/module.js +12 -12
- package/dist/module.js.map +1 -1
- package/dist/runtime/components/AiInsight.vue +1 -1
- package/dist/runtime/components/AiInsightDialog.vue +59 -45
- package/dist/runtime/i18n/messages.ts +24 -0
- package/dist/runtime/plugins/i18n.ts +12 -0
- package/dist/runtime/server/_errors.js +18 -0
- package/dist/runtime/server/proxy.post.ts +47 -10
- package/dist/runtime/utils/parseUIMessageStream.ts +29 -0
- package/package.json +6 -4
package/dist/module.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { addComponent as e,
|
|
1
|
+
import { addComponent as e, addPlugin as t, addServerHandler as n, createResolver as r, defineNuxtModule as i } from "@nuxt/kit";
|
|
2
2
|
//#region src/module.ts
|
|
3
|
-
var
|
|
3
|
+
var a = i({
|
|
4
4
|
meta: { name: "@mirodan/ai-insight" },
|
|
5
5
|
defaults: { endpoint: "https://api.mirodan.ai/v1/insight" },
|
|
6
|
-
setup(
|
|
7
|
-
let
|
|
8
|
-
|
|
6
|
+
setup(i, a) {
|
|
7
|
+
let o = r(import.meta.url);
|
|
8
|
+
a.options.runtimeConfig.aiInsight = {
|
|
9
9
|
key: "",
|
|
10
|
-
endpoint:
|
|
10
|
+
endpoint: i.endpoint
|
|
11
11
|
}, e({
|
|
12
12
|
name: "AiInsight",
|
|
13
|
-
filePath:
|
|
13
|
+
filePath: o.resolve("./runtime/components/AiInsight.vue")
|
|
14
14
|
}), e({
|
|
15
15
|
name: "AiInsightDialog",
|
|
16
|
-
filePath:
|
|
17
|
-
}),
|
|
16
|
+
filePath: o.resolve("./runtime/components/AiInsightDialog.vue")
|
|
17
|
+
}), n({
|
|
18
18
|
route: "/api/ai-insight/proxy",
|
|
19
|
-
handler:
|
|
20
|
-
});
|
|
19
|
+
handler: o.resolve("./runtime/server/proxy.post.ts")
|
|
20
|
+
}), t(o.resolve("./runtime/plugins/i18n.ts"));
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
//#endregion
|
|
24
|
-
export {
|
|
24
|
+
export { a as default };
|
|
25
25
|
|
|
26
26
|
//# sourceMappingURL=module.js.map
|
package/dist/module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","names":[],"sources":["../src/module.ts"],"sourcesContent":["import { defineNuxtModule, createResolver, addComponent, addServerHandler } from '@nuxt/kit'\n\nexport default defineNuxtModule({\n meta: { name: '@mirodan/ai-insight' },\n defaults: { endpoint: 'https://api.mirodan.ai/v1/insight' },\n setup(options, nuxt) {\n const resolver = createResolver(import.meta.url)\n\n nuxt.options.runtimeConfig.aiInsight = {\n key: '',\n endpoint: options.endpoint\n }\n\n addComponent({\n name: 'AiInsight',\n filePath: resolver.resolve('./runtime/components/AiInsight.vue')\n })\n\n addComponent({\n name: 'AiInsightDialog',\n filePath: resolver.resolve('./runtime/components/AiInsightDialog.vue')\n })\n\n addServerHandler({\n route: '/api/ai-insight/proxy',\n handler: resolver.resolve('./runtime/server/proxy.post.ts')\n })\n }\n})\n"],"mappings":";;AAEA,IAAA,IAAe,EAAiB;CAC9B,MAAM,EAAE,MAAM,sBAAsB;CACpC,UAAU,EAAE,UAAU,oCAAoC;CAC1D,MAAM,GAAS,GAAM;EACnB,IAAM,IAAW,EAAe,YAAY,GAAG;
|
|
1
|
+
{"version":3,"file":"module.js","names":[],"sources":["../src/module.ts"],"sourcesContent":["import { defineNuxtModule, createResolver, addComponent, addServerHandler, addPlugin } from '@nuxt/kit'\n\nexport default defineNuxtModule({\n meta: { name: '@mirodan/ai-insight' },\n defaults: { endpoint: 'https://api.mirodan.ai/v1/insight' },\n setup(options, nuxt) {\n const resolver = createResolver(import.meta.url)\n\n nuxt.options.runtimeConfig.aiInsight = {\n key: '',\n endpoint: options.endpoint\n }\n\n addComponent({\n name: 'AiInsight',\n filePath: resolver.resolve('./runtime/components/AiInsight.vue')\n })\n\n addComponent({\n name: 'AiInsightDialog',\n filePath: resolver.resolve('./runtime/components/AiInsightDialog.vue')\n })\n\n addServerHandler({\n route: '/api/ai-insight/proxy',\n handler: resolver.resolve('./runtime/server/proxy.post.ts')\n })\n\n addPlugin(resolver.resolve('./runtime/plugins/i18n.ts'))\n }\n})\n"],"mappings":";;AAEA,IAAA,IAAe,EAAiB;CAC9B,MAAM,EAAE,MAAM,sBAAsB;CACpC,UAAU,EAAE,UAAU,oCAAoC;CAC1D,MAAM,GAAS,GAAM;EACnB,IAAM,IAAW,EAAe,YAAY,GAAG;EAsB/C,AApBA,EAAK,QAAQ,cAAc,YAAY;GACrC,KAAK;GACL,UAAU,EAAQ;EACpB,GAEA,EAAa;GACX,MAAM;GACN,UAAU,EAAS,QAAQ,oCAAoC;EACjE,CAAC,GAED,EAAa;GACX,MAAM;GACN,UAAU,EAAS,QAAQ,0CAA0C;EACvE,CAAC,GAED,EAAiB;GACf,OAAO;GACP,SAAS,EAAS,QAAQ,gCAAgC;EAC5D,CAAC,GAED,EAAU,EAAS,QAAQ,2BAA2B,CAAC;CACzD;AACF,CAAC"}
|
|
@@ -16,7 +16,7 @@ const dialogOpen = ref(false)
|
|
|
16
16
|
<slot />
|
|
17
17
|
<div
|
|
18
18
|
v-if="!dialogOpen"
|
|
19
|
-
class="absolute z-10 flex items-center gap-1 rounded-md bg-muted px-2 py-1 text-xs text-muted
|
|
19
|
+
class="absolute z-10 flex items-center gap-1 rounded-md bg-muted px-2 py-1 text-xs text-muted cursor-pointer transition-opacity hover:opacity-80"
|
|
20
20
|
:class="position === 'top' ? 'bottom-full left-1/2 -translate-x-1/2 mb-2' : 'top-full left-1/2 -translate-x-1/2 mt-2'"
|
|
21
21
|
@click="dialogOpen = true"
|
|
22
22
|
>
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, watch, onMounted, nextTick } from 'vue'
|
|
3
3
|
import { useDraggable } from '@vueuse/core'
|
|
4
|
-
import {
|
|
4
|
+
import { useI18n } from 'vue-i18n'
|
|
5
|
+
import { parseUIMessageStream } from '../utils/parseUIMessageStream'
|
|
6
|
+
|
|
7
|
+
const { t, locale: i18nLocale } = useI18n()
|
|
5
8
|
|
|
6
9
|
interface Props {
|
|
7
10
|
open: boolean
|
|
@@ -62,7 +65,7 @@ async function loadInsight() {
|
|
|
62
65
|
followUpMessages.value = []
|
|
63
66
|
currentPrompt.value = ''
|
|
64
67
|
|
|
65
|
-
const locale = props.locale || navigator.language
|
|
68
|
+
const locale = props.locale || i18nLocale?.value || navigator.language
|
|
66
69
|
|
|
67
70
|
try {
|
|
68
71
|
const response = await fetch('/api/ai-insight/proxy', {
|
|
@@ -76,8 +79,15 @@ async function loadInsight() {
|
|
|
76
79
|
})
|
|
77
80
|
|
|
78
81
|
if (!response.ok) {
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
let msg = t('aiInsight.requestError', { status: response.status })
|
|
83
|
+
try {
|
|
84
|
+
const json = await response.json()
|
|
85
|
+
msg = json.statusMessage || json.message || msg
|
|
86
|
+
} catch {
|
|
87
|
+
const text = await response.text().catch(() => '')
|
|
88
|
+
if (text) msg = text
|
|
89
|
+
}
|
|
90
|
+
throw new Error(msg)
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
let result = ''
|
|
@@ -86,7 +96,7 @@ async function loadInsight() {
|
|
|
86
96
|
insight.value = result
|
|
87
97
|
}
|
|
88
98
|
} catch (e) {
|
|
89
|
-
errorMsg.value = e instanceof Error ? e.message : '
|
|
99
|
+
errorMsg.value = e instanceof Error ? e.message : t('aiInsight.unknownError')
|
|
90
100
|
} finally {
|
|
91
101
|
loading.value = false
|
|
92
102
|
}
|
|
@@ -123,16 +133,16 @@ watch(() => props.open, (val) => {
|
|
|
123
133
|
<div
|
|
124
134
|
v-if="open"
|
|
125
135
|
ref="panelEl"
|
|
126
|
-
class="ai-insight-panel fixed z-50 flex flex-col rounded-lg border bg-
|
|
136
|
+
class="ai-insight-panel fixed z-50 flex flex-col rounded-lg border border-default bg-default text-default text-sm shadow-xl"
|
|
127
137
|
:class="{ 'cursor-grabbing select-none': isDragging, 'cursor-se-resize select-none': resizing }"
|
|
128
138
|
:style="{ left: `${x}px`, top: `${y}px`, width: `${panelWidth}px`, height: `${panelHeight}px` }"
|
|
129
139
|
>
|
|
130
140
|
<div
|
|
131
141
|
ref="headerEl"
|
|
132
|
-
class="flex cursor-grab items-center justify-between rounded-t-lg border-b px-4 py-3"
|
|
142
|
+
class="flex cursor-grab items-center justify-between rounded-t-lg border-b border-default px-4 py-3"
|
|
133
143
|
>
|
|
134
144
|
<h3 class="text-sm font-medium">
|
|
135
|
-
{{ title || '
|
|
145
|
+
{{ title || t('aiInsight.title') }}
|
|
136
146
|
</h3>
|
|
137
147
|
<div class="flex items-center gap-1">
|
|
138
148
|
<UButton
|
|
@@ -156,13 +166,13 @@ watch(() => props.open, (val) => {
|
|
|
156
166
|
<div class="space-y-4">
|
|
157
167
|
<div
|
|
158
168
|
v-if="loading && !insight"
|
|
159
|
-
class="flex items-center gap-2 text-sm text-muted
|
|
169
|
+
class="flex items-center gap-2 text-sm text-muted"
|
|
160
170
|
>
|
|
161
171
|
<UIcon
|
|
162
172
|
name="i-lucide-loader-2"
|
|
163
173
|
class="size-4 animate-spin"
|
|
164
174
|
/>
|
|
165
|
-
<span>
|
|
175
|
+
<span>{{ t('aiInsight.analyzing') }}</span>
|
|
166
176
|
</div>
|
|
167
177
|
|
|
168
178
|
<div
|
|
@@ -185,7 +195,7 @@ watch(() => props.open, (val) => {
|
|
|
185
195
|
class="mt-2"
|
|
186
196
|
@click="refresh"
|
|
187
197
|
>
|
|
188
|
-
|
|
198
|
+
{{ t('aiInsight.retry') }}
|
|
189
199
|
</UButton>
|
|
190
200
|
</div>
|
|
191
201
|
</div>
|
|
@@ -200,46 +210,46 @@ watch(() => props.open, (val) => {
|
|
|
200
210
|
|
|
201
211
|
<div
|
|
202
212
|
v-else
|
|
203
|
-
class="text-sm text-muted
|
|
213
|
+
class="text-sm text-muted"
|
|
204
214
|
>
|
|
205
|
-
{{ loading ? '
|
|
215
|
+
{{ loading ? t('aiInsight.analyzing') : t('aiInsight.noData') }}
|
|
206
216
|
</div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
207
219
|
|
|
220
|
+
<div
|
|
221
|
+
v-if="insight && !loading"
|
|
222
|
+
class="shrink-0 border-t border-default px-4 py-3"
|
|
223
|
+
>
|
|
224
|
+
<div class="flex flex-col items-center space-y-2">
|
|
208
225
|
<div
|
|
209
|
-
v-
|
|
210
|
-
|
|
226
|
+
v-for="(msg, i) in followUpMessages"
|
|
227
|
+
:key="i"
|
|
228
|
+
:class="msg.role === 'user' ? 'text-right self-end' : 'text-left self-start'"
|
|
211
229
|
>
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
<span
|
|
219
|
-
class="inline-block rounded-lg px-3 py-2 text-sm"
|
|
220
|
-
:class="msg.role === 'user' ? 'bg-primary text-primary-foreground' : 'bg-muted'"
|
|
221
|
-
>
|
|
222
|
-
{{ msg.content }}
|
|
223
|
-
</span>
|
|
224
|
-
</div>
|
|
225
|
-
<UInput
|
|
226
|
-
v-model="followUpInput"
|
|
227
|
-
placeholder="Ask a follow-up..."
|
|
228
|
-
class="max-w-md w-full"
|
|
229
|
-
size="lg"
|
|
230
|
-
@keydown.enter="sendFollowUp"
|
|
231
|
-
>
|
|
232
|
-
<template #trailing>
|
|
233
|
-
<UButton
|
|
234
|
-
icon="i-lucide-send"
|
|
235
|
-
size="xs"
|
|
236
|
-
:disabled="!followUpInput.trim() || loading"
|
|
237
|
-
@click="sendFollowUp"
|
|
238
|
-
/>
|
|
239
|
-
</template>
|
|
240
|
-
</UInput>
|
|
241
|
-
</div>
|
|
230
|
+
<span
|
|
231
|
+
class="inline-block rounded-lg px-3 py-1.5 text-sm"
|
|
232
|
+
:class="msg.role === 'user' ? 'bg-primary text-white' : 'bg-muted'"
|
|
233
|
+
>
|
|
234
|
+
{{ msg.content }}
|
|
235
|
+
</span>
|
|
242
236
|
</div>
|
|
237
|
+
<UInput
|
|
238
|
+
v-model="followUpInput"
|
|
239
|
+
:placeholder="t('aiInsight.askQuestion')"
|
|
240
|
+
class="max-w-md w-full"
|
|
241
|
+
size="sm"
|
|
242
|
+
@keydown.enter="sendFollowUp"
|
|
243
|
+
>
|
|
244
|
+
<template #trailing>
|
|
245
|
+
<UButton
|
|
246
|
+
icon="i-lucide-send"
|
|
247
|
+
size="xs"
|
|
248
|
+
:disabled="!followUpInput.trim() || loading"
|
|
249
|
+
@click="sendFollowUp"
|
|
250
|
+
/>
|
|
251
|
+
</template>
|
|
252
|
+
</UInput>
|
|
243
253
|
</div>
|
|
244
254
|
</div>
|
|
245
255
|
|
|
@@ -253,4 +263,8 @@ watch(() => props.open, (val) => {
|
|
|
253
263
|
<style scoped>
|
|
254
264
|
.ai-insight-panel { min-height: 200px; }
|
|
255
265
|
.ai-insight-panel :deep(.prose) { word-break: break-word; }
|
|
266
|
+
.ai-insight-panel :deep(.prose) h1 { font-size: 1rem; font-weight: 600; }
|
|
267
|
+
.ai-insight-panel :deep(.prose) h2 { font-size: 0.9375rem; font-weight: 600; }
|
|
268
|
+
.ai-insight-panel :deep(.prose) h3 { font-size: 0.875rem; font-weight: 600; }
|
|
269
|
+
.ai-insight-panel :deep(.prose) h4 { font-size: 0.8125rem; font-weight: 600; }
|
|
256
270
|
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const messages = {
|
|
2
|
+
ru: {
|
|
3
|
+
aiInsight: {
|
|
4
|
+
title: 'AI анализ',
|
|
5
|
+
analyzing: 'Анализирую данные...',
|
|
6
|
+
noData: 'Нет данных для анализа',
|
|
7
|
+
askQuestion: 'Задать вопрос ИИ...',
|
|
8
|
+
retry: 'Повторить',
|
|
9
|
+
requestError: 'Ошибка запроса ({status})',
|
|
10
|
+
unknownError: 'Неизвестная ошибка'
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
en: {
|
|
14
|
+
aiInsight: {
|
|
15
|
+
title: 'AI Analysis',
|
|
16
|
+
analyzing: 'Analyzing data...',
|
|
17
|
+
noData: 'No data to analyze',
|
|
18
|
+
askQuestion: 'Ask the AI...',
|
|
19
|
+
retry: 'Retry',
|
|
20
|
+
requestError: 'Request failed ({status})',
|
|
21
|
+
unknownError: 'Unknown error'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from '#imports'
|
|
2
|
+
import { messages } from '../i18n/messages'
|
|
3
|
+
|
|
4
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
const i18n: any = nuxtApp.vueApp.config.globalProperties.$i18n
|
|
7
|
+
if (i18n?.mergeLocaleMessage) {
|
|
8
|
+
for (const [locale, msg] of Object.entries(messages)) {
|
|
9
|
+
i18n.mergeLocaleMessage(locale, msg)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const messages = {
|
|
2
|
+
ru: {
|
|
3
|
+
CONFIG_ENDPOINT_MISSING: 'Не указан endpoint AI Insight',
|
|
4
|
+
CONFIG_KEY_MISSING: 'Не указан API-ключ AI Insight',
|
|
5
|
+
BACKEND_UNREACHABLE: 'Не удалось подключиться к AI Insight',
|
|
6
|
+
BACKEND_ERROR: 'Ошибка AI Insight'
|
|
7
|
+
},
|
|
8
|
+
en: {
|
|
9
|
+
CONFIG_ENDPOINT_MISSING: 'AI Insight endpoint is not configured',
|
|
10
|
+
CONFIG_KEY_MISSING: 'AI Insight API key is not configured',
|
|
11
|
+
BACKEND_UNREACHABLE: 'Failed to reach AI Insight backend',
|
|
12
|
+
BACKEND_ERROR: 'AI Insight error'
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function tError(key, lang) {
|
|
17
|
+
return messages[lang === 'en' ? 'en' : 'ru']?.[key] || key
|
|
18
|
+
}
|
|
@@ -1,18 +1,55 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { tError } from './_errors.js'
|
|
2
2
|
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
const config = useRuntimeConfig(event)
|
|
5
5
|
const body = await readBody(event)
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
const endpoint = config.aiInsight.endpoint
|
|
8
|
+
const hasKey = !!config.aiInsight.key
|
|
9
|
+
const lang = getHeader(event, 'accept-language')?.slice(0, 2) || 'ru'
|
|
10
|
+
|
|
11
|
+
if (!endpoint) {
|
|
12
|
+
throw createError({ statusCode: 500, statusMessage: tError('CONFIG_ENDPOINT_MISSING', lang) })
|
|
13
|
+
}
|
|
14
|
+
if (!hasKey) {
|
|
15
|
+
throw createError({ statusCode: 500, statusMessage: tError('CONFIG_KEY_MISSING', lang) })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log('[ai-insight/proxy] →', endpoint, 'key:', hasKey ? 'yes' : 'NO KEY', 'data:', typeof body?.data, 'prompt:', body?.prompt?.slice(0, 80))
|
|
19
|
+
|
|
20
|
+
const origin = getHeader(event, 'origin') || getHeader(event, 'referer') || ''
|
|
21
|
+
|
|
22
|
+
let response
|
|
23
|
+
try {
|
|
24
|
+
response = await $fetch.raw(endpoint, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${config.aiInsight.key}`,
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
...(origin ? { Origin: origin } : {})
|
|
30
|
+
},
|
|
31
|
+
body,
|
|
32
|
+
responseType: 'stream',
|
|
33
|
+
ignoreResponseError: true
|
|
34
|
+
})
|
|
35
|
+
} catch (fetchErr) {
|
|
36
|
+
console.error('[ai-insight/proxy] fetch error:', fetchErr)
|
|
37
|
+
throw createError({ statusCode: 502, statusMessage: tError('BACKEND_UNREACHABLE', lang) })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log('[ai-insight/proxy] ←', response.status, response.statusText)
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const text = await response.text().catch(() => '')
|
|
44
|
+
let detail
|
|
45
|
+
try {
|
|
46
|
+
const json = JSON.parse(text)
|
|
47
|
+
detail = json.message || json.statusMessage || text
|
|
48
|
+
} catch {
|
|
49
|
+
detail = text || response.statusText
|
|
50
|
+
}
|
|
51
|
+
throw createError({ statusCode: response.status, statusMessage: detail || tError('BACKEND_ERROR', lang) })
|
|
52
|
+
}
|
|
16
53
|
|
|
17
54
|
setHeaders(event, {
|
|
18
55
|
'Content-Type': 'text/event-stream',
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export async function* parseUIMessageStream(response: Response) {
|
|
2
|
+
const reader = response.body!.getReader()
|
|
3
|
+
const decoder = new TextDecoder()
|
|
4
|
+
let buffer = ''
|
|
5
|
+
|
|
6
|
+
while (true) {
|
|
7
|
+
const { done, value } = await reader.read()
|
|
8
|
+
if (done) break
|
|
9
|
+
|
|
10
|
+
buffer += decoder.decode(value, { stream: true })
|
|
11
|
+
const lines = buffer.split('\n')
|
|
12
|
+
buffer = lines.pop() || ''
|
|
13
|
+
|
|
14
|
+
for (const line of lines) {
|
|
15
|
+
if (line.startsWith('data: ')) {
|
|
16
|
+
const data = line.slice(6).trim()
|
|
17
|
+
if (data === '[DONE]') return
|
|
18
|
+
try {
|
|
19
|
+
const chunk = JSON.parse(data)
|
|
20
|
+
if (chunk.type === 'text-delta' && chunk.delta != null) {
|
|
21
|
+
yield chunk.delta
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
console.warn('[parseUIMessageStream] parse error:', data)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mirodan/ai-insight",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "AI-powered insight tooltip component for
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "AI-powered insight tooltip component for Mirodan",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/module.js",
|
|
7
7
|
"types": "./dist/module.d.ts",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"typescript": "^5.9.3",
|
|
36
36
|
"typescript-eslint": "^8.65.0",
|
|
37
37
|
"vite": "^8.2.0",
|
|
38
|
-
"vitest": "^3.1.2"
|
|
38
|
+
"vitest": "^3.1.2",
|
|
39
|
+
"vue-i18n": "^11.4.6"
|
|
39
40
|
},
|
|
40
41
|
"peerDependencies": {
|
|
41
42
|
"@comark/nuxt": "^0.4.0",
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"@nuxt/ui": "^4.0.0",
|
|
45
46
|
"nuxt": "^4.0.0",
|
|
46
47
|
"tailwindcss": "^4.0.0",
|
|
47
|
-
"vue": "^3.0.0"
|
|
48
|
+
"vue": "^3.0.0",
|
|
49
|
+
"vue-i18n": "^11.0.0"
|
|
48
50
|
}
|
|
49
51
|
}
|