@8wave/ai-elements 0.119.0-beta.12 → 0.119.0-beta.15

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 (28) hide show
  1. package/dist/_chunks/{PkToolShowContactForm-CQQ6p4bJ.js → PkToolShowContactForm--9XOVqNB.js} +4 -4
  2. package/dist/_chunks/PkToolShowContactForm--9XOVqNB.js.map +1 -0
  3. package/dist/ai-elements.es.js +33 -28
  4. package/dist/ai-elements.es.js.map +1 -1
  5. package/dist-vue/PkChatbot.js +1 -1
  6. package/dist-vue/PkChatbotMessages.js +1 -1
  7. package/dist-vue/PkChatbotViewChat.js +1 -1
  8. package/dist-vue/_chunks/{PkChatbot-CcNC2r_E.js → PkChatbot-UzdAjTRl.js} +2 -2
  9. package/dist-vue/_chunks/PkChatbot-UzdAjTRl.js.map +1 -0
  10. package/dist-vue/_chunks/PkChatbotInput-BfZqRpQ2.js.map +1 -1
  11. package/dist-vue/_chunks/{PkChatbotMessages-B6qYZsMZ.js → PkChatbotMessages-D9uf0NMI.js} +3 -3
  12. package/dist-vue/_chunks/{PkChatbotMessages-B6qYZsMZ.js.map → PkChatbotMessages-D9uf0NMI.js.map} +1 -1
  13. package/dist-vue/_chunks/{PkChatbotViewChat--Q90F_Kg.js → PkChatbotViewChat-B_7IdZIz.js} +3 -3
  14. package/dist-vue/_chunks/{PkChatbotViewChat--Q90F_Kg.js.map → PkChatbotViewChat-B_7IdZIz.js.map} +1 -1
  15. package/dist-vue/_chunks/{PkToolShowContactForm-BjNpnv6y.js → PkToolShowContactForm-Y0lrHXhr.js} +4 -4
  16. package/dist-vue/_chunks/PkToolShowContactForm-Y0lrHXhr.js.map +1 -0
  17. package/dist-vue/_chunks/{PkToolShowDiagram-CqkabP0e.js → PkToolShowDiagram-BJ_ryTsD.js} +2 -2
  18. package/dist-vue/_chunks/{PkToolShowDiagram-CqkabP0e.js.map → PkToolShowDiagram-BJ_ryTsD.js.map} +1 -1
  19. package/dist-vue/composables.js +4 -0
  20. package/dist-vue/composables.js.map +1 -1
  21. package/dist-vue/index.js +24 -36
  22. package/dist-vue/index.js.map +1 -1
  23. package/dist-vue/locales.js +32 -8
  24. package/dist-vue/style.css +1 -1
  25. package/package.json +1 -1
  26. package/dist/_chunks/PkToolShowContactForm-CQQ6p4bJ.js.map +0 -1
  27. package/dist-vue/_chunks/PkChatbot-CcNC2r_E.js.map +0 -1
  28. package/dist-vue/_chunks/PkToolShowContactForm-BjNpnv6y.js.map +0 -1
@@ -68,7 +68,7 @@ var C = { class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-s
68
68
  label: y(N)("label.name"),
69
69
  placeholder: y(N)("placeholder.name"),
70
70
  icon: "ri:user-line",
71
- modifiers: "compact no-label",
71
+ modifiers: "compact",
72
72
  class: "mb-md"
73
73
  }, null, 8, ["label", "placeholder"]),
