@8wave/ai-elements 0.112.0-beta.9 → 0.114.0-beta.14

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 (139) hide show
  1. package/dist/_chunks/Agent-D3ODaN3E.js +566 -0
  2. package/dist/_chunks/Agent-D3ODaN3E.js.map +1 -0
  3. package/dist/_chunks/{PkStreamingMarkdown-CRlRDGYl.js → PkStreamingMarkdown-CYOm47PX.js} +1276 -453
  4. package/dist/_chunks/PkStreamingMarkdown-CYOm47PX.js.map +1 -0
  5. package/dist/_chunks/{PkToolRequestGeolocation-BbxYKt-9.js → PkToolRequestGeolocation-BY7nXImb.js} +2 -2
  6. package/dist/_chunks/{PkToolRequestGeolocation-BbxYKt-9.js.map → PkToolRequestGeolocation-BY7nXImb.js.map} +1 -1
  7. package/dist/_chunks/{PkToolRequestOAuthConnection-CTp2w60F.js → PkToolRequestOAuthConnection-DFOTALFt.js} +2 -2
  8. package/dist/_chunks/{PkToolRequestOAuthConnection-CTp2w60F.js.map → PkToolRequestOAuthConnection-DFOTALFt.js.map} +1 -1
  9. package/dist/_chunks/{PkToolShowArtifact-CNnt82aH.js → PkToolShowArtifact-DzHRWoru.js} +2 -2
  10. package/dist/_chunks/{PkToolShowArtifact-CNnt82aH.js.map → PkToolShowArtifact-DzHRWoru.js.map} +1 -1
  11. package/dist/_chunks/{PkToolShowContactForm-D77gyjQG.js → PkToolShowContactForm-1J6_1AA2.js} +2 -2
  12. package/dist/_chunks/{PkToolShowContactForm-D77gyjQG.js.map → PkToolShowContactForm-1J6_1AA2.js.map} +1 -1
  13. package/dist/_chunks/{PkToolShowForm-DP4LLAk3.js → PkToolShowForm-DmQYj3v1.js} +5 -5
  14. package/dist/_chunks/{PkToolShowForm-DP4LLAk3.js.map → PkToolShowForm-DmQYj3v1.js.map} +1 -1
  15. package/dist/_chunks/{PkToolShowSources-Bwy0Ptmi.js → PkToolShowSources-BO8V5rlS.js} +3 -3
  16. package/dist/_chunks/{PkToolShowSources-Bwy0Ptmi.js.map → PkToolShowSources-BO8V5rlS.js.map} +1 -1
  17. package/dist/_chunks/{PkToolShowSuggestedReply-CFO8wV1b.js → PkToolShowSuggestedReply-CTWVG1Zk.js} +2 -2
  18. package/dist/_chunks/{PkToolShowSuggestedReply-CFO8wV1b.js.map → PkToolShowSuggestedReply-CTWVG1Zk.js.map} +1 -1
  19. package/dist/_chunks/{PkToolShowVideo-Djr8Ofsz.js → PkToolShowVideo-p6Y9j3-B.js} +3 -3
  20. package/dist/_chunks/PkToolShowVideo-p6Y9j3-B.js.map +1 -0
  21. package/dist/_chunks/{browser-B0Xnbqi7.js → browser-Ct5TRDgj.js} +521 -518
  22. package/dist/_chunks/{browser-B0Xnbqi7.js.map → browser-Ct5TRDgj.js.map} +1 -1
  23. package/dist/_chunks/{markdown-BRD-hDrH.js → markdown-JpLFzufn.js} +7 -7
  24. package/dist/_chunks/markdown-JpLFzufn.js.map +1 -0
  25. package/dist/_chunks/url-validation-uLMZ5-AU.js.map +1 -1
  26. package/dist/_chunks/{web-Dzr6HJ14.js → web-CEmtEYdH.js} +89 -65
  27. package/dist/_chunks/web-CEmtEYdH.js.map +1 -0
  28. package/dist/ai-elements.es.js +2109 -2321
  29. package/dist/ai-elements.es.js.map +1 -1
  30. package/dist-vue/PkChatbot.js +1 -1
  31. package/dist-vue/PkChatbotAuth.js +1 -1
  32. package/dist-vue/PkChatbotEmptyState.js +1 -1
  33. package/dist-vue/PkChatbotInput.js +1 -1
  34. package/dist-vue/PkChatbotMessages.js +1 -1
  35. package/dist-vue/PkChatbotStepPayload.js +1 -1
  36. package/dist-vue/PkChatbotStepToolDetail.js +1 -1
  37. package/dist-vue/PkChatbotSteps.js +1 -1
  38. package/dist-vue/PkChatbotViewChat.js +1 -1
  39. package/dist-vue/PkChatbotViewConversations.js +1 -1
  40. package/dist-vue/PkChatbotViewFiles.js +1 -1
  41. package/dist-vue/PkChatbotViewProfile.js +1 -1
  42. package/dist-vue/_chunks/{Document-B6RpxFr9.js → Document-2X01-F4Z.js} +2 -2
  43. package/dist-vue/_chunks/Document-2X01-F4Z.js.map +1 -0
  44. package/dist-vue/_chunks/Media-_HfFLHGD.js.map +1 -1
  45. package/dist-vue/_chunks/{PkChatbot-CI9ucJ2M.js → PkChatbot-BdvDl_Gw.js} +8 -8
  46. package/dist-vue/_chunks/{PkChatbot-CI9ucJ2M.js.map → PkChatbot-BdvDl_Gw.js.map} +1 -1
  47. package/dist-vue/_chunks/{PkChatbotAuth-AxAOI9t2.js → PkChatbotAuth-Bi508F1u.js} +57 -57
  48. package/dist-vue/_chunks/PkChatbotAuth-Bi508F1u.js.map +1 -0
  49. package/dist-vue/_chunks/{PkChatbotEmptyState-CacY4o02.js → PkChatbotEmptyState-DycCrfwz.js} +2 -2
  50. package/dist-vue/_chunks/{PkChatbotEmptyState-CacY4o02.js.map → PkChatbotEmptyState-DycCrfwz.js.map} +1 -1
  51. package/dist-vue/_chunks/{PkChatbotInput-Dkz3SNAk.js → PkChatbotInput-BCuqLdkT.js} +2 -2
  52. package/dist-vue/_chunks/{PkChatbotInput-Dkz3SNAk.js.map → PkChatbotInput-BCuqLdkT.js.map} +1 -1
  53. package/dist-vue/_chunks/PkChatbotMessages-PjKRDNYJ.js +776 -0
  54. package/dist-vue/_chunks/PkChatbotMessages-PjKRDNYJ.js.map +1 -0
  55. package/dist-vue/_chunks/{PkChatbotStepPayload-DU0W-eUp.js → PkChatbotStepPayload-CI98RiNv.js} +2 -2
  56. package/dist-vue/_chunks/{PkChatbotStepPayload-DU0W-eUp.js.map → PkChatbotStepPayload-CI98RiNv.js.map} +1 -1
  57. package/dist-vue/_chunks/{PkChatbotStepToolDetail-etlBryOg.js → PkChatbotStepToolDetail-B8zXJOdI.js} +3 -3
  58. package/dist-vue/_chunks/{PkChatbotStepToolDetail-etlBryOg.js.map → PkChatbotStepToolDetail-B8zXJOdI.js.map} +1 -1
  59. package/dist-vue/_chunks/PkChatbotSteps-CL27pMFS.js +190 -0
  60. package/dist-vue/_chunks/{PkChatbotSteps-jW07Q-4x.js.map → PkChatbotSteps-CL27pMFS.js.map} +1 -1
  61. package/dist-vue/_chunks/{PkChatbotViewChat-jEBBPmSo.js → PkChatbotViewChat-CS-OSJfX.js} +8 -8
  62. package/dist-vue/_chunks/{PkChatbotViewChat-jEBBPmSo.js.map → PkChatbotViewChat-CS-OSJfX.js.map} +1 -1
  63. package/dist-vue/_chunks/{PkChatbotViewConversations-CWSrP36z.js → PkChatbotViewConversations-BCdolW0R.js} +2 -2
  64. package/dist-vue/_chunks/{PkChatbotViewConversations-CWSrP36z.js.map → PkChatbotViewConversations-BCdolW0R.js.map} +1 -1
  65. package/dist-vue/_chunks/{PkChatbotViewFiles-DzLp1WfU.js → PkChatbotViewFiles-Cudao8ew.js} +3 -3
  66. package/dist-vue/_chunks/{PkChatbotViewFiles-DzLp1WfU.js.map → PkChatbotViewFiles-Cudao8ew.js.map} +1 -1
  67. package/dist-vue/_chunks/{PkChatbotViewProfile-CHbn3W6F.js → PkChatbotViewProfile-DDsKOkcC.js} +2 -2
  68. package/dist-vue/_chunks/{PkChatbotViewProfile-CHbn3W6F.js.map → PkChatbotViewProfile-DDsKOkcC.js.map} +1 -1
  69. package/dist-vue/_chunks/PkStreamingMarkdown-B2iju0WK.js +3245 -0
  70. package/dist-vue/_chunks/PkStreamingMarkdown-B2iju0WK.js.map +1 -0
  71. package/dist-vue/_chunks/{PkToolShowArtifact-BnwU7CV2.js → PkToolShowArtifact-C0J71YF6.js} +2 -2
  72. package/dist-vue/_chunks/{PkToolShowArtifact-BnwU7CV2.js.map → PkToolShowArtifact-C0J71YF6.js.map} +1 -1
  73. package/dist-vue/_chunks/{PkToolShowContactForm-_x_dbiHn.js → PkToolShowContactForm-De3Od9mC.js} +2 -2
  74. package/dist-vue/_chunks/{PkToolShowContactForm-_x_dbiHn.js.map → PkToolShowContactForm-De3Od9mC.js.map} +1 -1
  75. package/dist-vue/_chunks/{PkToolShowDiagram-CjJcLvWs.js → PkToolShowDiagram-DmY_hhAS.js} +2 -2
  76. package/dist-vue/_chunks/{PkToolShowDiagram-CjJcLvWs.js.map → PkToolShowDiagram-DmY_hhAS.js.map} +1 -1
  77. package/dist-vue/_chunks/{PkToolShowEmail-CRQjoJr1.js → PkToolShowEmail-D_Z15wCS.js} +2 -2
  78. package/dist-vue/_chunks/{PkToolShowEmail-CRQjoJr1.js.map → PkToolShowEmail-D_Z15wCS.js.map} +1 -1
  79. package/dist-vue/_chunks/{PkToolShowForm-IPAkJIXh.js → PkToolShowForm-DUGysIxj.js} +3 -3
  80. package/dist-vue/_chunks/{PkToolShowForm-IPAkJIXh.js.map → PkToolShowForm-DUGysIxj.js.map} +1 -1
  81. package/dist-vue/_chunks/{PkToolShowMessage-joTzAg9N.js → PkToolShowMessage-e5_EI5mV.js} +2 -2
  82. package/dist-vue/_chunks/{PkToolShowMessage-joTzAg9N.js.map → PkToolShowMessage-e5_EI5mV.js.map} +1 -1
  83. package/dist-vue/_chunks/{PkToolShowProductList-tOYjd2bq.js → PkToolShowProductList-B3X7Svyc.js} +2 -2
  84. package/dist-vue/_chunks/{PkToolShowProductList-tOYjd2bq.js.map → PkToolShowProductList-B3X7Svyc.js.map} +1 -1
  85. package/dist-vue/_chunks/{PkToolShowSources-Cc26LzRd.js → PkToolShowSources-BUjBSGc5.js} +4 -4
  86. package/dist-vue/_chunks/{PkToolShowSources-Cc26LzRd.js.map → PkToolShowSources-BUjBSGc5.js.map} +1 -1
  87. package/dist-vue/_chunks/{PkToolShowVideo-Bv9kMd88.js → PkToolShowVideo-BZOTu2Qe.js} +3 -3
  88. package/dist-vue/_chunks/PkToolShowVideo-BZOTu2Qe.js.map +1 -0
  89. package/dist-vue/_chunks/{PkToolShowWebPages-D3Xy4iUs.js → PkToolShowWebPages-DEih8h5k.js} +2 -2
  90. package/dist-vue/_chunks/{PkToolShowWebPages-D3Xy4iUs.js.map → PkToolShowWebPages-DEih8h5k.js.map} +1 -1
  91. package/dist-vue/_chunks/{PkUrl-BgMBemSz.js → PkUrl-3NH9t-WA.js} +2 -2
  92. package/dist-vue/_chunks/{PkUrl-BgMBemSz.js.map → PkUrl-3NH9t-WA.js.map} +1 -1
  93. package/dist-vue/_chunks/url-validation-uLMZ5-AU.js.map +1 -1
  94. package/dist-vue/_chunks/{useChatbotStore-CMS2zFht.js → useChatbotStore-BwrBZJCq.js} +944 -1259
  95. package/dist-vue/_chunks/useChatbotStore-BwrBZJCq.js.map +1 -0
  96. package/dist-vue/_chunks/utils-BL4Ppr2R.js +184 -0
  97. package/dist-vue/_chunks/utils-BL4Ppr2R.js.map +1 -0
  98. package/dist-vue/_chunks/{web-Dzr6HJ14.js → web-CEmtEYdH.js} +89 -65
  99. package/dist-vue/_chunks/web-CEmtEYdH.js.map +1 -0
  100. package/dist-vue/apps/web-component/src/composables/useChatbotAuth.d.ts +1 -3
  101. package/dist-vue/apps/web-component/src/composables/useChatbotAuthForm.d.ts +0 -1
  102. package/dist-vue/composables.js +2841 -103
  103. package/dist-vue/composables.js.map +1 -1
  104. package/dist-vue/index.js +261 -206
  105. package/dist-vue/index.js.map +1 -1
  106. package/dist-vue/locales.js +16 -20
  107. package/dist-vue/packages/auth/src/social-redirect-plugin.d.ts +18 -12
  108. package/dist-vue/packages/components/src/chat/PkAgentSettingsPanel.d.ts +2 -2
  109. package/dist-vue/packages/components/src/chat/markdownHtml.d.ts +20 -0
  110. package/dist-vue/packages/components/src/chat/utils.d.ts +9 -0
  111. package/dist-vue/packages/models/src/schema/AgentApiTool.d.ts +70 -0
  112. package/dist-vue/packages/models/src/schema/Lead.d.ts +13 -0
  113. package/dist-vue/packages/models/src/schema/Media.d.ts +0 -1
  114. package/dist-vue/packages/models/src/schema/ModelProvider.d.ts +12 -3
  115. package/dist-vue/packages/models/src/schema/Organization.d.ts +3 -3
  116. package/dist-vue/packages/models/src/schema/User.d.ts +10 -0
  117. package/dist-vue/packages/models/src/schema/constants.d.ts +1 -1
  118. package/dist-vue/style.css +1 -1
  119. package/package.json +4 -3
  120. package/dist/_chunks/Agent-DTLrWpzX.js +0 -460
  121. package/dist/_chunks/Agent-DTLrWpzX.js.map +0 -1
  122. package/dist/_chunks/Media-Cft7Pwrh.js +0 -108
  123. package/dist/_chunks/Media-Cft7Pwrh.js.map +0 -1
  124. package/dist/_chunks/PkStreamingMarkdown-CRlRDGYl.js.map +0 -1
  125. package/dist/_chunks/PkToolShowVideo-Djr8Ofsz.js.map +0 -1
  126. package/dist/_chunks/markdown-BRD-hDrH.js.map +0 -1
  127. package/dist/_chunks/web-Dzr6HJ14.js.map +0 -1
  128. package/dist-vue/_chunks/Document-B6RpxFr9.js.map +0 -1
  129. package/dist-vue/_chunks/PkChatbotAuth-AxAOI9t2.js.map +0 -1
  130. package/dist-vue/_chunks/PkChatbotMessages-BsT48S-h.js +0 -776
  131. package/dist-vue/_chunks/PkChatbotMessages-BsT48S-h.js.map +0 -1
  132. package/dist-vue/_chunks/PkChatbotSteps-jW07Q-4x.js +0 -190
  133. package/dist-vue/_chunks/PkStreamingMarkdown-BoEpwaYQ.js +0 -2430
  134. package/dist-vue/_chunks/PkStreamingMarkdown-BoEpwaYQ.js.map +0 -1
  135. package/dist-vue/_chunks/PkToolShowVideo-Bv9kMd88.js.map +0 -1
  136. package/dist-vue/_chunks/useChatbotStore-CMS2zFht.js.map +0 -1
  137. package/dist-vue/_chunks/utils-h4C7rRcu.js +0 -177
  138. package/dist-vue/_chunks/utils-h4C7rRcu.js.map +0 -1
  139. package/dist-vue/_chunks/web-Dzr6HJ14.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PkToolShowSuggestedReply-CFO8wV1b.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowSuggestedReply.vue","../../../../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","<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,IAAM,IAAQ,GAIR,IAAO,GAIP,IAAW,QACN,KAAK,MAAM,KAAK,UAAU,EAAM,IAAI,CAAC,CAY/C,GAGK,IAAc,QAA6B;GAC7C,IAAM,IAAQ,EAAS,OAAO;GAW9B,OAVK,IAGD,EAAM,aAAa,SACZ,EAAM,cAGb,EAAM,QACC,CAAC;IAAE,OAAO,EAAM;IAAO,QAAQ,EAAM,UAAU,EAAM;GAAM,CAAC,IAEhE,CAAC,IATG,CAAC;EAUhB,CAAC,GAEK,IAAe,EAAmB,IAAI,GAEtC,IAAgB,SAOb,EAAS,OAAO,UAAU,qBACvB,EAAiB,EAAS,OAAO,MAAM,MAC3C,EAAa,UAAU,IAE9B;EAED,SAAS,EAAY,GAAe;GAChC,IAAI,CAAC,EAAc,OACf;GAEJ,EAAa,QAAQ;GACrB,IAAM,IAAa,EAAY,MAAM,IAC/B,IAAQ,GAAY,UAAU,GAAY;GAChD,AAAI,KACA,EAAK,UAAU,CAAK;EAE5B;;;GAIW,OAAA,EAAA,MAAY,UAAvB,EAAA,GAAA,EAiBM,OAjBN,GAiBM,EAhBF,EAAA,EAAA,GAAA,EAeS,GAAA,MAAA,EAdyB,EAAA,QAAtB,GAAY,OADxB,EAAA,GAAA,EAeS,UAAA;IAbJ,KAAK;IACN,MAAK;IACL,OAAK,EAAA,CAAC,iCAA+B;KACG,cAAA,EAAA,UAAiB;KAAuC,eAAA,CAAA,EAAA;;IAI/F,UAAQ,CAAG,EAAA;IACX,SAAK,GAAA,MAAO,EAAY,CAAK,GAAA,CAAA,MAAA,CAAA;GAC9B,GAAA,CAAA,EAE4C,GAAA;IADxC,MAAK;IACL,OAAM;GACV,CAAA,GAAA,EAAmC,QAAA,MAAA,EAA1B,EAAW,KAAK,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"PkToolShowSuggestedReply-CTWVG1Zk.js","names":[],"sources":["../../../../packages/components/src/chat/PkToolShowSuggestedReply.vue","../../../../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","<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,IAAM,IAAQ,GAIR,IAAO,GAIP,IAAW,QACN,KAAK,MAAM,KAAK,UAAU,EAAM,IAAI,CAAC,CAY/C,GAGK,IAAc,QAA6B;GAC7C,IAAM,IAAQ,EAAS,OAAO;GAW9B,OAVK,IAGD,EAAM,aAAa,SACZ,EAAM,cAGb,EAAM,QACC,CAAC;IAAE,OAAO,EAAM;IAAO,QAAQ,EAAM,UAAU,EAAM;GAAM,CAAC,IAEhE,CAAC,IATG,CAAC;EAUhB,CAAC,GAEK,IAAe,EAAmB,IAAI,GAEtC,IAAgB,SAOb,EAAS,OAAO,UAAU,qBACvB,EAAiB,EAAS,OAAO,MAAM,MAC3C,EAAa,UAAU,IAE9B;EAED,SAAS,EAAY,GAAe;GAChC,IAAI,CAAC,EAAc,OACf;GAEJ,EAAa,QAAQ;GACrB,IAAM,IAAa,EAAY,MAAM,IAC/B,IAAQ,GAAY,UAAU,GAAY;GAChD,AAAI,KACA,EAAK,UAAU,CAAK;EAE5B;;;GAIW,OAAA,EAAA,MAAY,UAAvB,EAAA,GAAA,EAiBM,OAjBN,GAiBM,EAhBF,EAAA,EAAA,GAAA,EAeS,GAAA,MAAA,EAdyB,EAAA,QAAtB,GAAY,OADxB,EAAA,GAAA,EAeS,UAAA;IAbJ,KAAK;IACN,MAAK;IACL,OAAK,EAAA,CAAC,iCAA+B;KACG,cAAA,EAAA,UAAiB;KAAuC,eAAA,CAAA,EAAA;;IAI/F,UAAQ,CAAG,EAAA;IACX,SAAK,GAAA,MAAO,EAAY,CAAK,GAAA,CAAA,MAAA,CAAA;GAC9B,GAAA,CAAA,EAE4C,GAAA;IADxC,MAAK;IACL,OAAM;GACV,CAAA,GAAA,EAAmC,QAAA,MAAA,EAA1B,EAAW,KAAK,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA"}
