@8wave/ai-elements 0.115.0-beta.34 → 0.115.0-beta.47
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/_chunks/{Agent-Cu04vBnd.js → Agent-D3cVY0pk.js} +121 -110
- package/dist/_chunks/Agent-D3cVY0pk.js.map +1 -0
- package/dist/_chunks/PkHeldImage-BtaPh0Jr.js +42 -0
- package/dist/_chunks/PkHeldImage-BtaPh0Jr.js.map +1 -0
- package/dist/_chunks/{PkStreamingMarkdown-Qk9Tk_N7.js → PkStreamingMarkdown-DyvZ5xKK.js} +782 -741
- package/dist/_chunks/PkStreamingMarkdown-DyvZ5xKK.js.map +1 -0
- package/dist/_chunks/{PkToolRequestGeolocation-BoKYkQ-Z.js → PkToolRequestGeolocation-DjRmmYMk.js} +2 -2
- package/dist/_chunks/{PkToolRequestGeolocation-BoKYkQ-Z.js.map → PkToolRequestGeolocation-DjRmmYMk.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestOAuthConnection-CDzpfBg6.js → PkToolRequestOAuthConnection-CJ8dg2RY.js} +2 -2
- package/dist/_chunks/{PkToolRequestOAuthConnection-CDzpfBg6.js.map → PkToolRequestOAuthConnection-CJ8dg2RY.js.map} +1 -1
- package/dist/_chunks/{PkToolShowArtifact-DB8JjN-b.js → PkToolShowArtifact-Bk-xmGxl.js} +2 -2
- package/dist/_chunks/{PkToolShowArtifact-DB8JjN-b.js.map → PkToolShowArtifact-Bk-xmGxl.js.map} +1 -1
- package/dist/_chunks/{PkToolShowContactForm-DHviNxDk.js → PkToolShowContactForm-B0o6CY0Q.js} +2 -2
- package/dist/_chunks/{PkToolShowContactForm-DHviNxDk.js.map → PkToolShowContactForm-B0o6CY0Q.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-B-R9fh3R.js → PkToolShowForm-C4-KAKPL.js} +3 -3
- package/dist/_chunks/{PkToolShowForm-B-R9fh3R.js.map → PkToolShowForm-C4-KAKPL.js.map} +1 -1
- package/dist/_chunks/PkToolShowImageGallery-Jdo7p2mZ.js +88 -0
- package/dist/_chunks/PkToolShowImageGallery-Jdo7p2mZ.js.map +1 -0
- package/dist/_chunks/PkToolShowProductList-Cfk865KB.js +72 -0
- package/dist/_chunks/PkToolShowProductList-Cfk865KB.js.map +1 -0
- package/dist/_chunks/{PkToolShowSources-CfZEfleZ.js → PkToolShowSources-qqUbdWzN.js} +3 -3
- package/dist/_chunks/{PkToolShowSources-CfZEfleZ.js.map → PkToolShowSources-qqUbdWzN.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSuggestedReply-DsB35hRR.js → PkToolShowSuggestedReply-Dk6Nu9cG.js} +2 -2
- package/dist/_chunks/{PkToolShowSuggestedReply-DsB35hRR.js.map → PkToolShowSuggestedReply-Dk6Nu9cG.js.map} +1 -1
- package/dist/_chunks/PkToolShowVideo-D07i4hVd.js +142 -0
- package/dist/_chunks/PkToolShowVideo-D07i4hVd.js.map +1 -0
- package/dist/_chunks/{PkToolShowWebPages-BOxbTe2X.js → PkToolShowWebPages-ksXRNsDK.js} +31 -24
- package/dist/_chunks/PkToolShowWebPages-ksXRNsDK.js.map +1 -0
- package/dist/_chunks/chatImagePolicy-B1iRRLAB.js +46 -0
- package/dist/_chunks/chatImagePolicy-B1iRRLAB.js.map +1 -0
- package/dist/_chunks/{markdown-_p-gd9w8.js → markdown-DOitQ2jz.js} +13 -13
- package/dist/_chunks/{markdown-_p-gd9w8.js.map → markdown-DOitQ2jz.js.map} +1 -1
- package/dist/ai-elements.es.js +1720 -1713
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotEmptyState.js +1 -1
- package/dist-vue/PkChatbotInput.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotStepPayload.js +1 -1
- package/dist-vue/PkChatbotStepToolDetail.js +1 -1
- package/dist-vue/PkChatbotSteps.js +1 -1
- package/dist-vue/PkChatbotViewChat.js +1 -1
- package/dist-vue/PkChatbotViewConversations.js +1 -1
- package/dist-vue/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/{Agent-KbZm8GvA.js → Agent-C4p46YGt.js} +67 -56
- package/dist-vue/_chunks/Agent-C4p46YGt.js.map +1 -0
- package/dist-vue/_chunks/{Document-DCSWwcRE.js → Document-B9QaYHKT.js} +10 -10
- package/dist-vue/_chunks/{Document-DCSWwcRE.js.map → Document-B9QaYHKT.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbot-BkMe7zxp.js +275 -0
- package/dist-vue/_chunks/PkChatbot-BkMe7zxp.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotEmptyState-CO7-vlsZ.js → PkChatbotEmptyState-HElKT2OO.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotEmptyState-CO7-vlsZ.js.map → PkChatbotEmptyState-HElKT2OO.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotInput-D8ykcHKJ.js → PkChatbotInput-BykvFqbm.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotInput-D8ykcHKJ.js.map → PkChatbotInput-BykvFqbm.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-UT72KC_z.js → PkChatbotMessages-wH9wxc6Y.js} +19 -19
- package/dist-vue/_chunks/{PkChatbotMessages-UT72KC_z.js.map → PkChatbotMessages-wH9wxc6Y.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotStepPayload-CSVHe7DP.js → PkChatbotStepPayload-DY0AzM6s.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotStepPayload-CSVHe7DP.js.map → PkChatbotStepPayload-DY0AzM6s.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-ixte-rGB.js → PkChatbotStepToolDetail-AXtXQVQY.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-ixte-rGB.js.map → PkChatbotStepToolDetail-AXtXQVQY.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotSteps-Y6kQ00ui.js → PkChatbotSteps-B-yrfTat.js} +5 -5
- package/dist-vue/_chunks/{PkChatbotSteps-Y6kQ00ui.js.map → PkChatbotSteps-B-yrfTat.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-D3PwREDw.js → PkChatbotViewChat-CCcMnVkM.js} +7 -7
- package/dist-vue/_chunks/{PkChatbotViewChat-D3PwREDw.js.map → PkChatbotViewChat-CCcMnVkM.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewConversations-dpwDFyr_.js → PkChatbotViewConversations-C9h_bBIc.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewConversations-dpwDFyr_.js.map → PkChatbotViewConversations-C9h_bBIc.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewFiles-BStYLY2B.js → PkChatbotViewFiles-BkoXgGE6.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotViewFiles-BStYLY2B.js.map → PkChatbotViewFiles-BkoXgGE6.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-Brylm3We.js → PkChatbotViewProfile-CFR0B99Z.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-Brylm3We.js.map → PkChatbotViewProfile-CFR0B99Z.js.map} +1 -1
- package/dist-vue/_chunks/PkHeldImage-BqnlurT_.js +41 -0
- package/dist-vue/_chunks/PkHeldImage-BqnlurT_.js.map +1 -0
- package/dist-vue/_chunks/{PkStreamingMarkdown-CJlfq7ub.js → PkStreamingMarkdown-XJBXdSG2.js} +792 -780
- package/dist-vue/_chunks/PkStreamingMarkdown-XJBXdSG2.js.map +1 -0
- package/dist-vue/_chunks/{PkToolRequestGeolocation-C8sLjD6g.js → PkToolRequestGeolocation-DpvsFEga.js} +2 -2
- package/dist-vue/_chunks/{PkToolRequestGeolocation-C8sLjD6g.js.map → PkToolRequestGeolocation-DpvsFEga.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolRequestOAuthConnection-rbjNi_l0.js → PkToolRequestOAuthConnection-DRefs-S1.js} +2 -2
- package/dist-vue/_chunks/{PkToolRequestOAuthConnection-rbjNi_l0.js.map → PkToolRequestOAuthConnection-DRefs-S1.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowArtifact-hBmAAfsd.js → PkToolShowArtifact-BopCMkyZ.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowArtifact-hBmAAfsd.js.map → PkToolShowArtifact-BopCMkyZ.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowContactForm-BHsm_W2R.js → PkToolShowContactForm-C6F_DdBu.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowContactForm-BHsm_W2R.js.map → PkToolShowContactForm-C6F_DdBu.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowDiagram-De2XvRnq.js → PkToolShowDiagram-7-m7VrED.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowDiagram-De2XvRnq.js.map → PkToolShowDiagram-7-m7VrED.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowEmail-D1mXZrT-.js → PkToolShowEmail-C591LR5N.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowEmail-D1mXZrT-.js.map → PkToolShowEmail-C591LR5N.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowForm-Ce_GpcBt.js → PkToolShowForm-DEr8wDJi.js} +4 -4
- package/dist-vue/_chunks/{PkToolShowForm-Ce_GpcBt.js.map → PkToolShowForm-DEr8wDJi.js.map} +1 -1
- package/dist-vue/_chunks/PkToolShowImageGallery-D44wf_u3.js +88 -0
- package/dist-vue/_chunks/PkToolShowImageGallery-D44wf_u3.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowMessage-lNPZxw2v.js → PkToolShowMessage-CbXERPHI.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowMessage-lNPZxw2v.js.map → PkToolShowMessage-CbXERPHI.js.map} +1 -1
- package/dist-vue/_chunks/PkToolShowProductList-C6CVRUlD.js +72 -0
- package/dist-vue/_chunks/PkToolShowProductList-C6CVRUlD.js.map +1 -0
- package/dist-vue/_chunks/{PkToolShowSources-B1Nhrk7O.js → PkToolShowSources-DzUNanmK.js} +4 -4
- package/dist-vue/_chunks/{PkToolShowSources-B1Nhrk7O.js.map → PkToolShowSources-DzUNanmK.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-Bmzygdww.js → PkToolShowSuggestedReply-DhNtI5gJ.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSuggestedReply-Bmzygdww.js.map → PkToolShowSuggestedReply-DhNtI5gJ.js.map} +1 -1
- package/dist-vue/_chunks/PkToolShowVideo-HbQzTMh5.js +141 -0
- package/dist-vue/_chunks/PkToolShowVideo-HbQzTMh5.js.map +1 -0
- package/dist-vue/_chunks/PkToolShowWebPages-Ct1HaCSq.js +86 -0
- package/dist-vue/_chunks/PkToolShowWebPages-Ct1HaCSq.js.map +1 -0
- package/dist-vue/_chunks/{PkUrl-E12f7rn_.js → PkUrl-CN1kkZiH.js} +2 -2
- package/dist-vue/_chunks/{PkUrl-E12f7rn_.js.map → PkUrl-CN1kkZiH.js.map} +1 -1
- package/dist-vue/_chunks/{createChatbotApiClient-B_mkyrEl.js → createChatbotApiClient-B2PvUG07.js} +16 -16
- package/dist-vue/_chunks/{createChatbotApiClient-B_mkyrEl.js.map → createChatbotApiClient-B2PvUG07.js.map} +1 -1
- package/dist-vue/_chunks/{useChatbotStore-DEELIJ89.js → useChatbotStore-Cj_UmNH3.js} +3 -3
- package/dist-vue/_chunks/{useChatbotStore-DEELIJ89.js.map → useChatbotStore-Cj_UmNH3.js.map} +1 -1
- package/dist-vue/_chunks/useShortUrlResolver-DI8ycJiO.js +71 -0
- package/dist-vue/_chunks/useShortUrlResolver-DI8ycJiO.js.map +1 -0
- package/dist-vue/_chunks/{utils-CtD8DCrq.js → utils-DXXzMVU3.js} +2 -2
- package/dist-vue/_chunks/{utils-CtD8DCrq.js.map → utils-DXXzMVU3.js.map} +1 -1
- package/dist-vue/api.js +1 -1
- package/dist-vue/apps/web-component/src/composables/useChatbotAgent.d.ts +2 -0
- package/dist-vue/composables.js +11 -7
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +3444 -3400
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +12 -0
- package/dist-vue/packages/components/src/chat/PkAgentSettingsPanel.d.ts +6 -2
- package/dist-vue/packages/components/src/chat/PkHeldImage.d.ts +21 -0
- package/dist-vue/packages/components/src/chat/chatImagePolicy.d.ts +55 -0
- package/dist-vue/packages/components/src/chat/index.d.ts +1 -0
- package/dist-vue/packages/components/src/chat/markdownHtml.d.ts +17 -1
- package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +6 -0
- package/dist-vue/packages/models/src/schema/Agent.d.ts +14 -0
- package/dist-vue/packages/models/src/schema/ReasoningChat.d.ts +2 -0
- package/dist-vue/packages/models/src/schema/SubAgent.d.ts +2 -0
- package/dist-vue/style.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/Agent-Cu04vBnd.js.map +0 -1
- package/dist/_chunks/PkStreamingMarkdown-Qk9Tk_N7.js.map +0 -1
- package/dist/_chunks/PkToolShowImageGallery-FubPWFCb.js +0 -75
- package/dist/_chunks/PkToolShowImageGallery-FubPWFCb.js.map +0 -1
- package/dist/_chunks/PkToolShowProductList-DFy2jxn4.js +0 -65
- package/dist/_chunks/PkToolShowProductList-DFy2jxn4.js.map +0 -1
- package/dist/_chunks/PkToolShowVideo-DClps9oP.js +0 -123
- package/dist/_chunks/PkToolShowVideo-DClps9oP.js.map +0 -1
- package/dist/_chunks/PkToolShowWebPages-BOxbTe2X.js.map +0 -1
- package/dist-vue/_chunks/Agent-KbZm8GvA.js.map +0 -1
- package/dist-vue/_chunks/PkChatbot-CRb-U8e3.js +0 -272
- package/dist-vue/_chunks/PkChatbot-CRb-U8e3.js.map +0 -1
- package/dist-vue/_chunks/PkStreamingMarkdown-CJlfq7ub.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowImageGallery-DmhZLnBk.js +0 -75
- package/dist-vue/_chunks/PkToolShowImageGallery-DmhZLnBk.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowProductList-BW5pRFe4.js +0 -65
- package/dist-vue/_chunks/PkToolShowProductList-BW5pRFe4.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowVideo-BZOTu2Qe.js +0 -122
- package/dist-vue/_chunks/PkToolShowVideo-BZOTu2Qe.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowWebPages-BiIBRV_n.js +0 -79
- package/dist-vue/_chunks/PkToolShowWebPages-BiIBRV_n.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkToolShowSuggestedReply-Bmzygdww.js","names":["__unplugin_components_0","_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeClass","_withModifiers","_createVNode","_createElementVNode","_toDisplayString","_createCommentVNode","_sfc_main","__exportAll","isVoiceRenderAck","_hoisted_1","_hoisted_2","PkToolShowSuggestedReply_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","toolPart","computed","suggestions","input","clickedIndex","ref","isInteractive","handleClick","index","suggestion","value","_component_VvIcon","VvIcon","openBlock","createElementBlock","Fragment","renderList","normalizeClass","withModifiers","createVNode","createElementVNode","toDisplayString","createCommentVNode","PkToolShowSuggestedReply_exports","PkToolShowSuggestedReply_default"],"sources":["../../../../packages/components/src/chat/PkToolShowSuggestedReply.vue","../../../../packages/components/src/chat/PkToolShowSuggestedReply.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isVoiceRenderAck } from 'models'\n\n type Suggestion = { label: string; prompt?: string }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const emit = defineEmits<{\n select: [value: string]\n }>()\n\n const toolPart = computed(() => {\n return JSON.parse(JSON.stringify(props.part)) as {\n state?: string\n toolCallId?: string\n input?: {\n /** New schema: array of suggestions */\n suggestions?: Suggestion[]\n /** Legacy schema: single suggestion */\n label?: string\n prompt?: string\n }\n output?: string\n }\n })\n\n /** Normalize to always work with an array of suggestions */\n const suggestions = computed<Suggestion[]>(() => {\n const input = toolPart.value?.input\n if (!input) {\n return []\n }\n if (input.suggestions?.length) {\n return input.suggestions\n }\n // Legacy single-suggestion format (client-side fallback)\n if (input.label) {\n return [{ label: input.label, prompt: input.prompt ?? input.label }]\n }\n return []\n })\n\n const clickedIndex = ref<number | null>(null)\n\n const isInteractive = computed(() => {\n // A chip drawn during a CALL arrives already resolved: the turn could\n // not wait for a tap, so the backend closed the tool with the note\n // that it was drawn (see `isVoiceRenderAck`, same rule as the forms).\n // The note is not a tap, and without this the chips of every spoken\n // turn rendered dead.\n return (\n (toolPart.value?.state === 'input-available' ||\n isVoiceRenderAck(toolPart.value?.output)) &&\n clickedIndex.value === null\n )\n })\n\n function handleClick(index: number) {\n if (!isInteractive.value) {\n return\n }\n clickedIndex.value = index\n const suggestion = suggestions.value[index]\n const value = suggestion?.prompt ?? suggestion?.label\n if (value) {\n emit('select', value)\n }\n }\n</script>\n\n<template>\n <div v-if=\"suggestions.length\" class=\"pk-tool-suggested-reply\">\n <button\n v-for=\"(suggestion, index) in suggestions\"\n :key=\"index\"\n type=\"button\"\n class=\"pk-tool-suggested-reply__link\"\n :class=\"{\n 'is-clicked': clickedIndex === index,\n 'is-disabled': !isInteractive,\n }\"\n :disabled=\"!isInteractive\"\n @click.stop=\"handleClick(index)\">\n <VvIcon\n name=\"ri:corner-down-right-line\"\n class=\"pk-tool-suggested-reply__icon\" />\n <span>{{ suggestion.label }}</span>\n </button>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-suggested-reply {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4);\n\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n background: none;\n border: none;\n padding: 0;\n font: inherit;\n font-size: var(--text-12);\n text-decoration: underline;\n text-underline-offset: 2px;\n cursor: pointer;\n color: var(--color-word-3);\n transition: var(--transition-colors);\n\n &:hover:not(:disabled) {\n color: var(--color-word);\n }\n\n &.is-disabled {\n cursor: default;\n pointer-events: none;\n color: var(--color-word-5);\n }\n\n &.is-clicked {\n color: var(--color-word-5);\n text-decoration: none;\n }\n }\n\n &__icon {\n width: 1em;\n height: 1em;\n flex-shrink: 0;\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isVoiceRenderAck } from 'models'\n\n type Suggestion = { label: string; prompt?: string }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const emit = defineEmits<{\n select: [value: string]\n }>()\n\n const toolPart = computed(() => {\n return JSON.parse(JSON.stringify(props.part)) as {\n state?: string\n toolCallId?: string\n input?: {\n /** New schema: array of suggestions */\n suggestions?: Suggestion[]\n /** Legacy schema: single suggestion */\n label?: string\n prompt?: string\n }\n output?: string\n }\n })\n\n /** Normalize to always work with an array of suggestions */\n const suggestions = computed<Suggestion[]>(() => {\n const input = toolPart.value?.input\n if (!input) {\n return []\n }\n if (input.suggestions?.length) {\n return input.suggestions\n }\n // Legacy single-suggestion format (client-side fallback)\n if (input.label) {\n return [{ label: input.label, prompt: input.prompt ?? input.label }]\n }\n return []\n })\n\n const clickedIndex = ref<number | null>(null)\n\n const isInteractive = computed(() => {\n // A chip drawn during a CALL arrives already resolved: the turn could\n // not wait for a tap, so the backend closed the tool with the note\n // that it was drawn (see `isVoiceRenderAck`, same rule as the forms).\n // The note is not a tap, and without this the chips of every spoken\n // turn rendered dead.\n return (\n (toolPart.value?.state === 'input-available' ||\n isVoiceRenderAck(toolPart.value?.output)) &&\n clickedIndex.value === null\n )\n })\n\n function handleClick(index: number) {\n if (!isInteractive.value) {\n return\n }\n clickedIndex.value = index\n const suggestion = suggestions.value[index]\n const value = suggestion?.prompt ?? suggestion?.label\n if (value) {\n emit('select', value)\n }\n }\n</script>\n\n<template>\n <div v-if=\"suggestions.length\" class=\"pk-tool-suggested-reply\">\n <button\n v-for=\"(suggestion, index) in suggestions\"\n :key=\"index\"\n type=\"button\"\n class=\"pk-tool-suggested-reply__link\"\n :class=\"{\n 'is-clicked': clickedIndex === index,\n 'is-disabled': !isInteractive,\n }\"\n :disabled=\"!isInteractive\"\n @click.stop=\"handleClick(index)\">\n <VvIcon\n name=\"ri:corner-down-right-line\"\n class=\"pk-tool-suggested-reply__icon\" />\n <span>{{ suggestion.label }}</span>\n </button>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-suggested-reply {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4);\n\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n background: none;\n border: none;\n padding: 0;\n font: inherit;\n font-size: var(--text-12);\n text-decoration: underline;\n text-underline-offset: 2px;\n cursor: pointer;\n color: var(--color-word-3);\n transition: var(--transition-colors);\n\n &:hover:not(:disabled) {\n color: var(--color-word);\n }\n\n &.is-disabled {\n cursor: default;\n pointer-events: none;\n color: var(--color-word-5);\n }\n\n &.is-clicked {\n color: var(--color-word-5);\n text-decoration: none;\n }\n }\n\n &__icon {\n width: 1em;\n height: 1em;\n flex-shrink: 0;\n }\n }\n</style>\n"],"mappings":";;;;;;;;;;;;;EAMI,IAAMkB,IAAQC,GAIRC,IAAOH,GAIPI,IAAWC,QACN,KAAK,MAAM,KAAK,UAAUJ,EAAM,IAAI,CAAC,CAY/C,GAGKK,IAAcD,QAA6B;GAC7C,IAAME,IAAQH,EAAS,OAAO;GAW9B,OAVKG,IAGDA,EAAM,aAAa,SACZA,EAAM,cAGbA,EAAM,QACC,CAAC;IAAE,OAAOA,EAAM;IAAO,QAAQA,EAAM,UAAUA,EAAM;GAAM,CAAC,IAEhE,CAAC,IATG,CAAC;EAUhB,CAAC,GAEKC,IAAeC,EAAmB,IAAI,GAEtCC,IAAgBL,SAObD,EAAS,OAAO,UAAU,qBACvBR,EAAiBQ,EAAS,OAAO,MAAM,MAC3CI,EAAa,UAAU,IAE9B;EAED,SAASG,EAAY,GAAe;GAChC,IAAI,CAACD,EAAc,OACf;GAEJ,EAAa,QAAQE;GACrB,IAAMC,IAAaP,EAAY,MAAMM,IAC/BE,IAAQD,GAAY,UAAUA,GAAY;GAChD,AAAIC,KACAX,EAAK,UAAUW,CAAK;EAE5B;;;GAIW,OAAAR,EAAA,MAAY,UAAvBtB,EAAA,GAAAC,EAiBM,OAjBNY,GAiBM,EAhBFb,EAAA,EAAA,GAAAC,EAeSC,GAAA,MAAAC,EAdyBmB,EAAA,QAAtB,GAAY,OADxBtB,EAAA,GAAAC,EAeS,UAAA;IAbJ,KAAK2B;IACN,MAAK;IACL,OAAKxB,EAAA,CAAC,iCAA+B;KACG,cAAAoB,EAAA,UAAiBI;KAAuC,eAAA,CAAAF,EAAA;;IAI/F,UAAQ,CAAGA,EAAA;IACX,SAAKrB,GAAA,MAAOsB,EAAYC,CAAK,GAAA,CAAA,MAAA,CAAA;GAC9BtB,GAAAA,CAAAA,EAE4CyB,GAAA;IADxC,MAAK;IACL,OAAM;GACVxB,CAAAA,GAAAA,EAAmC,QAAA,MAAAC,EAA1BqB,EAAW,KAAK,GAAA,CAAA,CAAA,GAAA,IAAAf,CAAA"}
|
|
1
|
+
{"version":3,"file":"PkToolShowSuggestedReply-DhNtI5gJ.js","names":["__unplugin_components_0","_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeClass","_withModifiers","_createVNode","_createElementVNode","_toDisplayString","_createCommentVNode","_sfc_main","__exportAll","isVoiceRenderAck","_hoisted_1","_hoisted_2","PkToolShowSuggestedReply_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","toolPart","computed","suggestions","input","clickedIndex","ref","isInteractive","handleClick","index","suggestion","value","_component_VvIcon","VvIcon","openBlock","createElementBlock","Fragment","renderList","normalizeClass","withModifiers","createVNode","createElementVNode","toDisplayString","createCommentVNode","PkToolShowSuggestedReply_exports","PkToolShowSuggestedReply_default"],"sources":["../../../../packages/components/src/chat/PkToolShowSuggestedReply.vue","../../../../packages/components/src/chat/PkToolShowSuggestedReply.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isVoiceRenderAck } from 'models'\n\n type Suggestion = { label: string; prompt?: string }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const emit = defineEmits<{\n select: [value: string]\n }>()\n\n const toolPart = computed(() => {\n return JSON.parse(JSON.stringify(props.part)) as {\n state?: string\n toolCallId?: string\n input?: {\n /** New schema: array of suggestions */\n suggestions?: Suggestion[]\n /** Legacy schema: single suggestion */\n label?: string\n prompt?: string\n }\n output?: string\n }\n })\n\n /** Normalize to always work with an array of suggestions */\n const suggestions = computed<Suggestion[]>(() => {\n const input = toolPart.value?.input\n if (!input) {\n return []\n }\n if (input.suggestions?.length) {\n return input.suggestions\n }\n // Legacy single-suggestion format (client-side fallback)\n if (input.label) {\n return [{ label: input.label, prompt: input.prompt ?? input.label }]\n }\n return []\n })\n\n const clickedIndex = ref<number | null>(null)\n\n const isInteractive = computed(() => {\n // A chip drawn during a CALL arrives already resolved: the turn could\n // not wait for a tap, so the backend closed the tool with the note\n // that it was drawn (see `isVoiceRenderAck`, same rule as the forms).\n // The note is not a tap, and without this the chips of every spoken\n // turn rendered dead.\n return (\n (toolPart.value?.state === 'input-available' ||\n isVoiceRenderAck(toolPart.value?.output)) &&\n clickedIndex.value === null\n )\n })\n\n function handleClick(index: number) {\n if (!isInteractive.value) {\n return\n }\n clickedIndex.value = index\n const suggestion = suggestions.value[index]\n const value = suggestion?.prompt ?? suggestion?.label\n if (value) {\n emit('select', value)\n }\n }\n</script>\n\n<template>\n <div v-if=\"suggestions.length\" class=\"pk-tool-suggested-reply\">\n <button\n v-for=\"(suggestion, index) in suggestions\"\n :key=\"index\"\n type=\"button\"\n class=\"pk-tool-suggested-reply__link\"\n :class=\"{\n 'is-clicked': clickedIndex === index,\n 'is-disabled': !isInteractive,\n }\"\n :disabled=\"!isInteractive\"\n @click.stop=\"handleClick(index)\">\n <VvIcon\n name=\"ri:corner-down-right-line\"\n class=\"pk-tool-suggested-reply__icon\" />\n <span>{{ suggestion.label }}</span>\n </button>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-suggested-reply {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4);\n\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n background: none;\n border: none;\n padding: 0;\n font: inherit;\n font-size: var(--text-12);\n text-decoration: underline;\n text-underline-offset: 2px;\n cursor: pointer;\n color: var(--color-word-3);\n transition: var(--transition-colors);\n\n &:hover:not(:disabled) {\n color: var(--color-word);\n }\n\n &.is-disabled {\n cursor: default;\n pointer-events: none;\n color: var(--color-word-5);\n }\n\n &.is-clicked {\n color: var(--color-word-5);\n text-decoration: none;\n }\n }\n\n &__icon {\n width: 1em;\n height: 1em;\n flex-shrink: 0;\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isVoiceRenderAck } from 'models'\n\n type Suggestion = { label: string; prompt?: string }\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const emit = defineEmits<{\n select: [value: string]\n }>()\n\n const toolPart = computed(() => {\n return JSON.parse(JSON.stringify(props.part)) as {\n state?: string\n toolCallId?: string\n input?: {\n /** New schema: array of suggestions */\n suggestions?: Suggestion[]\n /** Legacy schema: single suggestion */\n label?: string\n prompt?: string\n }\n output?: string\n }\n })\n\n /** Normalize to always work with an array of suggestions */\n const suggestions = computed<Suggestion[]>(() => {\n const input = toolPart.value?.input\n if (!input) {\n return []\n }\n if (input.suggestions?.length) {\n return input.suggestions\n }\n // Legacy single-suggestion format (client-side fallback)\n if (input.label) {\n return [{ label: input.label, prompt: input.prompt ?? input.label }]\n }\n return []\n })\n\n const clickedIndex = ref<number | null>(null)\n\n const isInteractive = computed(() => {\n // A chip drawn during a CALL arrives already resolved: the turn could\n // not wait for a tap, so the backend closed the tool with the note\n // that it was drawn (see `isVoiceRenderAck`, same rule as the forms).\n // The note is not a tap, and without this the chips of every spoken\n // turn rendered dead.\n return (\n (toolPart.value?.state === 'input-available' ||\n isVoiceRenderAck(toolPart.value?.output)) &&\n clickedIndex.value === null\n )\n })\n\n function handleClick(index: number) {\n if (!isInteractive.value) {\n return\n }\n clickedIndex.value = index\n const suggestion = suggestions.value[index]\n const value = suggestion?.prompt ?? suggestion?.label\n if (value) {\n emit('select', value)\n }\n }\n</script>\n\n<template>\n <div v-if=\"suggestions.length\" class=\"pk-tool-suggested-reply\">\n <button\n v-for=\"(suggestion, index) in suggestions\"\n :key=\"index\"\n type=\"button\"\n class=\"pk-tool-suggested-reply__link\"\n :class=\"{\n 'is-clicked': clickedIndex === index,\n 'is-disabled': !isInteractive,\n }\"\n :disabled=\"!isInteractive\"\n @click.stop=\"handleClick(index)\">\n <VvIcon\n name=\"ri:corner-down-right-line\"\n class=\"pk-tool-suggested-reply__icon\" />\n <span>{{ suggestion.label }}</span>\n </button>\n </div>\n</template>\n\n<style lang=\"scss\">\n .pk-tool-suggested-reply {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-4);\n\n &__link {\n display: inline-flex;\n align-items: center;\n gap: var(--spacing-4);\n background: none;\n border: none;\n padding: 0;\n font: inherit;\n font-size: var(--text-12);\n text-decoration: underline;\n text-underline-offset: 2px;\n cursor: pointer;\n color: var(--color-word-3);\n transition: var(--transition-colors);\n\n &:hover:not(:disabled) {\n color: var(--color-word);\n }\n\n &.is-disabled {\n cursor: default;\n pointer-events: none;\n color: var(--color-word-5);\n }\n\n &.is-clicked {\n color: var(--color-word-5);\n text-decoration: none;\n }\n }\n\n &__icon {\n width: 1em;\n height: 1em;\n flex-shrink: 0;\n }\n }\n</style>\n"],"mappings":";;;;;;;;;;;;;EAMI,IAAMkB,IAAQC,GAIRC,IAAOH,GAIPI,IAAWC,QACN,KAAK,MAAM,KAAK,UAAUJ,EAAM,IAAI,CAAC,CAY/C,GAGKK,IAAcD,QAA6B;GAC7C,IAAME,IAAQH,EAAS,OAAO;GAW9B,OAVKG,IAGDA,EAAM,aAAa,SACZA,EAAM,cAGbA,EAAM,QACC,CAAC;IAAE,OAAOA,EAAM;IAAO,QAAQA,EAAM,UAAUA,EAAM;GAAM,CAAC,IAEhE,CAAC,IATG,CAAC;EAUhB,CAAC,GAEKC,IAAeC,EAAmB,IAAI,GAEtCC,IAAgBL,SAObD,EAAS,OAAO,UAAU,qBACvBR,EAAiBQ,EAAS,OAAO,MAAM,MAC3CI,EAAa,UAAU,IAE9B;EAED,SAASG,EAAY,GAAe;GAChC,IAAI,CAACD,EAAc,OACf;GAEJ,EAAa,QAAQE;GACrB,IAAMC,IAAaP,EAAY,MAAMM,IAC/BE,IAAQD,GAAY,UAAUA,GAAY;GAChD,AAAIC,KACAX,EAAK,UAAUW,CAAK;EAE5B;;;GAIW,OAAAR,EAAA,MAAY,UAAvBtB,EAAA,GAAAC,EAiBM,OAjBNY,GAiBM,EAhBFb,EAAA,EAAA,GAAAC,EAeSC,GAAA,MAAAC,EAdyBmB,EAAA,QAAtB,GAAY,OADxBtB,EAAA,GAAAC,EAeS,UAAA;IAbJ,KAAK2B;IACN,MAAK;IACL,OAAKxB,EAAA,CAAC,iCAA+B;KACG,cAAAoB,EAAA,UAAiBI;KAAuC,eAAA,CAAAF,EAAA;;IAI/F,UAAQ,CAAGA,EAAA;IACX,SAAKrB,GAAA,MAAOsB,EAAYC,CAAK,GAAA,CAAA,MAAA,CAAA;GAC9BtB,GAAAA,CAAAA,EAE4CyB,GAAA;IADxC,MAAK;IACL,OAAM;GACVxB,CAAAA,GAAAA,EAAmC,QAAA,MAAAC,EAA1BqB,EAAW,KAAK,GAAA,CAAA,CAAA,GAAA,IAAAf,CAAA"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { r as e } from "./PkStreamingMarkdown-XJBXdSG2.js";
|
|
2
|
+
import "./src-DT8FLE4_.js";
|
|
3
|
+
import { t } from "./PkHeldImage-BqnlurT_.js";
|
|
4
|
+
import { VvIcon as n } from "@volverjs/ui-vue/components";
|
|
5
|
+
import { Fragment as r, computed as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createVNode as c, defineComponent as l, openBlock as u, renderList as d, toDisplayString as f, unref as p } from "vue";
|
|
6
|
+
//#region ../../packages/utils/src/video-source.ts
|
|
7
|
+
var m = (e) => e.replace(/[^a-zA-Z0-9_-]/g, ""), h = (e, t) => e === t || e.endsWith(`.${t}`), g = (e) => {
|
|
8
|
+
if (e.hostname.endsWith("youtu.be")) return m(e.pathname.split("/")[1] ?? "") || null;
|
|
9
|
+
let t = e.searchParams.get("v");
|
|
10
|
+
if (t) return m(t) || null;
|
|
11
|
+
let n = e.pathname.match(/^\/(?:shorts|live|embed|v)\/([^/?#]+)/);
|
|
12
|
+
return n && m(n[1]) || null;
|
|
13
|
+
}, _ = (e) => {
|
|
14
|
+
let t = e.pathname.match(/(?:^|\/)(\d+)(?:$|[/?#])/);
|
|
15
|
+
return t ? t[1] : null;
|
|
16
|
+
};
|
|
17
|
+
function v(e, t) {
|
|
18
|
+
let n;
|
|
19
|
+
try {
|
|
20
|
+
n = new URL(e);
|
|
21
|
+
} catch {
|
|
22
|
+
return {
|
|
23
|
+
provider: "file",
|
|
24
|
+
url: e,
|
|
25
|
+
embedUrl: e
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
let r = n.hostname.toLowerCase();
|
|
29
|
+
if (h(r, "youtube.com") || h(r, "youtu.be") || h(r, "youtube-nocookie.com")) {
|
|
30
|
+
let t = g(n);
|
|
31
|
+
if (t) return {
|
|
32
|
+
provider: "youtube",
|
|
33
|
+
url: e,
|
|
34
|
+
videoId: t,
|
|
35
|
+
embedUrl: `https://www.youtube-nocookie.com/embed/${t}`,
|
|
36
|
+
thumbnailUrl: `https://img.youtube.com/vi/${t}/hqdefault.jpg`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (h(r, "vimeo.com")) {
|
|
40
|
+
let t = _(n);
|
|
41
|
+
if (t) return {
|
|
42
|
+
provider: "vimeo",
|
|
43
|
+
url: e,
|
|
44
|
+
videoId: t,
|
|
45
|
+
embedUrl: `https://player.vimeo.com/video/${t}?dnt=1`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return t && t !== "file" ? {
|
|
49
|
+
provider: t,
|
|
50
|
+
url: e,
|
|
51
|
+
embedUrl: e
|
|
52
|
+
} : {
|
|
53
|
+
provider: "file",
|
|
54
|
+
url: e,
|
|
55
|
+
embedUrl: e
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region ../../packages/components/src/chat/PkToolShowVideo.vue?vue&type=script&setup=true&lang.ts
|
|
60
|
+
var y = {
|
|
61
|
+
key: 0,
|
|
62
|
+
class: "border border-surface-3 rounded-xl w-full overflow-hidden bg-surface"
|
|
63
|
+
}, b = { class: "px-sm py-6 bg-surface-1 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40" }, x = { class: "font-bold" }, S = { class: "p-sm flex flex-col gap-16" }, C = {
|
|
64
|
+
key: 0,
|
|
65
|
+
class: "font-bold text-14 mb-sm"
|
|
66
|
+
}, w = {
|
|
67
|
+
key: 1,
|
|
68
|
+
class: "w-full rounded-xl overflow-hidden",
|
|
69
|
+
style: { "aspect-ratio": "16 / 9" }
|
|
70
|
+
}, T = ["src", "poster"], E = ["href"], D = {
|
|
71
|
+
key: 3,
|
|
72
|
+
class: "relative w-full overflow-hidden rounded-xl bg-word-1",
|
|
73
|
+
style: { "aspect-ratio": "16 / 9" }
|
|
74
|
+
}, O = ["src", "title"], k = {
|
|
75
|
+
key: 4,
|
|
76
|
+
class: "text-12 text-word-3 mt-sm"
|
|
77
|
+
}, A = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", j = /* @__PURE__ */ l({
|
|
78
|
+
__name: "PkToolShowVideo",
|
|
79
|
+
props: { part: {} },
|
|
80
|
+
setup(l) {
|
|
81
|
+
let m = l, h = ["www.youtube-nocookie.com", "player.vimeo.com"], g = e(), _ = i(() => (m.part.input?.videos ?? []).map((e) => {
|
|
82
|
+
let t = v(e.url, e.provider), n = (t.provider === "file" ? [t.embedUrl, e.poster] : [t.embedUrl]).filter((e) => !!e && !h.includes(g.hostOf(e) ?? ""));
|
|
83
|
+
return {
|
|
84
|
+
...e,
|
|
85
|
+
source: t,
|
|
86
|
+
gated: n,
|
|
87
|
+
held: n.some((e) => !g.canLoad(e))
|
|
88
|
+
};
|
|
89
|
+
}));
|
|
90
|
+
function j(e) {
|
|
91
|
+
for (let t of e) g.release(t);
|
|
92
|
+
}
|
|
93
|
+
return (e, i) => {
|
|
94
|
+
let l = n;
|
|
95
|
+
return _.value.length ? (u(), o("div", y, [s("div", b, [c(l, {
|
|
96
|
+
name: "ri:film-line",
|
|
97
|
+
class: "text-16"
|
|
98
|
+
}), s("strong", x, f(e.$t("label.video")), 1)]), s("div", S, [(u(!0), o(r, null, d(_.value, (e) => (u(), o("div", {
|
|
99
|
+
key: e.url,
|
|
100
|
+
class: "flex flex-col"
|
|
101
|
+
}, [
|
|
102
|
+
e.title ? (u(), o("strong", C, f(e.title), 1)) : a("", !0),
|
|
103
|
+
e.held ? (u(), o("div", w, [c(t, {
|
|
104
|
+
host: p(g).hostOf(e.gated[0]),
|
|
105
|
+
kind: "video",
|
|
106
|
+
fill: "",
|
|
107
|
+
onRelease: (t) => j(e.gated)
|
|
108
|
+
}, null, 8, ["host", "onRelease"])])) : e.source.provider === "file" ? (u(), o("video", {
|
|
109
|
+
key: 2,
|
|
110
|
+
src: e.source.embedUrl,
|
|
111
|
+
poster: e.poster,
|
|
112
|
+
controls: "",
|
|
113
|
+
preload: "metadata",
|
|
114
|
+
playsinline: "",
|
|
115
|
+
class: "w-full rounded-xl bg-word-1",
|
|
116
|
+
style: {
|
|
117
|
+
"aspect-ratio": "16 / 9",
|
|
118
|
+
"max-height": "480px"
|
|
119
|
+
}
|
|
120
|
+
}, [s("a", {
|
|
121
|
+
href: e.url,
|
|
122
|
+
target: "_blank",
|
|
123
|
+
rel: "noopener noreferrer"
|
|
124
|
+
}, f(e.title ?? e.url), 9, E)], 8, T)) : (u(), o("div", D, [s("iframe", {
|
|
125
|
+
src: e.source.embedUrl,
|
|
126
|
+
title: e.title ?? e.url,
|
|
127
|
+
loading: "lazy",
|
|
128
|
+
referrerpolicy: "strict-origin-when-cross-origin",
|
|
129
|
+
allow: A,
|
|
130
|
+
allowfullscreen: "",
|
|
131
|
+
class: "absolute inset-0 w-full h-full border-0"
|
|
132
|
+
}, null, 8, O)])),
|
|
133
|
+
e.description ? (u(), o("span", k, f(e.description), 1)) : a("", !0)
|
|
134
|
+
]))), 128))])])) : a("", !0);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
//#endregion
|
|
139
|
+
export { j as default };
|
|
140
|
+
|
|
141
|
+
//# sourceMappingURL=PkToolShowVideo-HbQzTMh5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PkToolShowVideo-HbQzTMh5.js","names":["$t","__unplugin_components_0","_openBlock","_createElementBlock","_createElementVNode","_createVNode","_toDisplayString","_Fragment","_renderList","_createCommentVNode","PkHeldImage","_unref","_sfc_main","useChatImageGate","sanitizeId","raw","hostMatches","host","domain","youtubeId","u","v","m","vimeoId","parseVideoSource","url","id","hint","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","IFRAME_ALLOW","PkToolShowVideo_default","defineComponent","props","__props","PLAYER_HOSTS","imageGate","videos","computed","source","video","gated","src","release","urls","_component_VvIcon","VvIcon","openBlock","createElementBlock","createElementVNode","createVNode","toDisplayString","_ctx","Fragment","renderList","createCommentVNode","PkHeldImage_default","unref"],"sources":["../../../../packages/utils/src/video-source.ts","../../../../packages/components/src/chat/PkToolShowVideo.vue","../../../../packages/components/src/chat/PkToolShowVideo.vue"],"sourcesContent":["/**\n * Video source parsing shared by the backend platform renderer and the frontend\n * player component, so both classify a video URL the same way.\n *\n * A URL is either a known provider (YouTube/Vimeo), which plays through the\n * provider's iframe embed, or a direct media file (`file`), which plays in a\n * native `<video>` element. Detection is by URL shape; an explicit `hint` is\n * used only as a fallback when the URL alone is inconclusive.\n */\n\nexport type VideoProvider = 'youtube' | 'vimeo' | 'file'\n\nexport interface ParsedVideoSource {\n provider: VideoProvider\n /** The original URL, unchanged. */\n url: string\n /**\n * The URL to actually load: the provider iframe embed URL for\n * YouTube/Vimeo, or the direct file URL for `file`.\n */\n embedUrl: string\n /** The provider video id (providers only). */\n videoId?: string\n /**\n * A preview image URL derived from the provider, when one is available\n * without an extra API call (currently YouTube only). Lets a link-only\n * surface (e.g. a Teams card) show a thumbnail instead of a bare link.\n */\n thumbnailUrl?: string\n}\n\n/** Keep only characters that are legal in a provider video id. */\nconst sanitizeId = (raw: string): string => raw.replace(/[^a-zA-Z0-9_-]/g, '')\n\n/**\n * True when `host` is exactly `domain` or a subdomain of it. Guards against\n * lookalike hosts (e.g. `notyoutube.com`) that a bare substring check would\n * misclassify.\n */\nconst hostMatches = (host: string, domain: string): boolean =>\n host === domain || host.endsWith(`.${domain}`)\n\n/** Extract the YouTube video id from any of its URL shapes, or null. */\nconst youtubeId = (u: URL): string | null => {\n // youtu.be/<id>\n if (u.hostname.endsWith('youtu.be')) {\n return sanitizeId(u.pathname.split('/')[1] ?? '') || null\n }\n // youtube.com/watch?v=<id>\n const v = u.searchParams.get('v')\n if (v) {\n return sanitizeId(v) || null\n }\n // youtube.com/shorts/<id>, /live/<id>, /embed/<id>, /v/<id>\n const m = u.pathname.match(/^\\/(?:shorts|live|embed|v)\\/([^/?#]+)/)\n return m ? sanitizeId(m[1]) || null : null\n}\n\n/** Extract the Vimeo (numeric) video id, or null. */\nconst vimeoId = (u: URL): string | null => {\n // player.vimeo.com/video/<id> or vimeo.com/<id>\n const m = u.pathname.match(/(?:^|\\/)(\\d+)(?:$|[/?#])/)\n return m ? m[1] : null\n}\n\n/**\n * Classify a video URL into a provider + a loadable embed/file URL.\n * Falls back to `file` (native `<video>`) for anything not recognized as a\n * supported provider, or when a provider is matched but no id can be extracted.\n */\nexport function parseVideoSource(\n url: string,\n hint?: VideoProvider,\n): ParsedVideoSource {\n let u: URL | undefined\n try {\n u = new URL(url)\n } catch {\n // Not an absolute URL: treat as a direct file reference.\n return { provider: 'file', url, embedUrl: url }\n }\n\n const host = u.hostname.toLowerCase()\n\n const isYouTube =\n hostMatches(host, 'youtube.com') ||\n hostMatches(host, 'youtu.be') ||\n hostMatches(host, 'youtube-nocookie.com')\n if (isYouTube) {\n const id = youtubeId(u)\n if (id) {\n return {\n provider: 'youtube',\n url,\n videoId: id,\n embedUrl: `https://www.youtube-nocookie.com/embed/${id}`,\n thumbnailUrl: `https://img.youtube.com/vi/${id}/hqdefault.jpg`,\n }\n }\n }\n\n if (hostMatches(host, 'vimeo.com')) {\n const id = vimeoId(u)\n if (id) {\n return {\n provider: 'vimeo',\n url,\n videoId: id,\n // `dnt=1`: the player sets no cookies and records no session\n // data, the Vimeo counterpart of youtube-nocookie above.\n embedUrl: `https://player.vimeo.com/video/${id}?dnt=1`,\n }\n }\n }\n\n // Honor an explicit provider hint only if we could not extract an id from a\n // provider URL (so a caller-tagged provider link still routes to an iframe\n // via its original URL rather than a broken native <video>).\n if (hint && hint !== 'file') {\n return { provider: hint, url, embedUrl: url }\n }\n\n return { provider: 'file', url, embedUrl: url }\n}\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { parseVideoSource, type VideoProvider } from 'utils'\n import { useChatImageGate } from './chatImagePolicy'\n import PkHeldImage from './PkHeldImage.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n interface VideoInput {\n url: string\n title?: string\n description?: string\n poster?: string\n provider?: VideoProvider\n }\n\n // Permissions granted to provider embeds (YouTube/Vimeo players).\n const IFRAME_ALLOW =\n 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'\n\n /**\n * Where a YouTube or Vimeo player lives: the url is built here from the\n * video id, so nothing the model wrote reaches these hosts but the id.\n */\n const PLAYER_HOSTS = ['www.youtube-nocookie.com', 'player.vimeo.com']\n\n // A file, its poster, or an embed this card does not recognize, fetch the\n // url the model wrote as soon as they render: from a host the agent does\n // not list they wait for a click, like an image (chatImagePolicy.ts).\n const imageGate = useChatImageGate()\n\n const videos = computed(() => {\n const part = props.part as { input?: { videos?: VideoInput[] } }\n return (part.input?.videos ?? []).map((video) => {\n const source = parseVideoSource(video.url, video.provider)\n const fetched =\n source.provider === 'file'\n ? [source.embedUrl, video.poster]\n : [source.embedUrl]\n const gated = fetched.filter(\n (src): src is string =>\n !!src &&\n !PLAYER_HOSTS.includes(imageGate.hostOf(src) ?? ''),\n )\n return {\n ...video,\n source,\n gated,\n held: gated.some((src) => !imageGate.canLoad(src)),\n }\n })\n })\n\n function release(urls: string[]) {\n for (const url of urls) {\n imageGate.release(url)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"videos.length\"\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 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:film-line\" class=\"text-16\" />\n <strong class=\"font-bold\">\n {{ $t('label.video') }}\n </strong>\n </div>\n <div class=\"p-sm flex flex-col gap-16\">\n <div v-for=\"video in videos\" :key=\"video.url\" class=\"flex flex-col\">\n <strong v-if=\"video.title\" class=\"font-bold text-14 mb-sm\">\n {{ video.title }}\n </strong>\n <!-- Direct file: native player. Handles MP4/WebM/OGG and works\n inside the widget's Shadow DOM without any extra CSS. -->\n <div\n v-if=\"video.held\"\n class=\"w-full rounded-xl overflow-hidden\"\n style=\"aspect-ratio: 16 / 9\">\n <PkHeldImage\n :host=\"imageGate.hostOf(video.gated[0])\"\n kind=\"video\"\n fill\n @release=\"release(video.gated)\" />\n </div>\n <video\n v-else-if=\"video.source.provider === 'file'\"\n :src=\"video.source.embedUrl\"\n :poster=\"video.poster\"\n controls\n preload=\"metadata\"\n playsinline\n class=\"w-full rounded-xl bg-word-1\"\n style=\"aspect-ratio: 16 / 9; max-height: 480px\">\n <a\n :href=\"video.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\">\n {{ video.title ?? video.url }}\n </a>\n </video>\n <!-- Provider (YouTube/Vimeo): iframe embed, 16:9 responsive. -->\n <div\n v-else\n class=\"relative w-full overflow-hidden rounded-xl bg-word-1\"\n style=\"aspect-ratio: 16 / 9\">\n <iframe\n :src=\"video.source.embedUrl\"\n :title=\"video.title ?? video.url\"\n loading=\"lazy\"\n referrerpolicy=\"strict-origin-when-cross-origin\"\n :allow=\"IFRAME_ALLOW\"\n allowfullscreen\n class=\"absolute inset-0 w-full h-full border-0\"></iframe>\n </div>\n <span\n v-if=\"video.description\"\n class=\"text-12 text-word-3 mt-sm\">\n {{ video.description }}\n </span>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { parseVideoSource, type VideoProvider } from 'utils'\n import { useChatImageGate } from './chatImagePolicy'\n import PkHeldImage from './PkHeldImage.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n interface VideoInput {\n url: string\n title?: string\n description?: string\n poster?: string\n provider?: VideoProvider\n }\n\n // Permissions granted to provider embeds (YouTube/Vimeo players).\n const IFRAME_ALLOW =\n 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'\n\n /**\n * Where a YouTube or Vimeo player lives: the url is built here from the\n * video id, so nothing the model wrote reaches these hosts but the id.\n */\n const PLAYER_HOSTS = ['www.youtube-nocookie.com', 'player.vimeo.com']\n\n // A file, its poster, or an embed this card does not recognize, fetch the\n // url the model wrote as soon as they render: from a host the agent does\n // not list they wait for a click, like an image (chatImagePolicy.ts).\n const imageGate = useChatImageGate()\n\n const videos = computed(() => {\n const part = props.part as { input?: { videos?: VideoInput[] } }\n return (part.input?.videos ?? []).map((video) => {\n const source = parseVideoSource(video.url, video.provider)\n const fetched =\n source.provider === 'file'\n ? [source.embedUrl, video.poster]\n : [source.embedUrl]\n const gated = fetched.filter(\n (src): src is string =>\n !!src &&\n !PLAYER_HOSTS.includes(imageGate.hostOf(src) ?? ''),\n )\n return {\n ...video,\n source,\n gated,\n held: gated.some((src) => !imageGate.canLoad(src)),\n }\n })\n })\n\n function release(urls: string[]) {\n for (const url of urls) {\n imageGate.release(url)\n }\n }\n</script>\n\n<template>\n <div\n v-if=\"videos.length\"\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 text-12 border-b border-surface-3 text-word-3 flex items-center gap-8 min-h-40\">\n <VvIcon name=\"ri:film-line\" class=\"text-16\" />\n <strong class=\"font-bold\">\n {{ $t('label.video') }}\n </strong>\n </div>\n <div class=\"p-sm flex flex-col gap-16\">\n <div v-for=\"video in videos\" :key=\"video.url\" class=\"flex flex-col\">\n <strong v-if=\"video.title\" class=\"font-bold text-14 mb-sm\">\n {{ video.title }}\n </strong>\n <!-- Direct file: native player. Handles MP4/WebM/OGG and works\n inside the widget's Shadow DOM without any extra CSS. -->\n <div\n v-if=\"video.held\"\n class=\"w-full rounded-xl overflow-hidden\"\n style=\"aspect-ratio: 16 / 9\">\n <PkHeldImage\n :host=\"imageGate.hostOf(video.gated[0])\"\n kind=\"video\"\n fill\n @release=\"release(video.gated)\" />\n </div>\n <video\n v-else-if=\"video.source.provider === 'file'\"\n :src=\"video.source.embedUrl\"\n :poster=\"video.poster\"\n controls\n preload=\"metadata\"\n playsinline\n class=\"w-full rounded-xl bg-word-1\"\n style=\"aspect-ratio: 16 / 9; max-height: 480px\">\n <a\n :href=\"video.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\">\n {{ video.title ?? video.url }}\n </a>\n </video>\n <!-- Provider (YouTube/Vimeo): iframe embed, 16:9 responsive. -->\n <div\n v-else\n class=\"relative w-full overflow-hidden rounded-xl bg-word-1\"\n style=\"aspect-ratio: 16 / 9\">\n <iframe\n :src=\"video.source.embedUrl\"\n :title=\"video.title ?? video.url\"\n loading=\"lazy\"\n referrerpolicy=\"strict-origin-when-cross-origin\"\n :allow=\"IFRAME_ALLOW\"\n allowfullscreen\n class=\"absolute inset-0 w-full h-full border-0\"></iframe>\n </div>\n <span\n v-if=\"video.description\"\n class=\"text-12 text-word-3 mt-sm\">\n {{ video.description }}\n </span>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
|
|
2
|
+
import { r as t } from "./PkStreamingMarkdown-XJBXdSG2.js";
|
|
3
|
+
import "./src-DT8FLE4_.js";
|
|
4
|
+
import { n } from "./url-validation-9DstIfq2.js";
|
|
5
|
+
import { t as r } from "./PkLazyImg-CDxX9xnq.js";
|
|
6
|
+
import { t as i } from "./PkUrl-CN1kkZiH.js";
|
|
7
|
+
import { t as a } from "./PkHeldImage-BqnlurT_.js";
|
|
8
|
+
import { VvIcon as o } from "@volverjs/ui-vue/components";
|
|
9
|
+
import { Fragment as s, computed as c, createBlock as l, createCommentVNode as u, createElementBlock as d, createElementVNode as f, createTextVNode as p, createVNode as m, defineComponent as h, openBlock as g, ref as _, renderList as v, toDisplayString as y, unref as b } from "vue";
|
|
10
|
+
//#region ../../packages/components/src/chat/PkToolShowWebPages.vue?vue&type=script&setup=true&lang.ts
|
|
11
|
+
var x = {
|
|
12
|
+
key: 0,
|
|
13
|
+
class: "u-pick-list w-full shrink-0"
|
|
14
|
+
}, S = [
|
|
15
|
+
"href",
|
|
16
|
+
"title",
|
|
17
|
+
"target",
|
|
18
|
+
"rel"
|
|
19
|
+
], C = { class: "flex flex-col gap-4 min-w-0 flex-1" }, w = { class: "u-pick-list__label font-medium text-12 text-word-1" }, T = { class: "text-10 text-word-4 truncate flex items-center gap-4" }, E = {
|
|
20
|
+
key: 0,
|
|
21
|
+
class: "text-12 text-word-3 line-clamp-2"
|
|
22
|
+
}, D = {
|
|
23
|
+
key: 0,
|
|
24
|
+
class: "u-pick-list__row"
|
|
25
|
+
}, O = /*@__PURE__*/ h({
|
|
26
|
+
__name: "PkToolShowWebPages",
|
|
27
|
+
props: { part: {} },
|
|
28
|
+
setup(e) {
|
|
29
|
+
let h = e, O = c(() => h.part), k = t(), A = (e) => {
|
|
30
|
+
try {
|
|
31
|
+
let t = new URL(e);
|
|
32
|
+
return t.hash = "", t.toString().replace(/\/$/, "");
|
|
33
|
+
} catch {
|
|
34
|
+
return e.trim().replace(/\/$/, "");
|
|
35
|
+
}
|
|
36
|
+
}, j = c(() => {
|
|
37
|
+
let e = /* @__PURE__ */ new Set(), t = [];
|
|
38
|
+
for (let n of O.value.input?.pages || []) {
|
|
39
|
+
let r = A(n.url);
|
|
40
|
+
e.has(r) || (e.add(r), t.push(n));
|
|
41
|
+
}
|
|
42
|
+
return t;
|
|
43
|
+
}), M = _(!1), N = c(() => M.value ? j.value : j.value.slice(0, 2)), P = c(() => j.value.length > 2);
|
|
44
|
+
return (e, t) => {
|
|
45
|
+
let c = o;
|
|
46
|
+
return N.value?.length ? (g(), d("ul", x, [(g(!0), d(s, null, v(N.value, (t, o) => (g(), d("li", {
|
|
47
|
+
key: o,
|
|
48
|
+
class: "u-pick-list__row"
|
|
49
|
+
}, [f("a", {
|
|
50
|
+
href: t.url,
|
|
51
|
+
title: e.$t("action.open"),
|
|
52
|
+
target: b(n)(t.url) ? void 0 : "_blank",
|
|
53
|
+
rel: b(n)(t.url) ? void 0 : "noopener noreferrer",
|
|
54
|
+
class: "u-pick-list__entry u-pick-list__entry--roomy u-pick-list__entry--top"
|
|
55
|
+
}, [t.image && !b(k).canLoad(t.image) ? (g(), l(a, {
|
|
56
|
+
key: 0,
|
|
57
|
+
host: b(k).hostOf(t.image),
|
|
58
|
+
class: "w-48 h-48 rounded-lg shrink-0 border border-surface-3 bg-surface",
|
|
59
|
+
onRelease: (e) => b(k).release(t.image)
|
|
60
|
+
}, null, 8, ["host", "onRelease"])) : t.image ? (g(), l(r, {
|
|
61
|
+
key: 1,
|
|
62
|
+
src: t.image,
|
|
63
|
+
alt: t.title,
|
|
64
|
+
"fallback-icon": "ri:image-off-line",
|
|
65
|
+
class: "w-48 h-48 object-contain rounded-lg shrink-0 border border-surface-3 bg-surface"
|
|
66
|
+
}, null, 8, ["src", "alt"])) : u("", !0), f("div", C, [
|
|
67
|
+
f("strong", w, y(t.title), 1),
|
|
68
|
+
f("span", T, [m(c, {
|
|
69
|
+
name: "ri:link",
|
|
70
|
+
class: "shrink-0 text-12"
|
|
71
|
+
}), m(i, { url: t.url }, null, 8, ["url"])]),
|
|
72
|
+
t.description ? (g(), d("p", E, y(t.description), 1)) : u("", !0)
|
|
73
|
+
])], 8, S)]))), 128)), P.value ? (g(), d("li", D, [f("button", {
|
|
74
|
+
class: "u-pick-list__entry text-12 text-word-3 justify-center cursor-pointer",
|
|
75
|
+
onClick: t[0] ||= (e) => M.value = !M.value
|
|
76
|
+
}, [m(c, {
|
|
77
|
+
name: M.value ? "ri:arrow-up-s-line" : "ri:arrow-down-s-line",
|
|
78
|
+
class: "text-16"
|
|
79
|
+
}, null, 8, ["name"]), p(" " + y(M.value ? e.$t("action.showLess") : e.$t("action.showMore", { count: j.value.length - 2 })), 1)])])) : u("", !0)])) : u("", !0);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}), k = /* @__PURE__ */ e({ default: () => A }), A = O;
|
|
83
|
+
//#endregion
|
|
84
|
+
export { k as n, A as t };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=PkToolShowWebPages-Ct1HaCSq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PkToolShowWebPages-Ct1HaCSq.js","names":["$t","__unplugin_components_0","_openBlock","_createElementBlock","_Fragment","_renderList","_createElementVNode","_unref","_createBlock","PkHeldImage","PkLazyImg","_createCommentVNode","_toDisplayString","_createVNode","PkUrl","_createTextVNode","_sfc_main","__exportAll","useChatImageGate","PkLazyImg_default","PkUrl_default","PkHeldImage_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","PkToolShowWebPages_vue_vue_type_script_setup_true_lang_default","props","__props","toolPart","computed","imageGate","normalizeUrl","u","url","uniquePages","seen","result","page","key","showMore","ref","pages","hasMore","_component_VvIcon","VvIcon","openBlock","createElementBlock","Fragment","renderList","index","createElementVNode","_ctx","unref","isSameSite","createBlock","createCommentVNode","toDisplayString","createVNode","_cache","createTextVNode","PkToolShowWebPages_exports","PkToolShowWebPages_default"],"sources":["../../../../packages/components/src/chat/PkToolShowWebPages.vue","../../../../packages/components/src/chat/PkToolShowWebPages.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isSameSite } from 'utils'\n import PkLazyImg from '../PkLazyImg.vue'\n import { useChatImageGate } from './chatImagePolicy'\n import PkHeldImage from './PkHeldImage.vue'\n import PkUrl from '../PkUrl.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n pages: Array<{\n title: string\n url: string\n image?: string\n description?: string\n }>\n }\n }\n return part\n })\n\n // The model writes these urls: one from a host the agent does not list\n // waits for a click (chatImagePolicy.ts).\n const imageGate = useChatImageGate()\n\n // The model often passes the same URL several times (e.g. multiple KB\n // chunks from one page), so collapse duplicates, keeping the first (richest)\n // entry. Normalize by dropping the hash and any trailing slash.\n const normalizeUrl = (url: string) => {\n try {\n const u = new URL(url)\n u.hash = ''\n return u.toString().replace(/\\/$/, '')\n } catch {\n return url.trim().replace(/\\/$/, '')\n }\n }\n const uniquePages = computed(() => {\n const seen = new Set<string>()\n const result: NonNullable<typeof toolPart.value.input>['pages'] = []\n for (const page of toolPart.value.input?.pages || []) {\n const key = normalizeUrl(page.url)\n if (seen.has(key)) {\n continue\n }\n seen.add(key)\n result.push(page)\n }\n return result\n })\n\n const showMore = ref(false)\n const pages = computed(() => {\n if (showMore.value) {\n return uniquePages.value\n }\n return uniquePages.value.slice(0, 2)\n })\n const hasMore = computed(() => uniquePages.value.length > 2)\n</script>\n\n<template>\n <ul v-if=\"pages?.length\" class=\"u-pick-list w-full shrink-0\">\n <li\n v-for=\"(page, index) in pages\"\n :key=\"index\"\n class=\"u-pick-list__row\">\n <a\n :href=\"page.url\"\n :title=\"$t('action.open')\"\n :target=\"isSameSite(page.url) ? undefined : '_blank'\"\n :rel=\"isSameSite(page.url) ? undefined : 'noopener noreferrer'\"\n class=\"u-pick-list__entry u-pick-list__entry--roomy u-pick-list__entry--top\">\n <!-- og:images the model scraped are third-party urls: a dead one\n degrades to an icon instead of breaking the card layout -->\n <PkHeldImage\n v-if=\"page.image && !imageGate.canLoad(page.image)\"\n :host=\"imageGate.hostOf(page.image)\"\n class=\"w-48 h-48 rounded-lg shrink-0 border border-surface-3 bg-surface\"\n @release=\"imageGate.release(page.image)\" />\n <PkLazyImg\n v-else-if=\"page.image\"\n :src=\"page.image\"\n :alt=\"page.title\"\n fallback-icon=\"ri:image-off-line\"\n class=\"w-48 h-48 object-contain rounded-lg shrink-0 border border-surface-3 bg-surface\" />\n <div class=\"flex flex-col gap-4 min-w-0 flex-1\">\n <strong\n class=\"u-pick-list__label font-medium text-12 text-word-1\">\n {{ page.title }}\n </strong>\n <span\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-12\" />\n <PkUrl :url=\"page.url\" />\n </span>\n <p\n v-if=\"page.description\"\n class=\"text-12 text-word-3 line-clamp-2\">\n {{ page.description }}\n </p>\n </div>\n </a>\n </li>\n <li v-if=\"hasMore\" class=\"u-pick-list__row\">\n <button\n class=\"u-pick-list__entry text-12 text-word-3 justify-center cursor-pointer\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"\n showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\n \"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: uniquePages.length - 2,\n })\n }}\n </button>\n </li>\n </ul>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref } from 'vue'\n import { isSameSite } from 'utils'\n import PkLazyImg from '../PkLazyImg.vue'\n import { useChatImageGate } from './chatImagePolicy'\n import PkHeldImage from './PkHeldImage.vue'\n import PkUrl from '../PkUrl.vue'\n\n const props = defineProps<{\n part: unknown\n }>()\n\n const toolPart = computed(() => {\n const part = props.part as {\n input?: {\n pages: Array<{\n title: string\n url: string\n image?: string\n description?: string\n }>\n }\n }\n return part\n })\n\n // The model writes these urls: one from a host the agent does not list\n // waits for a click (chatImagePolicy.ts).\n const imageGate = useChatImageGate()\n\n // The model often passes the same URL several times (e.g. multiple KB\n // chunks from one page), so collapse duplicates, keeping the first (richest)\n // entry. Normalize by dropping the hash and any trailing slash.\n const normalizeUrl = (url: string) => {\n try {\n const u = new URL(url)\n u.hash = ''\n return u.toString().replace(/\\/$/, '')\n } catch {\n return url.trim().replace(/\\/$/, '')\n }\n }\n const uniquePages = computed(() => {\n const seen = new Set<string>()\n const result: NonNullable<typeof toolPart.value.input>['pages'] = []\n for (const page of toolPart.value.input?.pages || []) {\n const key = normalizeUrl(page.url)\n if (seen.has(key)) {\n continue\n }\n seen.add(key)\n result.push(page)\n }\n return result\n })\n\n const showMore = ref(false)\n const pages = computed(() => {\n if (showMore.value) {\n return uniquePages.value\n }\n return uniquePages.value.slice(0, 2)\n })\n const hasMore = computed(() => uniquePages.value.length > 2)\n</script>\n\n<template>\n <ul v-if=\"pages?.length\" class=\"u-pick-list w-full shrink-0\">\n <li\n v-for=\"(page, index) in pages\"\n :key=\"index\"\n class=\"u-pick-list__row\">\n <a\n :href=\"page.url\"\n :title=\"$t('action.open')\"\n :target=\"isSameSite(page.url) ? undefined : '_blank'\"\n :rel=\"isSameSite(page.url) ? undefined : 'noopener noreferrer'\"\n class=\"u-pick-list__entry u-pick-list__entry--roomy u-pick-list__entry--top\">\n <!-- og:images the model scraped are third-party urls: a dead one\n degrades to an icon instead of breaking the card layout -->\n <PkHeldImage\n v-if=\"page.image && !imageGate.canLoad(page.image)\"\n :host=\"imageGate.hostOf(page.image)\"\n class=\"w-48 h-48 rounded-lg shrink-0 border border-surface-3 bg-surface\"\n @release=\"imageGate.release(page.image)\" />\n <PkLazyImg\n v-else-if=\"page.image\"\n :src=\"page.image\"\n :alt=\"page.title\"\n fallback-icon=\"ri:image-off-line\"\n class=\"w-48 h-48 object-contain rounded-lg shrink-0 border border-surface-3 bg-surface\" />\n <div class=\"flex flex-col gap-4 min-w-0 flex-1\">\n <strong\n class=\"u-pick-list__label font-medium text-12 text-word-1\">\n {{ page.title }}\n </strong>\n <span\n class=\"text-10 text-word-4 truncate flex items-center gap-4\">\n <VvIcon name=\"ri:link\" class=\"shrink-0 text-12\" />\n <PkUrl :url=\"page.url\" />\n </span>\n <p\n v-if=\"page.description\"\n class=\"text-12 text-word-3 line-clamp-2\">\n {{ page.description }}\n </p>\n </div>\n </a>\n </li>\n <li v-if=\"hasMore\" class=\"u-pick-list__row\">\n <button\n class=\"u-pick-list__entry text-12 text-word-3 justify-center cursor-pointer\"\n @click=\"showMore = !showMore\">\n <VvIcon\n :name=\"\n showMore ? 'ri:arrow-up-s-line' : 'ri:arrow-down-s-line'\n \"\n class=\"text-16\" />\n {{\n showMore\n ? $t('action.showLess')\n : $t('action.showMore', {\n count: uniquePages.length - 2,\n })\n }}\n </button>\n </li>\n </ul>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as e } from "./useShortUrlResolver-DI8ycJiO.js";
|
|
2
2
|
import { Fragment as t, computed as n, createBlock as r, createElementBlock as i, createElementVNode as a, defineComponent as o, normalizeClass as s, openBlock as c, renderList as l, resolveDynamicComponent as u, toDisplayString as d, withCtx as f } from "vue";
|
|
3
3
|
//#region ../../packages/components/src/PkUrl.vue?vue&type=script&setup=true&lang.ts
|
|
4
4
|
var p = {
|
|
@@ -80,4 +80,4 @@ var p = {
|
|
|
80
80
|
//#endregion
|
|
81
81
|
export { h as t };
|
|
82
82
|
|
|
83
|
-
//# sourceMappingURL=PkUrl-
|
|
83
|
+
//# sourceMappingURL=PkUrl-CN1kkZiH.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PkUrl-E12f7rn_.js","names":["_openBlock","_createBlock","_resolveDynamicComponent","_withCtx","_createElementBlock","_createElementVNode","_Fragment","_normalizeClass","_toDisplayString","_renderList","_sfc_main","useShortUrlResolver","_hoisted_1","_hoisted_2","PkUrl_default","defineComponent","props","__props","resolved","resolve","displayUrl","computed","isResolving","parsedUrl","urlObj","hostname","pathname","segments","segment","fallback","truncatedUrl","domain","allSegments","currentLength","i","openBlock","createBlock","resolveDynamicComponent","withCtx","createElementBlock","_cache","createElementVNode","Fragment","normalizeClass","toDisplayString","renderList","index"],"sources":["../../../../packages/components/src/PkUrl.vue","../../../../packages/components/src/PkUrl.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useShortUrlResolver } from 'composables'\n\n interface Props {\n url: string\n maxLength?: number\n domainClass?: string\n target?: '_blank' | '_self' | '_parent' | '_top'\n rel?: string\n muted?: boolean\n }\n\n const props = withDefaults(defineProps<Props>(), {\n maxLength: 60,\n domainClass: undefined,\n target: '_blank',\n rel: 'noopener noreferrer',\n muted: false,\n })\n\n // Only the displayed URL text is resolved to its real target; the href is\n // kept as the short url so the redirect endpoint still handles tracking.\n const { resolve } = useShortUrlResolver()\n const resolved = resolve(() => props.url)\n const displayUrl = computed(() => resolved.value.url)\n const isResolving = computed(() => resolved.value.loading)\n\n const parsedUrl = computed(() => {\n try {\n const urlObj = new URL(displayUrl.value ?? props.url)\n let hostname = urlObj.hostname\n\n // Remove www. prefix\n if (hostname.startsWith('www.')) {\n hostname = hostname.substring(4)\n }\n\n // Get pathname and remove trailing slash\n let pathname = urlObj.pathname\n if (pathname === '/') {\n pathname = ''\n }\n\n // Split pathname into segments\n const segments = pathname\n .split('/')\n .filter((segment) => segment.length > 0)\n\n return {\n domain: hostname,\n segments,\n fullPath: hostname + pathname,\n }\n } catch {\n // If URL parsing fails, return the original URL\n const fallback = displayUrl.value ?? props.url\n return {\n domain: fallback,\n segments: [],\n fullPath: fallback,\n }\n }\n })\n\n const truncatedUrl = computed(() => {\n if (parsedUrl.value.fullPath.length <= props.maxLength) {\n return parsedUrl.value\n }\n\n // If too long, truncate segments from the beginning\n const domain = parsedUrl.value.domain\n const allSegments = parsedUrl.value.segments\n\n // Always show at least the last segment\n if (allSegments.length === 0) {\n return {\n domain,\n segments: [],\n fullPath: domain,\n }\n }\n\n const segments: string[] = []\n\n // Calculate from the end\n let currentLength = domain.length\n\n // Try to fit segments from the end\n for (let i = allSegments.length - 1; i >= 0; i--) {\n const segment = allSegments[i]\n\n // Always include at least the last segment\n if (segments.length === 0) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else if (currentLength + segment.length + 1 <= props.maxLength) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else {\n segments.unshift('...')\n break\n }\n }\n\n return {\n domain,\n segments,\n fullPath: domain + '/' + segments.join('/'),\n }\n })\n</script>\n\n<template>\n <Component\n :is=\"muted ? 'span' : 'a'\"\n class=\"pk-url\"\n :href=\"muted ? undefined : props.url\"\n :target=\"muted ? undefined : props.target\"\n :rel=\"muted ? undefined : props.rel\">\n <span v-if=\"isResolving\" class=\"pk-url__skeleton vv-skeleton\">\n <span class=\"vv-skeleton__item\" />\n </span>\n <template v-else>\n <span :class=\"['pk-url__domain', domainClass]\">{{\n truncatedUrl.domain\n }}</span>\n <template\n v-for=\"(segment, index) in truncatedUrl.segments\"\n :key=\"index\">\n <span class=\"pk-url__separator\">›</span>\n <span class=\"pk-url__segment\">{{ segment }}</span>\n </template>\n </template>\n </Component>\n</template>\n\n<style lang=\"scss\">\n @layer volver.components {\n .pk-url {\n display: inline-flex;\n align-items: center;\n gap: 0.5ch;\n }\n\n .pk-url__skeleton {\n display: inline-flex;\n width: 12ch;\n max-width: 100%;\n\n .vv-skeleton__item {\n width: 100%;\n height: 1em;\n border-radius: 0.25em;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useShortUrlResolver } from 'composables'\n\n interface Props {\n url: string\n maxLength?: number\n domainClass?: string\n target?: '_blank' | '_self' | '_parent' | '_top'\n rel?: string\n muted?: boolean\n }\n\n const props = withDefaults(defineProps<Props>(), {\n maxLength: 60,\n domainClass: undefined,\n target: '_blank',\n rel: 'noopener noreferrer',\n muted: false,\n })\n\n // Only the displayed URL text is resolved to its real target; the href is\n // kept as the short url so the redirect endpoint still handles tracking.\n const { resolve } = useShortUrlResolver()\n const resolved = resolve(() => props.url)\n const displayUrl = computed(() => resolved.value.url)\n const isResolving = computed(() => resolved.value.loading)\n\n const parsedUrl = computed(() => {\n try {\n const urlObj = new URL(displayUrl.value ?? props.url)\n let hostname = urlObj.hostname\n\n // Remove www. prefix\n if (hostname.startsWith('www.')) {\n hostname = hostname.substring(4)\n }\n\n // Get pathname and remove trailing slash\n let pathname = urlObj.pathname\n if (pathname === '/') {\n pathname = ''\n }\n\n // Split pathname into segments\n const segments = pathname\n .split('/')\n .filter((segment) => segment.length > 0)\n\n return {\n domain: hostname,\n segments,\n fullPath: hostname + pathname,\n }\n } catch {\n // If URL parsing fails, return the original URL\n const fallback = displayUrl.value ?? props.url\n return {\n domain: fallback,\n segments: [],\n fullPath: fallback,\n }\n }\n })\n\n const truncatedUrl = computed(() => {\n if (parsedUrl.value.fullPath.length <= props.maxLength) {\n return parsedUrl.value\n }\n\n // If too long, truncate segments from the beginning\n const domain = parsedUrl.value.domain\n const allSegments = parsedUrl.value.segments\n\n // Always show at least the last segment\n if (allSegments.length === 0) {\n return {\n domain,\n segments: [],\n fullPath: domain,\n }\n }\n\n const segments: string[] = []\n\n // Calculate from the end\n let currentLength = domain.length\n\n // Try to fit segments from the end\n for (let i = allSegments.length - 1; i >= 0; i--) {\n const segment = allSegments[i]\n\n // Always include at least the last segment\n if (segments.length === 0) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else if (currentLength + segment.length + 1 <= props.maxLength) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else {\n segments.unshift('...')\n break\n }\n }\n\n return {\n domain,\n segments,\n fullPath: domain + '/' + segments.join('/'),\n }\n })\n</script>\n\n<template>\n <Component\n :is=\"muted ? 'span' : 'a'\"\n class=\"pk-url\"\n :href=\"muted ? undefined : props.url\"\n :target=\"muted ? undefined : props.target\"\n :rel=\"muted ? undefined : props.rel\">\n <span v-if=\"isResolving\" class=\"pk-url__skeleton vv-skeleton\">\n <span class=\"vv-skeleton__item\" />\n </span>\n <template v-else>\n <span :class=\"['pk-url__domain', domainClass]\">{{\n truncatedUrl.domain\n }}</span>\n <template\n v-for=\"(segment, index) in truncatedUrl.segments\"\n :key=\"index\">\n <span class=\"pk-url__separator\">›</span>\n <span class=\"pk-url__segment\">{{ segment }}</span>\n </template>\n </template>\n </Component>\n</template>\n\n<style lang=\"scss\">\n @layer volver.components {\n .pk-url {\n display: inline-flex;\n align-items: center;\n gap: 0.5ch;\n }\n\n .pk-url__skeleton {\n display: inline-flex;\n width: 12ch;\n max-width: 100%;\n\n .vv-skeleton__item {\n width: 100%;\n height: 1em;\n border-radius: 0.25em;\n }\n }\n }\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAaI,IAAMgB,IAAQC,GAUR,EAAE,eAAYN,EAAoB,GAClCO,IAAWC,QAAcH,EAAM,GAAG,GAClCI,IAAaC,QAAeH,EAAS,MAAM,GAAG,GAC9CI,IAAcD,QAAeH,EAAS,MAAM,OAAO,GAEnDK,IAAYF,QAAe;GAC7B,IAAI;IACA,IAAMG,IAAS,IAAI,IAAIJ,EAAW,SAASJ,EAAM,GAAG,GAChDS,IAAWD,EAAO;IAGtB,AAAIC,EAAS,WAAW,MAAM,MAC1B,IAAWA,EAAS,UAAU,CAAC;IAInC,IAAIC,IAAWF,EAAO;IACtB,AAAIE,MAAa,QACb,IAAW;IAIf,IAAMC,IAAWD,EACZ,MAAM,GAAG,CAAA,CACT,QAAQ,MAAYE,EAAQ,SAAS,CAAC;IAE3C,OAAO;KACH,QAAQH;KACR;KACA,UAAUA,IAAWC;IACzB;GACJ,QAAQ;IAEJ,IAAMG,IAAWT,EAAW,SAASJ,EAAM;IAC3C,OAAO;KACH,QAAQa;KACR,UAAU,CAAC;KACX,UAAUA;IACd;GACJ;EACJ,CAAC,GAEKC,IAAeT,QAAe;GAChC,IAAIE,EAAU,MAAM,SAAS,UAAUP,EAAM,WACzC,OAAOO,EAAU;GAIrB,IAAMQ,IAASR,EAAU,MAAM,QACzBS,IAAcT,EAAU,MAAM;GAGpC,IAAIS,EAAY,WAAW,GACvB,OAAO;IACH;IACA,UAAU,CAAC;IACX,UAAUD;GACd;GAGJ,IAAMJ,IAAqB,CAAC,GAGxBM,IAAgBF,EAAO;GAG3B,KAAK,IAAIG,IAAIF,EAAY,SAAS,GAAGE,KAAK,GAAG,KAAK;IAC9C,IAAMN,IAAUI,EAAYE;IAG5B,IAAIP,EAAS,WAAW,GAEpB,AADA,KAAiBC,EAAQ,SAAS,GAClCD,EAAS,QAAQC,CAAO;SACrB,IAAIK,IAAgBL,EAAQ,SAAS,KAAKZ,EAAM,WAEnD,AADA,KAAiBY,EAAQ,SAAS,GAClCD,EAAS,QAAQC,CAAO;SACrB;KACH,EAAS,QAAQ,KAAK;KACtB;IACJ;GACJ;GAEA,OAAO;IACH;IACA;IACA,UAAUG,IAAS,MAAMJ,EAAS,KAAK,GAAG;GAC9C;EACJ,CAAC;oBAID3B,EAAA,GAAAC,EAoBYC,EAnBHe,EAAA,QAAK,SAAA,GAAA,GAAA;GACV,OAAM;GACL,MAAMA,EAAA,QAAQ,KAAA,IAAYD,EAAM;GAChC,QAAQC,EAAA,QAAQ,KAAA,IAAYD,EAAM;GAClC,KAAKC,EAAA,QAAQ,KAAA,IAAYD,EAAM;;GAChC,SAAAb,QAEO,CAFKmB,EAAA,SAAZtB,EAAA,GAAAI,EAEO,QAFPQ,GAEO,CAAA,GAAA,AAAA4B,EAAA,OAAA,CADHnC,EAAkC,QAAA,EAA5B,OAAM,oBAAmB,GAAA,MAAA,EAAA,CAAA,CAAA,CAAA,MAEnCL,EAAA,GAAAI,EAUWE,GAAA,EAAA,KAAA,EAAA,GAAA,CATPD,EAES,QAAA,EAFF,OAAKE,EAAA,CAAA,kBAAqBU,EAAA,WAAW,CAAA,EACxC,GAAAT,EAAAsB,EAAA,MAAa,MAAM,GAAA,CAAA,IAEvB9B,EAAA,EAAA,GAAAI,EAKWE,GAAA,MAAAG,EAJoBqB,EAAA,MAAa,WAAhC,GAAS,OACX9B,EAAA,GAAAI,EAAAE,GAAA,EAAA,KAAAwC,EAAK,GAAA,CACX,AAAAN,EAAA,OAAAnC,EAAwC,QAAA,EAAlC,OAAM,oBAAmB,GAAC,KAAC,EAAA,GACjCA,EAAkD,QAAlDQ,GAAkDL,EAAjBoB,CAAO,GAAA,CAAA,CAAA,GAAA,EAAA"}
|
|
1
|
+
{"version":3,"file":"PkUrl-CN1kkZiH.js","names":["_openBlock","_createBlock","_resolveDynamicComponent","_withCtx","_createElementBlock","_createElementVNode","_Fragment","_normalizeClass","_toDisplayString","_renderList","_sfc_main","useShortUrlResolver","_hoisted_1","_hoisted_2","PkUrl_default","defineComponent","props","__props","resolved","resolve","displayUrl","computed","isResolving","parsedUrl","urlObj","hostname","pathname","segments","segment","fallback","truncatedUrl","domain","allSegments","currentLength","i","openBlock","createBlock","resolveDynamicComponent","withCtx","createElementBlock","_cache","createElementVNode","Fragment","normalizeClass","toDisplayString","renderList","index"],"sources":["../../../../packages/components/src/PkUrl.vue","../../../../packages/components/src/PkUrl.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useShortUrlResolver } from 'composables'\n\n interface Props {\n url: string\n maxLength?: number\n domainClass?: string\n target?: '_blank' | '_self' | '_parent' | '_top'\n rel?: string\n muted?: boolean\n }\n\n const props = withDefaults(defineProps<Props>(), {\n maxLength: 60,\n domainClass: undefined,\n target: '_blank',\n rel: 'noopener noreferrer',\n muted: false,\n })\n\n // Only the displayed URL text is resolved to its real target; the href is\n // kept as the short url so the redirect endpoint still handles tracking.\n const { resolve } = useShortUrlResolver()\n const resolved = resolve(() => props.url)\n const displayUrl = computed(() => resolved.value.url)\n const isResolving = computed(() => resolved.value.loading)\n\n const parsedUrl = computed(() => {\n try {\n const urlObj = new URL(displayUrl.value ?? props.url)\n let hostname = urlObj.hostname\n\n // Remove www. prefix\n if (hostname.startsWith('www.')) {\n hostname = hostname.substring(4)\n }\n\n // Get pathname and remove trailing slash\n let pathname = urlObj.pathname\n if (pathname === '/') {\n pathname = ''\n }\n\n // Split pathname into segments\n const segments = pathname\n .split('/')\n .filter((segment) => segment.length > 0)\n\n return {\n domain: hostname,\n segments,\n fullPath: hostname + pathname,\n }\n } catch {\n // If URL parsing fails, return the original URL\n const fallback = displayUrl.value ?? props.url\n return {\n domain: fallback,\n segments: [],\n fullPath: fallback,\n }\n }\n })\n\n const truncatedUrl = computed(() => {\n if (parsedUrl.value.fullPath.length <= props.maxLength) {\n return parsedUrl.value\n }\n\n // If too long, truncate segments from the beginning\n const domain = parsedUrl.value.domain\n const allSegments = parsedUrl.value.segments\n\n // Always show at least the last segment\n if (allSegments.length === 0) {\n return {\n domain,\n segments: [],\n fullPath: domain,\n }\n }\n\n const segments: string[] = []\n\n // Calculate from the end\n let currentLength = domain.length\n\n // Try to fit segments from the end\n for (let i = allSegments.length - 1; i >= 0; i--) {\n const segment = allSegments[i]\n\n // Always include at least the last segment\n if (segments.length === 0) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else if (currentLength + segment.length + 1 <= props.maxLength) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else {\n segments.unshift('...')\n break\n }\n }\n\n return {\n domain,\n segments,\n fullPath: domain + '/' + segments.join('/'),\n }\n })\n</script>\n\n<template>\n <Component\n :is=\"muted ? 'span' : 'a'\"\n class=\"pk-url\"\n :href=\"muted ? undefined : props.url\"\n :target=\"muted ? undefined : props.target\"\n :rel=\"muted ? undefined : props.rel\">\n <span v-if=\"isResolving\" class=\"pk-url__skeleton vv-skeleton\">\n <span class=\"vv-skeleton__item\" />\n </span>\n <template v-else>\n <span :class=\"['pk-url__domain', domainClass]\">{{\n truncatedUrl.domain\n }}</span>\n <template\n v-for=\"(segment, index) in truncatedUrl.segments\"\n :key=\"index\">\n <span class=\"pk-url__separator\">›</span>\n <span class=\"pk-url__segment\">{{ segment }}</span>\n </template>\n </template>\n </Component>\n</template>\n\n<style lang=\"scss\">\n @layer volver.components {\n .pk-url {\n display: inline-flex;\n align-items: center;\n gap: 0.5ch;\n }\n\n .pk-url__skeleton {\n display: inline-flex;\n width: 12ch;\n max-width: 100%;\n\n .vv-skeleton__item {\n width: 100%;\n height: 1em;\n border-radius: 0.25em;\n }\n }\n }\n</style>\n","<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useShortUrlResolver } from 'composables'\n\n interface Props {\n url: string\n maxLength?: number\n domainClass?: string\n target?: '_blank' | '_self' | '_parent' | '_top'\n rel?: string\n muted?: boolean\n }\n\n const props = withDefaults(defineProps<Props>(), {\n maxLength: 60,\n domainClass: undefined,\n target: '_blank',\n rel: 'noopener noreferrer',\n muted: false,\n })\n\n // Only the displayed URL text is resolved to its real target; the href is\n // kept as the short url so the redirect endpoint still handles tracking.\n const { resolve } = useShortUrlResolver()\n const resolved = resolve(() => props.url)\n const displayUrl = computed(() => resolved.value.url)\n const isResolving = computed(() => resolved.value.loading)\n\n const parsedUrl = computed(() => {\n try {\n const urlObj = new URL(displayUrl.value ?? props.url)\n let hostname = urlObj.hostname\n\n // Remove www. prefix\n if (hostname.startsWith('www.')) {\n hostname = hostname.substring(4)\n }\n\n // Get pathname and remove trailing slash\n let pathname = urlObj.pathname\n if (pathname === '/') {\n pathname = ''\n }\n\n // Split pathname into segments\n const segments = pathname\n .split('/')\n .filter((segment) => segment.length > 0)\n\n return {\n domain: hostname,\n segments,\n fullPath: hostname + pathname,\n }\n } catch {\n // If URL parsing fails, return the original URL\n const fallback = displayUrl.value ?? props.url\n return {\n domain: fallback,\n segments: [],\n fullPath: fallback,\n }\n }\n })\n\n const truncatedUrl = computed(() => {\n if (parsedUrl.value.fullPath.length <= props.maxLength) {\n return parsedUrl.value\n }\n\n // If too long, truncate segments from the beginning\n const domain = parsedUrl.value.domain\n const allSegments = parsedUrl.value.segments\n\n // Always show at least the last segment\n if (allSegments.length === 0) {\n return {\n domain,\n segments: [],\n fullPath: domain,\n }\n }\n\n const segments: string[] = []\n\n // Calculate from the end\n let currentLength = domain.length\n\n // Try to fit segments from the end\n for (let i = allSegments.length - 1; i >= 0; i--) {\n const segment = allSegments[i]\n\n // Always include at least the last segment\n if (segments.length === 0) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else if (currentLength + segment.length + 1 <= props.maxLength) {\n currentLength += segment.length + 1\n segments.unshift(segment)\n } else {\n segments.unshift('...')\n break\n }\n }\n\n return {\n domain,\n segments,\n fullPath: domain + '/' + segments.join('/'),\n }\n })\n</script>\n\n<template>\n <Component\n :is=\"muted ? 'span' : 'a'\"\n class=\"pk-url\"\n :href=\"muted ? undefined : props.url\"\n :target=\"muted ? undefined : props.target\"\n :rel=\"muted ? undefined : props.rel\">\n <span v-if=\"isResolving\" class=\"pk-url__skeleton vv-skeleton\">\n <span class=\"vv-skeleton__item\" />\n </span>\n <template v-else>\n <span :class=\"['pk-url__domain', domainClass]\">{{\n truncatedUrl.domain\n }}</span>\n <template\n v-for=\"(segment, index) in truncatedUrl.segments\"\n :key=\"index\">\n <span class=\"pk-url__separator\">›</span>\n <span class=\"pk-url__segment\">{{ segment }}</span>\n </template>\n </template>\n </Component>\n</template>\n\n<style lang=\"scss\">\n @layer volver.components {\n .pk-url {\n display: inline-flex;\n align-items: center;\n gap: 0.5ch;\n }\n\n .pk-url__skeleton {\n display: inline-flex;\n width: 12ch;\n max-width: 100%;\n\n .vv-skeleton__item {\n width: 100%;\n height: 1em;\n border-radius: 0.25em;\n }\n }\n }\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAaI,IAAMgB,IAAQC,GAUR,EAAE,eAAYN,EAAoB,GAClCO,IAAWC,QAAcH,EAAM,GAAG,GAClCI,IAAaC,QAAeH,EAAS,MAAM,GAAG,GAC9CI,IAAcD,QAAeH,EAAS,MAAM,OAAO,GAEnDK,IAAYF,QAAe;GAC7B,IAAI;IACA,IAAMG,IAAS,IAAI,IAAIJ,EAAW,SAASJ,EAAM,GAAG,GAChDS,IAAWD,EAAO;IAGtB,AAAIC,EAAS,WAAW,MAAM,MAC1B,IAAWA,EAAS,UAAU,CAAC;IAInC,IAAIC,IAAWF,EAAO;IACtB,AAAIE,MAAa,QACb,IAAW;IAIf,IAAMC,IAAWD,EACZ,MAAM,GAAG,CAAA,CACT,QAAQ,MAAYE,EAAQ,SAAS,CAAC;IAE3C,OAAO;KACH,QAAQH;KACR;KACA,UAAUA,IAAWC;IACzB;GACJ,QAAQ;IAEJ,IAAMG,IAAWT,EAAW,SAASJ,EAAM;IAC3C,OAAO;KACH,QAAQa;KACR,UAAU,CAAC;KACX,UAAUA;IACd;GACJ;EACJ,CAAC,GAEKC,IAAeT,QAAe;GAChC,IAAIE,EAAU,MAAM,SAAS,UAAUP,EAAM,WACzC,OAAOO,EAAU;GAIrB,IAAMQ,IAASR,EAAU,MAAM,QACzBS,IAAcT,EAAU,MAAM;GAGpC,IAAIS,EAAY,WAAW,GACvB,OAAO;IACH;IACA,UAAU,CAAC;IACX,UAAUD;GACd;GAGJ,IAAMJ,IAAqB,CAAC,GAGxBM,IAAgBF,EAAO;GAG3B,KAAK,IAAIG,IAAIF,EAAY,SAAS,GAAGE,KAAK,GAAG,KAAK;IAC9C,IAAMN,IAAUI,EAAYE;IAG5B,IAAIP,EAAS,WAAW,GAEpB,AADA,KAAiBC,EAAQ,SAAS,GAClCD,EAAS,QAAQC,CAAO;SACrB,IAAIK,IAAgBL,EAAQ,SAAS,KAAKZ,EAAM,WAEnD,AADA,KAAiBY,EAAQ,SAAS,GAClCD,EAAS,QAAQC,CAAO;SACrB;KACH,EAAS,QAAQ,KAAK;KACtB;IACJ;GACJ;GAEA,OAAO;IACH;IACA;IACA,UAAUG,IAAS,MAAMJ,EAAS,KAAK,GAAG;GAC9C;EACJ,CAAC;oBAID3B,EAAA,GAAAC,EAoBYC,EAnBHe,EAAA,QAAK,SAAA,GAAA,GAAA;GACV,OAAM;GACL,MAAMA,EAAA,QAAQ,KAAA,IAAYD,EAAM;GAChC,QAAQC,EAAA,QAAQ,KAAA,IAAYD,EAAM;GAClC,KAAKC,EAAA,QAAQ,KAAA,IAAYD,EAAM;;GAChC,SAAAb,QAEO,CAFKmB,EAAA,SAAZtB,EAAA,GAAAI,EAEO,QAFPQ,GAEO,CAAA,GAAA,AAAA4B,EAAA,OAAA,CADHnC,EAAkC,QAAA,EAA5B,OAAM,oBAAmB,GAAA,MAAA,EAAA,CAAA,CAAA,CAAA,MAEnCL,EAAA,GAAAI,EAUWE,GAAA,EAAA,KAAA,EAAA,GAAA,CATPD,EAES,QAAA,EAFF,OAAKE,EAAA,CAAA,kBAAqBU,EAAA,WAAW,CAAA,EACxC,GAAAT,EAAAsB,EAAA,MAAa,MAAM,GAAA,CAAA,IAEvB9B,EAAA,EAAA,GAAAI,EAKWE,GAAA,MAAAG,EAJoBqB,EAAA,MAAa,WAAhC,GAAS,OACX9B,EAAA,GAAAI,EAAAE,GAAA,EAAA,KAAAwC,EAAK,GAAA,CACX,AAAAN,EAAA,OAAAnC,EAAwC,QAAA,EAAlC,OAAM,oBAAmB,GAAC,KAAC,EAAA,GACjCA,EAAkD,QAAlDQ,GAAkDL,EAAjBoB,CAAO,GAAA,CAAA,CAAA,GAAA,EAAA"}
|
package/dist-vue/_chunks/{createChatbotApiClient-B_mkyrEl.js → createChatbotApiClient-B2PvUG07.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as e, S as t, _ as n, a as r, b as i, g as a, l as o, n as s, o as c, p as l, y as u } from "./schemas-BIfOAo0P.js";
|
|
2
|
-
import { i as d, t as
|
|
3
|
-
import {
|
|
2
|
+
import { i as d, t as f } from "./Media-Dk4odudr.js";
|
|
3
|
+
import { A as ee, M as te, N as p, a as m, b as h, c as g, f as ne, l as re, n as ie, s as ae } from "./Agent-C4p46YGt.js";
|
|
4
4
|
import { t as oe } from "./StreamError-D1AtGgS3.js";
|
|
5
5
|
import "./src-DT8FLE4_.js";
|
|
6
6
|
import { r as _, t as se } from "./url-validation-9DstIfq2.js";
|
|
@@ -47,7 +47,7 @@ var me = /* @__PURE__ */ function(e) {
|
|
|
47
47
|
text: i()
|
|
48
48
|
}), D = n({
|
|
49
49
|
type: l("file"),
|
|
50
|
-
mediaType: i().regex(
|
|
50
|
+
mediaType: i().regex(f),
|
|
51
51
|
url: e(),
|
|
52
52
|
filename: i().optional(),
|
|
53
53
|
providerMetadata: u(i(), u(i(), t())).optional()
|
|
@@ -70,13 +70,13 @@ n({
|
|
|
70
70
|
searchMode: s(re).optional(),
|
|
71
71
|
limit: a().optional(),
|
|
72
72
|
ranking: ae.optional(),
|
|
73
|
-
reasoning:
|
|
74
|
-
webSearch:
|
|
73
|
+
reasoning: g,
|
|
74
|
+
webSearch: ne.optional(),
|
|
75
75
|
maxStepCount: a().int().min(1).max(40).optional(),
|
|
76
76
|
disabledTools: r(s(ie)).optional(),
|
|
77
77
|
fallbackModels: r(i()).optional(),
|
|
78
|
-
gatewayOptions:
|
|
79
|
-
deviceContext:
|
|
78
|
+
gatewayOptions: m.partial().optional(),
|
|
79
|
+
deviceContext: ee.optional(),
|
|
80
80
|
externalContext: u(i(), t()).optional(),
|
|
81
81
|
smoothStream: c().optional()
|
|
82
82
|
}), n({
|
|
@@ -104,7 +104,7 @@ function A(e) {
|
|
|
104
104
|
}
|
|
105
105
|
continue;
|
|
106
106
|
}
|
|
107
|
-
let n =
|
|
107
|
+
let n = te(e, r.keys()), s = n && r.get(n);
|
|
108
108
|
s && (t.state === "output-error" && oe.test(t.errorText ?? "") ? o.add(s) : a.add(s));
|
|
109
109
|
}
|
|
110
110
|
let s = new Set(n.selected), c = new Set(n.seeded), l = t.groups.map((e) => ({
|
|
@@ -161,16 +161,16 @@ var j = [
|
|
|
161
161
|
lastMessageAt: d()
|
|
162
162
|
});
|
|
163
163
|
n({
|
|
164
|
-
...
|
|
165
|
-
...
|
|
164
|
+
...p.pagination(),
|
|
165
|
+
...p.sort([
|
|
166
166
|
"updatedAt",
|
|
167
167
|
"createdAt",
|
|
168
168
|
"lastMessageAt"
|
|
169
169
|
]),
|
|
170
|
-
...
|
|
171
|
-
...
|
|
172
|
-
...
|
|
173
|
-
...
|
|
170
|
+
...p.filters(["agentId", "userId"]),
|
|
171
|
+
...p.range("updatedAt"),
|
|
172
|
+
...p.range("lastMessageAt"),
|
|
173
|
+
...p.ids(),
|
|
174
174
|
startDate: i().optional(),
|
|
175
175
|
endDate: i().optional()
|
|
176
176
|
});
|
|
@@ -553,7 +553,7 @@ function ze(e) {
|
|
|
553
553
|
return i(`/agents/${encodeURIComponent(e)}/mcp-oauth/status?${n}`);
|
|
554
554
|
},
|
|
555
555
|
async createVoiceSession(e, t, n) {
|
|
556
|
-
return
|
|
556
|
+
return h.parse(await i(`/agents/${encodeURIComponent(e)}/voice/session`, {
|
|
557
557
|
method: "POST",
|
|
558
558
|
headers: { "Content-Type": "application/json" },
|
|
559
559
|
body: JSON.stringify({
|
|
@@ -580,4 +580,4 @@ function ze(e) {
|
|
|
580
580
|
//#endregion
|
|
581
581
|
export { A as a, j as i, Z as n, M as r, ze as t };
|
|
582
582
|
|
|
583
|
-
//# sourceMappingURL=createChatbotApiClient-
|
|
583
|
+
//# sourceMappingURL=createChatbotApiClient-B2PvUG07.js.map
|