74
74
  m(y(V), {
@@ -77,7 +77,7 @@ var C = { class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-s
77
77
  label: y(N)("label.email"),
78
78
  placeholder: y(N)("placeholder.email"),
79
79
  icon: "ri:mail-line",
80
- modifiers: "compact no-label",
80
+ modifiers: "compact",
81
81
  class: "mb-md"
82
82
  }, null, 8, ["label", "placeholder"]),
83
83
  m(y(V), {
@@ -87,7 +87,7 @@ var C = { class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-s
87
87
  placeholder: y(N)("placeholder.phone"),
88
88
  icon: "ri:phone-line",
89
89
  class: "mb-md",
90
- modifiers: "compact no-label"
90
+ modifiers: "compact"
91
91
  }, null, 8, ["label", "placeholder"]),
92
92
  p("div", D, [p("div", O, [m(r, {
93
93
  name: "ri:shield-check-line",
@@ -119,4 +119,4 @@ var C = { class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-s
119
119
  //#endregion
120
120
  export { P as n, F as t };
121
121
 
122
- //# sourceMappingURL=PkToolShowContactForm-BjNpnv6y.js.map
122
+ //# sourceMappingURL=PkToolShowContactForm-Y0lrHXhr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PkToolShowContactForm-Y0lrHXhr.js","names":["z.object","z.string","z.email","useForm","__unplugin_components_0","__unplugin_components_1","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_Transition","_withCtx","_isRef","PkStreamingMarkdown","_sfc_main","__exportAll","string","email","object","PkStreamingMarkdown_default","useSafeLocalStorage","useLocalizedString","Se","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","PkToolShowContactForm_vue_vue_type_script_setup_true_lang_default","__emit","$t","useI18n","props","__props","toolPart","computed","input","localSubmitted","LeadFormSchema","emit","watch","newInput","submitted","isSubmitted","submitLead","data","privacyNotice","_component_VvIcon","VvIcon","_component_VvButton","VvButton","openBlock","createElementBlock","createElementVNode","createVNode","toDisplayString","unref","Transition","withCtx","VvForm","formData","_cache","isRef","$event","VvFormField","PkToolShowContactForm_exports","PkToolShowContactForm_default"],"sources":["../../../../packages/components/src/chat/PkToolShowContactForm.vue","../../../../packages/components/src/chat/PkToolShowContactForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact\" />\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a green\n check, the title in weight, the body quiet. The check is\n the outcome, so it alone carries the colour (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import { useForm } from '@volverjs/form-vue'\n import * as z from 'zod'\n import { useSafeLocalStorage, useLocalizedString } from 'composables'\n import type { LocalizedString } from 'models'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n\n const { t: $t } = useI18n({ useScope: 'global' })\n\n const props = defineProps<{\n part: unknown\n submitted?: boolean\n loading?: boolean\n error?: string\n readonly?: boolean\n privacyPolicyNotice?: LocalizedString\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n type: string\n toolCallId: string\n input?: {\n name: string\n email: string\n phone?: string\n conversationContext?: Record<string, unknown>\n }\n }\n return part\n })\n\n const input = computed(() => toolPart.value.input)\n\n const localSubmitted = useSafeLocalStorage<boolean>(\n computed(\n () =>\n `${toolPart.value.toolCallId}-${toolPart.value.type}-submitted`,\n ),\n false,\n )\n\n const LeadFormSchema = z.object({\n name: z.string().min(1).max(255).default(''),\n email: z.email().max(255).default(''),\n phone: z\n .string()\n .max(50)\n .regex(/^[+0-9\\s\\-.()]*$/, $t('validation.invalidPhoneNumber'))\n .optional(),\n })\n\n const emit = defineEmits<{\n submit: [data: Record<string, unknown>]\n }>()\n\n const { VvForm, VvFormField, formData } = useForm(LeadFormSchema, {\n lazyLoad: true,\n })\n\n // Pre-populate form with input values\n watch(\n input,\n (newInput) => {\n formData.value = {\n name: newInput?.name || '',\n email: newInput?.email || '',\n phone: newInput?.phone || '',\n }\n },\n { immediate: true },\n )\n\n watch(\n () => props.submitted,\n (submitted) => {\n if (submitted) {\n localSubmitted.value = true\n }\n },\n { immediate: true },\n )\n\n const isSubmitted = computed(() => {\n return props.submitted || localSubmitted.value\n })\n\n const submitLead = async (data: Record<string, unknown>): Promise<void> => {\n emit('submit', { ...data, metadata: input.value?.conversationContext })\n }\n\n const privacyNotice = useLocalizedString(\n () => props.privacyPolicyNotice,\n 'message.defaultPrivacyPolicyNotice',\n )\n</script>\n\n<template>\n <div\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden bg-surface\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:send-plane-2-line\" class=\"text-16\" />\n <strong class=\"font-bold\">{{ $t('label.contactUs') }}</strong>\n </div>\n\n <Transition mode=\"out-in\">\n <div v-if=\"!isSubmitted\" class=\"p-sm\">\n <VvForm v-model=\"formData\" :readonly @submit=\"submitLead\">\n <VvFormField\n name=\"name\"\n type=\"text\"\n :label=\"$t('label.name')\"\n :placeholder=\"$t('placeholder.name')\"\n icon=\"ri:user-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"email\"\n type=\"email\"\n :label=\"$t('label.email')\"\n :placeholder=\"$t('placeholder.email')\"\n icon=\"ri:mail-line\"\n modifiers=\"compact\"\n class=\"mb-md\" />\n <VvFormField\n name=\"phone\"\n type=\"tel\"\n :label=\"$t('label.phone')\"\n :placeholder=\"$t('placeholder.phone')\"\n icon=\"ri:phone-line\"\n class=\"mb-md\"\n modifiers=\"compact\" />\n\n <div class=\"flex justify-between items-center gap-16 mt-xs\">\n <div\n class=\"text-smaller text-word-3 flex items-center gap-8\">\n <VvIcon\n name=\"ri:shield-check-line\"\n class=\"text-16 shrink-0\" />\n <PkStreamingMarkdown\n :markdown=\"privacyNotice\"\n class=\"wysiwyg\" />\n </div>\n <VvButton\n type=\"submit\"\n modifiers=\"primary\"\n :loading\n :disabled=\"loading || readonly\"\n icon-position=\"right\"\n class=\"shrink-0\"\n :label=\"$t('action.submit')\" />\n </div>\n </VvForm>\n </div>\n <div v-else class=\"p-sm\">\n <!-- One confirmation row across every tool card: a green\n check, the title in weight, the body quiet. The check is\n the outcome, so it alone carries the colour (DESIGN.md). -->\n <div class=\"flex items-center gap-sm\">\n <VvIcon name=\"ri:check-line\" class=\"text-16 text-success\" />\n <div>\n <strong class=\"font-medium block text-word-1\">\n {{ $t('message.leadSubmittedTitle') }}\n </strong>\n <p class=\"text-word-3\">\n {{ $t('message.leadSubmittedMessage') }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as e } from "./PkChatbotMessages-B6qYZsMZ.js";
1
+ import { a as e } from "./PkChatbotMessages-D9uf0NMI.js";
2
2
  import "./src-DCfvejyT.js";
3
3
  import { t } from "./PkSvgViewer-Ts3gQ1hB.js";
4
4
  import { VvButton as n, VvButtonGroup as r, VvIcon as i } from "@volverjs/ui-vue/components";
@@ -119,4 +119,4 @@ var x = {
119
119
  //#endregion
120
120
  export { k as default };
121
121
 
122
- //# sourceMappingURL=PkToolShowDiagram-CqkabP0e.js.map
122
+ //# sourceMappingURL=PkToolShowDiagram-BJ_ryTsD.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowDiagram-CqkabP0e.js","names":["__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_withCtx","_createBlock","_createCommentVNode","PkSvgViewer","_sfc_main","toSnakeCase","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","DiagramType","\"Sequence\"","\"Class\"","\"State\"","\"ER\"","\"XYChart\"","\"Flowchart\"","\"Graph\"","PkToolShowDiagram_default","defineComponent","props","__props","$t","$te","useI18n","useClipboard","toolPart","computed","svgError","ref","svgContent","diagramType","code","diagramTypeLabel","diagramTypeIcon","renderDiagram","theme","THEMES","renderMermaidSVG","watch","onMounted","downloadSvg","blob","url","anchor","copyCode","copy","_component_VvIcon","VvIcon","_component_VvButton","VvButton","_component_VvButtonGroup","VvButtonGroup","openBlock","createElementBlock","createElementVNode","createVNode","toDisplayString","unref","withCtx","copied","createBlock","createCommentVNode","PkSvgViewer_default"],"sources":["../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"PkToolShowDiagram-BJ_ryTsD.js","names":["__unplugin_components_0","__unplugin_components_1","__unplugin_components_2","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_unref","_withCtx","_createBlock","_createCommentVNode","PkSvgViewer","_sfc_main","toSnakeCase","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","DiagramType","\"Sequence\"","\"Class\"","\"State\"","\"ER\"","\"XYChart\"","\"Flowchart\"","\"Graph\"","PkToolShowDiagram_default","defineComponent","props","__props","$t","$te","useI18n","useClipboard","toolPart","computed","svgError","ref","svgContent","diagramType","code","diagramTypeLabel","diagramTypeIcon","renderDiagram","theme","THEMES","renderMermaidSVG","watch","onMounted","downloadSvg","blob","url","anchor","copyCode","copy","_component_VvIcon","VvIcon","_component_VvButton","VvButton","_component_VvButtonGroup","VvButtonGroup","openBlock","createElementBlock","createElementVNode","createVNode","toDisplayString","unref","withCtx","copied","createBlock","createCommentVNode","PkSvgViewer_default"],"sources":["../../../../packages/components/src/chat/PkToolShowDiagram.vue","../../../../packages/components/src/chat/PkToolShowDiagram.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, onMounted, ref, watch } from 'vue'\n import { useClipboard } from '@vueuse/core'\n import { useI18n } from 'vue-i18n'\n import { toSnakeCase } from 'utils'\n import PkSvgViewer from '../PkSvgViewer.vue'\n\n const props = defineProps<{\n part: unknown\n isDark?: boolean\n }>()\n\n const { t: $t, te: $te } = useI18n({\n useScope: 'global',\n })\n\n const { copy, copied } = useClipboard()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n code: string\n title?: string\n caption?: string\n }\n }\n return part\n })\n\n const svgError = ref(false)\n\n // Serialized SVG content\n const svgContent = ref('')\n\n enum DiagramType {\n Sequence = 'sequenceDiagram',\n Class = 'classDiagram',\n State = 'stateDiagram',\n ER = 'erDiagram',\n XYChart = 'xychart',\n Flowchart = 'flowchart',\n Graph = 'graph',\n }\n\n const diagramType = computed<DiagramType | undefined>(() => {\n const code = toolPart.value.input?.code?.trimStart()\n if (!code) {\n return undefined\n }\n const firstWord = code.match(/^[A-Za-z-]+/)?.[0]\n switch (firstWord) {\n case 'sequenceDiagram':\n return DiagramType.Sequence\n case 'classDiagram':\n return DiagramType.Class\n case 'stateDiagram':\n return DiagramType.State\n case 'erDiagram':\n return DiagramType.ER\n case 'xychart-beta':\n return DiagramType.XYChart\n case 'flowchart':\n return DiagramType.Flowchart\n case 'graph':\n return DiagramType.Graph\n default:\n return undefined\n }\n })\n\n const diagramTypeLabel = computed(() => {\n if (!diagramType.value) {\n return ''\n }\n return $te(`diagram.${diagramType.value}`)\n ? $t(`diagram.${diagramType.value}`)\n : diagramType.value\n })\n\n const diagramTypeIcon = computed(() => {\n switch (diagramType.value) {\n case DiagramType.Sequence:\n return 'ri:timeline-view'\n case DiagramType.Class:\n return 'ri:organization-chart'\n case DiagramType.State:\n return 'ri:organization-chart'\n case DiagramType.ER:\n return 'ri:organization-chart'\n case DiagramType.XYChart:\n return 'ri:line-chart-line'\n case DiagramType.Flowchart:\n return 'ri:flow-chart'\n case DiagramType.Graph:\n return 'ri:flow-chart'\n default:\n return 'ri:flow-chart'\n }\n })\n\n async function renderDiagram() {\n const code = toolPart.value.input?.code\n if (!code) {\n return\n }\n\n svgError.value = false\n try {\n const { renderMermaidSVG, THEMES } =\n await import('beautiful-mermaid')\n const theme = props.isDark\n ? THEMES['github-dark']\n : THEMES['github-light']\n svgContent.value = renderMermaidSVG(code, theme)\n } catch {\n svgError.value = true\n }\n }\n\n watch([() => toolPart.value.input?.code, () => props.isDark], renderDiagram)\n onMounted(async () => {\n await renderDiagram()\n })\n\n const downloadSvg = () => {\n if (!svgContent.value) {\n return\n }\n const blob = new Blob([svgContent.value], {\n type: 'image/svg+xml;charset=utf-8',\n })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${toSnakeCase(toolPart.value.input?.title || 'diagram')}.svg`\n document.body.appendChild(anchor)\n anchor.click()\n document.body.removeChild(anchor)\n URL.revokeObjectURL(url)\n }\n\n const copyCode = () => {\n const code = toolPart.value.input?.code\n if (code) {\n copy(code)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"toolPart?.input?.code\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden\">\n <div\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8\">\n <VvIcon :name=\"diagramTypeIcon\" class=\"text-16\" />\n <div class=\"flex flex-col items-start gap-2\">\n <strong class=\"font-bold\">{{\n diagramTypeLabel ?? $t('label.diagram')\n }}</strong>\n </div>\n <VvButtonGroup modifiers=\"compact\" class=\"ml-auto shrink-0\">\n <VvButton\n modifiers=\"action-quiet-small\"\n :icon=\"copied ? 'ri:check-line' : 'ri:file-copy-line'\"\n :label=\"copied ? $t('action.copied') : $t('action.copy')\"\n @click=\"copyCode\" />\n <VvButton\n v-if=\"svgContent\"\n modifiers=\"action-quiet-small\"\n icon=\"ri:download-line\"\n :label=\"$t('action.download')\"\n @click=\"downloadSvg\" />\n </VvButtonGroup>\n </div>\n <div v-if=\"svgError\" class=\"text-12 text-danger py-8 px-sm\">\n {{ $t('message.diagramRenderError') }}\n </div>\n <PkSvgViewer\n v-else-if=\"svgContent\"\n class=\"pk-tool-show-diagram__stage\"\n :svg=\"svgContent\"\n :title=\"toolPart.input?.title\" />\n <div\n v-else\n class=\"pk-tool-show-diagram__stage flex justify-center items-center\">\n <VvIcon name=\"loading-loop\" class=\"w-24 h-24 text-word-5\" />\n </div>\n <div\n v-if=\"toolPart.input.caption\"\n class=\"px-sm py-6 bg-surface-1 min-h-40 text-12 border-t border-surface-3 text-word-3\">\n {{ toolPart.input.caption }}\n </div>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-show-diagram {\n // Fixed stage height: the diagram renders lazily (async chunk +\n // dynamic mermaid import), so the loader and the rendered SVG must\n // occupy the same space or the chat scroll position jumps around.\n // The SVG scales to fit; zoom/pan and the lightbox cover the rest.\n &__stage {\n height: 384px;\n\n .pk-svg-viewer__stage,\n .pk-svg-viewer__content {\n height: 100%;\n }\n }\n }\n</style>\n"],"mappings":"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"}
@@ -125,6 +125,7 @@ var j = {
125
125
  clear: "Pulisci",
126
126
  clearSearch: "Cancella ricerca",
127
127
  close: "Chiudi",
128
+ closeChat: "Chiudi la chat",
128
129
  closeSidebar: "Chiudi la sidebar",
129
130
  collapse: "Collassa",
130
131
  compare: "Confronta",
@@ -220,6 +221,7 @@ var j = {
220
221
  mute: "Disattiva il microfono",
221
222
  next: "Avanti",
222
223
  open: "Apri",
224
+ openChat: "Apri la chat",
223
225
  openFilters: "Apri i filtri",
224
226
  openInGmail: "Apri in Gmail",
225
227
  openInMailApp: "Apri con l'app di posta",
@@ -1766,6 +1768,7 @@ var j = {
1766
1768
  clear: "Clear",
1767
1769
  clearSearch: "Clear search",
1768
1770
  close: "Close",
1771
+ closeChat: "Close the chat",
1769
1772
  closeSidebar: "Close sidebar",
1770
1773
  collapse: "Collapse",
1771
1774
  compare: "Compare",
@@ -1861,6 +1864,7 @@ var j = {
1861
1864
  mute: "Mute the microphone",
1862
1865
  next: "Next",
1863
1866
  open: "Open",
1867
+ openChat: "Open the chat",
1864
1868
  openFilters: "Open the filters",
1865
1869
  openInGmail: "Open in Gmail",
1866
1870
  openInMailApp: "Open in mail app",