@@ -6,7 +6,7 @@ var p = (e) => e.replace(/[^a-zA-Z0-9_-]/g, ""), m = (e, t) => e === t || e.ends
6
6
  if (e.hostname.endsWith("youtu.be")) return p(e.pathname.split("/")[1] ?? "") || null;
7
7
  let t = e.searchParams.get("v");
8
8
  if (t) return p(t) || null;
9
- let n = e.pathname.match(/^\/(?:shorts|embed|v)\/([^/?#]+)/);
9
+ let n = e.pathname.match(/^\/(?:shorts|live|embed|v)\/([^/?#]+)/);
10
10
  return n && p(n[1]) || null;
11
11
  }, g = (e) => {
12
12
  let t = e.pathname.match(/(?:^|\/)(\d+)(?:$|[/?#])/);
@@ -40,7 +40,7 @@ function _(e, t) {
40
40
  provider: "vimeo",
41
41
  url: e,
42
42
  videoId: t,
43
- embedUrl: `https://player.vimeo.com/video/${t}`
43
+ embedUrl: `https://player.vimeo.com/video/${t}?dnt=1`
44
44
  };
45
45
  }
46
46
  return t && t !== "file" ? {
@@ -118,4 +118,4 @@ var v = {
118
118
  //#endregion
119
119
  export { k as default };
120
120
 
121
- //# sourceMappingURL=PkToolShowVideo-Djr8Ofsz.js.map
121
+ //# sourceMappingURL=PkToolShowVideo-p6Y9j3-B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PkToolShowVideo-p6Y9j3-B.js","names":["$t"],"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\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 const videos = computed(() => {\n const part = props.part as { input?: { videos?: VideoInput[] } }\n return (part.input?.videos ?? []).map((video) => ({\n ...video,\n source: parseVideoSource(video.url, video.provider),\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 <video\n v-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\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 const videos = computed(() => {\n const part = props.part as { input?: { videos?: VideoInput[] } }\n return (part.input?.videos ?? []).map((video) => ({\n ...video,\n source: parseVideoSource(video.url, video.provider),\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 <video\n v-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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"}