@knime/product-features 2.0.2 → 2.1.0

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 (36) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/ai.css +151 -52
  3. package/dist/ai.js +480 -32
  4. package/dist/ai.js.map +1 -1
  5. package/dist/dataTransfer.js +2 -2
  6. package/dist/{dist-BrNh9vG-.js → dist-D6REgugT.js} +104 -15
  7. package/dist/dist-D6REgugT.js.map +1 -0
  8. package/dist/{rfcErrors-Dw7NLSDp.js → rfcErrors-DZsECPcA.js} +2 -2
  9. package/dist/{rfcErrors-Dw7NLSDp.js.map → rfcErrors-DZsECPcA.js.map} +1 -1
  10. package/dist/rfcErrors.js +1 -1
  11. package/dist/src/ai/KdsPromptInput/AttachableSurface.vue.d.ts +29 -0
  12. package/dist/src/ai/KdsPromptInput/AttachableSurface.vue.d.ts.map +1 -0
  13. package/dist/src/ai/KdsPromptInput/InlineStatusBanner.vue.d.ts.map +1 -1
  14. package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts +3 -1
  15. package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts.map +1 -1
  16. package/dist/src/ai/KdsPromptInput/enums.d.ts +5 -0
  17. package/dist/src/ai/KdsPromptInput/enums.d.ts.map +1 -1
  18. package/dist/src/ai/KdsPromptInput/inquiries/InquiryStack.vue.d.ts +27 -0
  19. package/dist/src/ai/KdsPromptInput/inquiries/InquiryStack.vue.d.ts.map +1 -0
  20. package/dist/src/ai/KdsPromptInput/inquiries/PermissionCard.vue.d.ts +12 -0
  21. package/dist/src/ai/KdsPromptInput/inquiries/PermissionCard.vue.d.ts.map +1 -0
  22. package/dist/src/ai/KdsPromptInput/inquiries/QuestionCard.vue.d.ts +32 -0
  23. package/dist/src/ai/KdsPromptInput/inquiries/QuestionCard.vue.d.ts.map +1 -0
  24. package/dist/src/ai/KdsPromptInput/inquiries/index.d.ts +2 -0
  25. package/dist/src/ai/KdsPromptInput/inquiries/index.d.ts.map +1 -0
  26. package/dist/src/ai/KdsPromptInput/inquiries/usePermissionPhase.d.ts +20 -0
  27. package/dist/src/ai/KdsPromptInput/inquiries/usePermissionPhase.d.ts.map +1 -0
  28. package/dist/src/ai/KdsPromptInput/inquiries/useQuestionPhase.d.ts +26 -0
  29. package/dist/src/ai/KdsPromptInput/inquiries/useQuestionPhase.d.ts.map +1 -0
  30. package/dist/src/ai/KdsPromptInput/types.d.ts +96 -1
  31. package/dist/src/ai/KdsPromptInput/types.d.ts.map +1 -1
  32. package/dist/src/versions/VersionHistory/VersionHistory.vue.d.ts +2 -2
  33. package/dist/toasts.js +1 -1
  34. package/dist/versions.js +1 -1
  35. package/package.json +3 -3
  36. package/dist/dist-BrNh9vG-.js.map +0 -1
package/dist/ai.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ai.js","names":[],"sources":["../src/ai/KdsPromptInput/InlineStatusBanner.vue","../src/ai/KdsPromptInput/InlineStatusBanner.vue","../src/ai/KdsPromptInput/KdsPromptInput.vue","../src/ai/KdsPromptInput/KdsPromptInput.vue","../src/ai/KdsPromptInput/enums.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { KdsButton, KdsIcon } from \"@knime/kds-components\";\n\nimport type { KdsPromptInputStatus } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n}>();\n</script>\n\n<template>\n <div\n :class=\"['inline-status-banner', status.type]\"\n :role=\"status.type === 'error' ? 'alert' : 'status'\"\n >\n <KdsIcon\n :name=\"status.type === 'error' ? 'circle-error' : 'circle-info'\"\n size=\"small\"\n class=\"icon\"\n />\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 2-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <KdsButton\n v-if=\"status.buttonLabel\"\n class=\"action\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n gap: var(--kds-spacing-container-0-37x);\n align-items: center;\n\n /*\n * The banner extends 0.25x behind the input below so the input's\n * rounded top corners carve out of the banner's bottom edge.\n * The extra 0.25x of bottom padding keeps the\n * content visually centered against the visible portion of the box.\n * Top and horizontal padding subtract the border width so the box\n * matches Figma (where the border is drawn outside the layout box).\n * Bottom has no border, so no subtraction is needed there.\n */\n padding: calc(\n var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs)\n )\n calc(var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs))\n calc(var(--kds-spacing-container-0-5x) + var(--kds-spacing-container-0-25x));\n margin-bottom: calc(-1 * var(--kds-spacing-container-0-25x));\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n border-style: solid;\n border-width: var(--kds-core-border-width-xs);\n border-bottom: none;\n border-top-left-radius: var(--kds-border-radius-container-0-50x);\n border-top-right-radius: var(--kds-border-radius-container-0-50x);\n\n .icon,\n .action {\n flex-shrink: 0;\n }\n\n &.error {\n background-color: var(--kds-color-background-static-danger-muted);\n border: var(--kds-border-base-danger);\n border-bottom: none;\n\n .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n }\n\n &.note {\n background-color: var(--kds-color-background-static-info-muted);\n border: var(--kds-border-base-info);\n border-bottom: none;\n\n .icon {\n color: var(--kds-color-text-and-icon-info);\n }\n }\n}\n\n.text {\n flex: 1 0 0;\n min-width: 0;\n\n /* Allow up to 2 lines, scroll beyond that. */\n max-height: calc(2 * 1lh);\n overflow-y: auto;\n overflow-wrap: anywhere;\n scrollbar-width: thin;\n\n &:focus-visible {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { KdsButton, KdsIcon } from \"@knime/kds-components\";\n\nimport type { KdsPromptInputStatus } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n}>();\n</script>\n\n<template>\n <div\n :class=\"['inline-status-banner', status.type]\"\n :role=\"status.type === 'error' ? 'alert' : 'status'\"\n >\n <KdsIcon\n :name=\"status.type === 'error' ? 'circle-error' : 'circle-info'\"\n size=\"small\"\n class=\"icon\"\n />\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 2-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <KdsButton\n v-if=\"status.buttonLabel\"\n class=\"action\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n gap: var(--kds-spacing-container-0-37x);\n align-items: center;\n\n /*\n * The banner extends 0.25x behind the input below so the input's\n * rounded top corners carve out of the banner's bottom edge.\n * The extra 0.25x of bottom padding keeps the\n * content visually centered against the visible portion of the box.\n * Top and horizontal padding subtract the border width so the box\n * matches Figma (where the border is drawn outside the layout box).\n * Bottom has no border, so no subtraction is needed there.\n */\n padding: calc(\n var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs)\n )\n calc(var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs))\n calc(var(--kds-spacing-container-0-5x) + var(--kds-spacing-container-0-25x));\n margin-bottom: calc(-1 * var(--kds-spacing-container-0-25x));\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n border-style: solid;\n border-width: var(--kds-core-border-width-xs);\n border-bottom: none;\n border-top-left-radius: var(--kds-border-radius-container-0-50x);\n border-top-right-radius: var(--kds-border-radius-container-0-50x);\n\n .icon,\n .action {\n flex-shrink: 0;\n }\n\n &.error {\n background-color: var(--kds-color-background-static-danger-muted);\n border: var(--kds-border-base-danger);\n border-bottom: none;\n\n .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n }\n\n &.note {\n background-color: var(--kds-color-background-static-info-muted);\n border: var(--kds-border-base-info);\n border-bottom: none;\n\n .icon {\n color: var(--kds-color-text-and-icon-info);\n }\n }\n}\n\n.text {\n flex: 1 0 0;\n min-width: 0;\n\n /* Allow up to 2 lines, scroll beyond that. */\n max-height: calc(2 * 1lh);\n overflow-y: auto;\n overflow-wrap: anywhere;\n scrollbar-width: thin;\n\n &:focus-visible {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useTemplateRef, watch } from \"vue\";\nimport { useResizeObserver } from \"@vueuse/core\";\n\nimport { KdsButton, KdsLink, KdsProgressBar } from \"@knime/kds-components\";\n\nimport InlineStatusBanner from \"./InlineStatusBanner.vue\";\nimport type { KdsPromptInputProps } from \"./types\";\n\nconst {\n placeholder = \"\",\n disabled = false,\n loading = false,\n status = undefined,\n ariaLabel = \"Prompt\",\n leftSubtext = undefined,\n rightSubtext = undefined,\n link = undefined,\n} = defineProps<KdsPromptInputProps>();\n\nconst emit = defineEmits<{\n /** User submitted the prompt (Enter or send button). */\n submit: [value: string];\n /** User clicked the stop button while loading. */\n stop: [];\n /** User clicked the action button inside the inline status banner. */\n statusButtonClick: [];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst textareaRef = useTemplateRef<HTMLTextAreaElement>(\"textareaRef\");\n\nconst canSubmit = computed(\n () => !disabled && !loading && modelValue.value.trim().length > 0,\n);\n\nfunction resizeTextarea() {\n const el = textareaRef.value;\n if (!el) {\n return;\n }\n // Defer style writes to the next animation frame so they don't run inside\n // the ResizeObserver callback (which would otherwise trigger the\n // \"ResizeObserver loop completed with undelivered notifications\" warning).\n requestAnimationFrame(() => {\n if (!textareaRef.value) {\n return;\n }\n el.style.height = \"auto\";\n const scrollHeight = el.scrollHeight;\n if (scrollHeight <= el.offsetHeight) {\n return;\n }\n el.style.height = `${scrollHeight}px`;\n });\n}\n\nuseResizeObserver(textareaRef, resizeTextarea);\nwatch(modelValue, resizeTextarea, { immediate: true });\n\nfunction onSubmit() {\n if (!canSubmit.value) {\n return;\n }\n emit(\"submit\", modelValue.value.trim());\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n // Enter submits, Shift+Enter inserts a newline. Ignore Enter while an IME\n // composition session is in progress.\n if (event.key === \"Enter\" && !event.shiftKey && !event.isComposing) {\n event.preventDefault();\n onSubmit();\n }\n}\n\ndefineExpose({\n focus: () => textareaRef.value?.focus(),\n});\n</script>\n\n<template>\n <div class=\"prompt-input\">\n <InlineStatusBanner\n v-if=\"status\"\n :status=\"status\"\n @button-click=\"emit('statusButtonClick')\"\n />\n\n <div\n :class=\"['input-container', { disabled }]\"\n @click=\"textareaRef?.focus()\"\n >\n <KdsProgressBar v-if=\"loading\" class=\"progress-bar\" indeterminate />\n\n <div class=\"text-input-wrapper\">\n <textarea\n ref=\"textareaRef\"\n v-model=\"modelValue\"\n class=\"text-input\"\n rows=\"1\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n />\n </div>\n\n <div class=\"action\">\n <KdsButton\n v-if=\"loading\"\n variant=\"outlined\"\n leading-icon=\"stop\"\n size=\"medium\"\n ariaLabel=\"Stop\"\n @click.stop=\"emit('stop')\"\n />\n <KdsButton\n v-else\n variant=\"filled\"\n leading-icon=\"send\"\n size=\"medium\"\n ariaLabel=\"Send\"\n :disabled=\"!canSubmit\"\n @click.stop=\"onSubmit\"\n />\n </div>\n </div>\n\n <div v-if=\"leftSubtext || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"leftSubtext\" class=\"subtext-left\">{{ leftSubtext }}</div>\n <div v-if=\"rightSubtext || link\" class=\"subtext-right\">\n <span v-if=\"rightSubtext\">{{ rightSubtext }}</span>\n <KdsLink v-if=\"link\" :label=\"link.label\" :to=\"link.to\" size=\"small\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.prompt-input {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n\n.input-container {\n position: relative;\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n align-items: flex-end;\n padding: calc(\n var(--kds-spacing-container-0-37x) - var(--kds-core-border-width-xs)\n );\n overflow: hidden;\n cursor: text;\n background: var(--kds-color-background-input-initial);\n border: var(--kds-border-action-input);\n border-radius: var(--kds-border-radius-container-0-37x);\n\n &:has(textarea:focus:not(:disabled)) {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n\n &:has(textarea:hover:not(:disabled, :focus)) {\n background: var(--kds-color-background-input-hover);\n }\n\n &.disabled {\n cursor: default;\n border: var(--kds-border-action-disabled);\n }\n}\n\n.text-input-wrapper {\n display: flex;\n flex: 1 0 0;\n align-items: center;\n min-width: 0;\n\n /*\n * Match the Send/Stop button height so a single-line textarea sits\n * vertically centered against the button.\n */\n min-height: var(--kds-dimension-component-height-1-75x);\n padding-left: var(--kds-spacing-container-0-37x);\n}\n\n.text-input {\n width: 100%;\n min-width: 0;\n\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n max-height: calc(4 * 1lh);\n padding: 0;\n overflow-y: auto;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n resize: none;\n outline: none;\n scrollbar-width: none;\n background: transparent;\n border: none;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-subtle);\n }\n\n &:disabled {\n color: var(--kds-color-text-and-icon-disabled);\n cursor: default;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-disabled);\n }\n }\n\n &::-webkit-scrollbar {\n display: none;\n }\n}\n\n.action {\n display: flex;\n flex-shrink: 0;\n align-items: flex-end;\n}\n\n.progress-bar {\n position: absolute;\n inset: -1px 0 auto;\n}\n\n.subtext {\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-5x);\n align-items: center;\n padding: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-25x)\n 0;\n font: var(--kds-font-base-subtext-small);\n color: var(--kds-color-text-and-icon-neutral);\n}\n\n.subtext-left {\n /*\n * Take all remaining inline space so the right block is pushed to the\n * end. `min-width: 0` lets the ellipsis kick in when the right block\n * grows past the container width.\n */\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.subtext-right {\n /*\n * On row 1 (alongside .subtext-left) the right block stays at its\n * intrinsic width — line-breaking in flex layout is decided from\n * `flex-basis`, so it wraps onto its own row as soon as the container\n * is narrower than that intrinsic width. On row 2 (its own row) the\n * default `flex-shrink: 1` plus `min-width: 0` lets it shrink to the\n * container width and wrap its own content (text + link).\n */\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-width: 0;\n margin-left: auto;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useTemplateRef, watch } from \"vue\";\nimport { useResizeObserver } from \"@vueuse/core\";\n\nimport { KdsButton, KdsLink, KdsProgressBar } from \"@knime/kds-components\";\n\nimport InlineStatusBanner from \"./InlineStatusBanner.vue\";\nimport type { KdsPromptInputProps } from \"./types\";\n\nconst {\n placeholder = \"\",\n disabled = false,\n loading = false,\n status = undefined,\n ariaLabel = \"Prompt\",\n leftSubtext = undefined,\n rightSubtext = undefined,\n link = undefined,\n} = defineProps<KdsPromptInputProps>();\n\nconst emit = defineEmits<{\n /** User submitted the prompt (Enter or send button). */\n submit: [value: string];\n /** User clicked the stop button while loading. */\n stop: [];\n /** User clicked the action button inside the inline status banner. */\n statusButtonClick: [];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst textareaRef = useTemplateRef<HTMLTextAreaElement>(\"textareaRef\");\n\nconst canSubmit = computed(\n () => !disabled && !loading && modelValue.value.trim().length > 0,\n);\n\nfunction resizeTextarea() {\n const el = textareaRef.value;\n if (!el) {\n return;\n }\n // Defer style writes to the next animation frame so they don't run inside\n // the ResizeObserver callback (which would otherwise trigger the\n // \"ResizeObserver loop completed with undelivered notifications\" warning).\n requestAnimationFrame(() => {\n if (!textareaRef.value) {\n return;\n }\n el.style.height = \"auto\";\n const scrollHeight = el.scrollHeight;\n if (scrollHeight <= el.offsetHeight) {\n return;\n }\n el.style.height = `${scrollHeight}px`;\n });\n}\n\nuseResizeObserver(textareaRef, resizeTextarea);\nwatch(modelValue, resizeTextarea, { immediate: true });\n\nfunction onSubmit() {\n if (!canSubmit.value) {\n return;\n }\n emit(\"submit\", modelValue.value.trim());\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n // Enter submits, Shift+Enter inserts a newline. Ignore Enter while an IME\n // composition session is in progress.\n if (event.key === \"Enter\" && !event.shiftKey && !event.isComposing) {\n event.preventDefault();\n onSubmit();\n }\n}\n\ndefineExpose({\n focus: () => textareaRef.value?.focus(),\n});\n</script>\n\n<template>\n <div class=\"prompt-input\">\n <InlineStatusBanner\n v-if=\"status\"\n :status=\"status\"\n @button-click=\"emit('statusButtonClick')\"\n />\n\n <div\n :class=\"['input-container', { disabled }]\"\n @click=\"textareaRef?.focus()\"\n >\n <KdsProgressBar v-if=\"loading\" class=\"progress-bar\" indeterminate />\n\n <div class=\"text-input-wrapper\">\n <textarea\n ref=\"textareaRef\"\n v-model=\"modelValue\"\n class=\"text-input\"\n rows=\"1\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n />\n </div>\n\n <div class=\"action\">\n <KdsButton\n v-if=\"loading\"\n variant=\"outlined\"\n leading-icon=\"stop\"\n size=\"medium\"\n ariaLabel=\"Stop\"\n @click.stop=\"emit('stop')\"\n />\n <KdsButton\n v-else\n variant=\"filled\"\n leading-icon=\"send\"\n size=\"medium\"\n ariaLabel=\"Send\"\n :disabled=\"!canSubmit\"\n @click.stop=\"onSubmit\"\n />\n </div>\n </div>\n\n <div v-if=\"leftSubtext || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"leftSubtext\" class=\"subtext-left\">{{ leftSubtext }}</div>\n <div v-if=\"rightSubtext || link\" class=\"subtext-right\">\n <span v-if=\"rightSubtext\">{{ rightSubtext }}</span>\n <KdsLink v-if=\"link\" :label=\"link.label\" :to=\"link.to\" size=\"small\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.prompt-input {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n\n.input-container {\n position: relative;\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n align-items: flex-end;\n padding: calc(\n var(--kds-spacing-container-0-37x) - var(--kds-core-border-width-xs)\n );\n overflow: hidden;\n cursor: text;\n background: var(--kds-color-background-input-initial);\n border: var(--kds-border-action-input);\n border-radius: var(--kds-border-radius-container-0-37x);\n\n &:has(textarea:focus:not(:disabled)) {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n\n &:has(textarea:hover:not(:disabled, :focus)) {\n background: var(--kds-color-background-input-hover);\n }\n\n &.disabled {\n cursor: default;\n border: var(--kds-border-action-disabled);\n }\n}\n\n.text-input-wrapper {\n display: flex;\n flex: 1 0 0;\n align-items: center;\n min-width: 0;\n\n /*\n * Match the Send/Stop button height so a single-line textarea sits\n * vertically centered against the button.\n */\n min-height: var(--kds-dimension-component-height-1-75x);\n padding-left: var(--kds-spacing-container-0-37x);\n}\n\n.text-input {\n width: 100%;\n min-width: 0;\n\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n max-height: calc(4 * 1lh);\n padding: 0;\n overflow-y: auto;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n resize: none;\n outline: none;\n scrollbar-width: none;\n background: transparent;\n border: none;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-subtle);\n }\n\n &:disabled {\n color: var(--kds-color-text-and-icon-disabled);\n cursor: default;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-disabled);\n }\n }\n\n &::-webkit-scrollbar {\n display: none;\n }\n}\n\n.action {\n display: flex;\n flex-shrink: 0;\n align-items: flex-end;\n}\n\n.progress-bar {\n position: absolute;\n inset: -1px 0 auto;\n}\n\n.subtext {\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-5x);\n align-items: center;\n padding: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-25x)\n 0;\n font: var(--kds-font-base-subtext-small);\n color: var(--kds-color-text-and-icon-neutral);\n}\n\n.subtext-left {\n /*\n * Take all remaining inline space so the right block is pushed to the\n * end. `min-width: 0` lets the ellipsis kick in when the right block\n * grows past the container width.\n */\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.subtext-right {\n /*\n * On row 1 (alongside .subtext-left) the right block stays at its\n * intrinsic width — line-breaking in flex layout is decided from\n * `flex-basis`, so it wraps onto its own row as soon as the container\n * is narrower than that intrinsic width. On row 2 (its own row) the\n * default `flex-shrink: 1` plus `min-width: 0` lets it shrink to the\n * container width and wrap its own content (text + link).\n */\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-width: 0;\n margin-left: auto;\n}\n</style>\n","export const kdsPromptInputStatusType = {\n ERROR: \"error\",\n NOTE: \"note\",\n} as const;\n\nexport const kdsPromptInputStatusTypes = Object.values(\n kdsPromptInputStatusType,\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai.js","names":[],"sources":["../src/ai/KdsPromptInput/AttachableSurface.vue","../src/ai/KdsPromptInput/AttachableSurface.vue","../src/ai/KdsPromptInput/InlineStatusBanner.vue","../src/ai/KdsPromptInput/InlineStatusBanner.vue","../src/ai/KdsPromptInput/inquiries/PermissionCard.vue","../src/ai/KdsPromptInput/inquiries/PermissionCard.vue","../src/ai/KdsPromptInput/inquiries/QuestionCard.vue","../src/ai/KdsPromptInput/inquiries/QuestionCard.vue","../src/ai/KdsPromptInput/inquiries/usePermissionPhase.ts","../src/ai/KdsPromptInput/inquiries/useQuestionPhase.ts","../src/ai/KdsPromptInput/inquiries/InquiryStack.vue","../src/ai/KdsPromptInput/inquiries/InquiryStack.vue","../src/ai/KdsPromptInput/KdsPromptInput.vue","../src/ai/KdsPromptInput/KdsPromptInput.vue","../src/ai/KdsPromptInput/enums.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Wrapper for a surface that attaches directly above the prompt input\n * (or above another attachable surface).\n *\n * Used by the status banner and inquiry cards.\n */\nconst { tone = \"neutral\" } = defineProps<{\n /**\n * Palette of the surface:\n * - `neutral` — default surface with a neutral border (inquiry cards).\n * - `danger` — danger-muted background (error status).\n * - `info` — info-muted background (note status).\n */\n tone?: \"neutral\" | \"danger\" | \"info\";\n}>();\n</script>\n\n<template>\n <div :class=\"['attachable-surface', tone]\">\n <slot />\n </div>\n</template>\n\n<style scoped>\n.attachable-surface {\n margin-bottom: calc(-1 * var(--kds-spacing-container-0-25x));\n background-color: var(--surface-background);\n border: var(--surface-border);\n border-bottom: none;\n border-top-left-radius: var(--kds-border-radius-container-0-50x);\n border-top-right-radius: var(--kds-border-radius-container-0-50x);\n\n &.neutral {\n --surface-background: var(--kds-color-background-input-initial);\n --surface-border: var(--kds-border-action-default);\n }\n\n &.danger {\n --surface-background: var(--kds-color-background-static-danger-muted);\n --surface-border: var(--kds-border-base-danger);\n }\n\n &.info {\n --surface-background: var(--kds-color-background-static-info-muted);\n --surface-border: var(--kds-border-base-info);\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * Wrapper for a surface that attaches directly above the prompt input\n * (or above another attachable surface).\n *\n * Used by the status banner and inquiry cards.\n */\nconst { tone = \"neutral\" } = defineProps<{\n /**\n * Palette of the surface:\n * - `neutral` — default surface with a neutral border (inquiry cards).\n * - `danger` — danger-muted background (error status).\n * - `info` — info-muted background (note status).\n */\n tone?: \"neutral\" | \"danger\" | \"info\";\n}>();\n</script>\n\n<template>\n <div :class=\"['attachable-surface', tone]\">\n <slot />\n </div>\n</template>\n\n<style scoped>\n.attachable-surface {\n margin-bottom: calc(-1 * var(--kds-spacing-container-0-25x));\n background-color: var(--surface-background);\n border: var(--surface-border);\n border-bottom: none;\n border-top-left-radius: var(--kds-border-radius-container-0-50x);\n border-top-right-radius: var(--kds-border-radius-container-0-50x);\n\n &.neutral {\n --surface-background: var(--kds-color-background-input-initial);\n --surface-border: var(--kds-border-action-default);\n }\n\n &.danger {\n --surface-background: var(--kds-color-background-static-danger-muted);\n --surface-border: var(--kds-border-base-danger);\n }\n\n &.info {\n --surface-background: var(--kds-color-background-static-info-muted);\n --surface-border: var(--kds-border-base-info);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { KdsButton, KdsIcon } from \"@knime/kds-components\";\n\nimport AttachableSurface from \"./AttachableSurface.vue\";\nimport type { KdsPromptInputStatus } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n}>();\n</script>\n\n<template>\n <AttachableSurface :tone=\"status.type === 'error' ? 'danger' : 'info'\">\n <div\n :class=\"['inline-status-banner', status.type]\"\n :role=\"status.type === 'error' ? 'alert' : 'status'\"\n >\n <KdsIcon\n :name=\"status.type === 'error' ? 'circle-error' : 'circle-info'\"\n size=\"small\"\n class=\"icon\"\n />\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 2-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <KdsButton\n v-if=\"status.buttonLabel\"\n class=\"action\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n gap: var(--kds-spacing-container-0-37x);\n align-items: center;\n\n /*\n * Horizontal / top padding subtract the surface's border width so the\n * content box matches the design (where the border is drawn outside the\n * layout box). The bottom adds an extra 0.25x to compensate for the 0.25x\n * the surface tucks behind whatever sits below it, keeping the content\n * vertically centered against the visible portion of the banner.\n */\n padding: calc(\n var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs)\n )\n calc(var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs))\n calc(var(--kds-spacing-container-0-5x) + var(--kds-spacing-container-0-25x));\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n\n .icon,\n .action {\n flex-shrink: 0;\n }\n\n &.error .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n\n &.note .icon {\n color: var(--kds-color-text-and-icon-info);\n }\n}\n\n.text {\n flex: 1 0 0;\n min-width: 0;\n\n /* Allow up to 2 lines, scroll beyond that. */\n max-height: calc(2 * 1lh);\n overflow-y: auto;\n overflow-wrap: anywhere;\n scrollbar-width: thin;\n\n &:focus-visible {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { KdsButton, KdsIcon } from \"@knime/kds-components\";\n\nimport AttachableSurface from \"./AttachableSurface.vue\";\nimport type { KdsPromptInputStatus } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n}>();\n</script>\n\n<template>\n <AttachableSurface :tone=\"status.type === 'error' ? 'danger' : 'info'\">\n <div\n :class=\"['inline-status-banner', status.type]\"\n :role=\"status.type === 'error' ? 'alert' : 'status'\"\n >\n <KdsIcon\n :name=\"status.type === 'error' ? 'circle-error' : 'circle-info'\"\n size=\"small\"\n class=\"icon\"\n />\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 2-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <KdsButton\n v-if=\"status.buttonLabel\"\n class=\"action\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n gap: var(--kds-spacing-container-0-37x);\n align-items: center;\n\n /*\n * Horizontal / top padding subtract the surface's border width so the\n * content box matches the design (where the border is drawn outside the\n * layout box). The bottom adds an extra 0.25x to compensate for the 0.25x\n * the surface tucks behind whatever sits below it, keeping the content\n * vertically centered against the visible portion of the banner.\n */\n padding: calc(\n var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs)\n )\n calc(var(--kds-spacing-container-0-5x) - var(--kds-core-border-width-xs))\n calc(var(--kds-spacing-container-0-5x) + var(--kds-spacing-container-0-25x));\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n\n .icon,\n .action {\n flex-shrink: 0;\n }\n\n &.error .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n\n &.note .icon {\n color: var(--kds-color-text-and-icon-info);\n }\n}\n\n.text {\n flex: 1 0 0;\n min-width: 0;\n\n /* Allow up to 2 lines, scroll beyond that. */\n max-height: calc(2 * 1lh);\n overflow-y: auto;\n overflow-wrap: anywhere;\n scrollbar-width: thin;\n\n &:focus-visible {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { onMounted, ref, useTemplateRef } from \"vue\";\n\nimport { KdsButton, KdsCheckbox } from \"@knime/kds-components\";\n\nimport type {\n KdsPromptInputPermission,\n KdsPromptInputPermissionDecision,\n} from \"../types\";\n\nconst { permission } = defineProps<{\n permission: KdsPromptInputPermission;\n}>();\n\nconst emit = defineEmits<{\n decide: [decision: Omit<KdsPromptInputPermissionDecision, \"id\">];\n}>();\n\nconst isRememberChoiceChecked = ref(false);\n\nconst decide = (decision: \"allow\" | \"deny\") => {\n emit(\"decide\", {\n decision,\n ...(permission.remember && {\n remember: isRememberChoiceChecked.value,\n }),\n });\n};\n\n// Focus the \"Allow\" button on render so the user can proceed quickly by\n// pressing \"Enter\".\nconst allowButtonRef =\n useTemplateRef<InstanceType<typeof KdsButton>>(\"allowButton\");\nonMounted(() => {\n (allowButtonRef.value?.$el as HTMLElement | undefined)?.focus();\n});\n\nconst handleKeydown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n decide(\"allow\");\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n decide(\"deny\");\n }\n};\n</script>\n\n<template>\n <div class=\"inquiry-card\" @keydown=\"handleKeydown\">\n <span class=\"title\">{{ permission.headline }}</span>\n\n <div class=\"content\">\n <p v-if=\"permission.description\" class=\"description\">\n {{ permission.description }}\n </p>\n\n <KdsCheckbox\n v-if=\"permission.remember\"\n v-model=\"isRememberChoiceChecked\"\n :label=\"permission.remember.label\"\n :sub-text=\"permission.remember.subtext\"\n />\n\n <div class=\"action-buttons\">\n <KdsButton\n variant=\"transparent\"\n size=\"small\"\n label=\"Deny\"\n @click=\"decide('deny')\"\n />\n <KdsButton\n ref=\"allowButton\"\n variant=\"outlined\"\n size=\"small\"\n label=\"Allow\"\n @click=\"decide('allow')\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.inquiry-card {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n\n & .title {\n font: var(--kds-font-base-title-medium-strong);\n color: var(--kds-color-text-and-icon-neutral);\n overflow-wrap: anywhere;\n }\n\n & .content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-5x);\n\n & .description {\n margin: 0;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-subtle);\n overflow-wrap: anywhere;\n }\n\n & .action-buttons {\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n justify-content: end;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { onMounted, ref, useTemplateRef } from \"vue\";\n\nimport { KdsButton, KdsCheckbox } from \"@knime/kds-components\";\n\nimport type {\n KdsPromptInputPermission,\n KdsPromptInputPermissionDecision,\n} from \"../types\";\n\nconst { permission } = defineProps<{\n permission: KdsPromptInputPermission;\n}>();\n\nconst emit = defineEmits<{\n decide: [decision: Omit<KdsPromptInputPermissionDecision, \"id\">];\n}>();\n\nconst isRememberChoiceChecked = ref(false);\n\nconst decide = (decision: \"allow\" | \"deny\") => {\n emit(\"decide\", {\n decision,\n ...(permission.remember && {\n remember: isRememberChoiceChecked.value,\n }),\n });\n};\n\n// Focus the \"Allow\" button on render so the user can proceed quickly by\n// pressing \"Enter\".\nconst allowButtonRef =\n useTemplateRef<InstanceType<typeof KdsButton>>(\"allowButton\");\nonMounted(() => {\n (allowButtonRef.value?.$el as HTMLElement | undefined)?.focus();\n});\n\nconst handleKeydown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n decide(\"allow\");\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n decide(\"deny\");\n }\n};\n</script>\n\n<template>\n <div class=\"inquiry-card\" @keydown=\"handleKeydown\">\n <span class=\"title\">{{ permission.headline }}</span>\n\n <div class=\"content\">\n <p v-if=\"permission.description\" class=\"description\">\n {{ permission.description }}\n </p>\n\n <KdsCheckbox\n v-if=\"permission.remember\"\n v-model=\"isRememberChoiceChecked\"\n :label=\"permission.remember.label\"\n :sub-text=\"permission.remember.subtext\"\n />\n\n <div class=\"action-buttons\">\n <KdsButton\n variant=\"transparent\"\n size=\"small\"\n label=\"Deny\"\n @click=\"decide('deny')\"\n />\n <KdsButton\n ref=\"allowButton\"\n variant=\"outlined\"\n size=\"small\"\n label=\"Allow\"\n @click=\"decide('allow')\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.inquiry-card {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n\n & .title {\n font: var(--kds-font-base-title-medium-strong);\n color: var(--kds-color-text-and-icon-neutral);\n overflow-wrap: anywhere;\n }\n\n & .content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-5x);\n\n & .description {\n margin: 0;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-subtle);\n overflow-wrap: anywhere;\n }\n\n & .action-buttons {\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n justify-content: end;\n }\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from \"vue\";\n\nimport {\n KdsButton,\n KdsCheckboxGroup,\n KdsRadioButtonGroup,\n KdsTextInput,\n} from \"@knime/kds-components\";\n\nimport type {\n KdsPromptInputQuestion,\n KdsPromptInputQuestionAnswer,\n} from \"../types\";\n\n/**\n * A single pending question, shown one at a time and paginated by the parent\n * stack.\n */\nconst props = defineProps<{\n question: KdsPromptInputQuestion;\n /** Zero-based position of this question within the stack. */\n pageIndex: number;\n /** Total number of questions in the stack. */\n pageCount: number;\n /**\n * True when every *other* question in the stack has already been resolved\n * (answered or skipped). Combined with this card's own selection it decides\n * whether \"Submit\" is enabled on the last page.\n */\n othersAnswered: boolean;\n}>();\n\nconst emit = defineEmits<{\n /** Move to the previous question without recording an answer. */\n back: [];\n /** Skip this question (records `skipped: true`) and move on. */\n skip: [];\n /** Record this answer and advance to the next question. */\n next: [answer: Omit<KdsPromptInputQuestionAnswer, \"id\">];\n /** Record this answer and finalize every inquiry. */\n submit: [answer: Omit<KdsPromptInputQuestionAnswer, \"id\">];\n}>();\n\n/**\n * Synthetic id of the trailing \"Type your own answer\" option. Prefixed so it\n * cannot collide with consumer-provided option ids.\n */\nconst FREEFORM_OPTION_ID = \"__kds-prompt-input-freeform__\";\n\nconst hasOptions = computed(() => (props.question.options ?? []).length > 0);\n\nconst optionValues = computed(() => [\n ...(props.question.options ?? []).map((option) => ({\n id: option.id,\n text: option.label,\n helperText: option.subText,\n })),\n { id: FREEFORM_OPTION_ID, text: \"Type your own answer\" },\n]);\n\nconst selectedSingleId = ref(\"\");\nconst selectedMultiIds = ref<string[]>([]);\nconst freeformText = ref(\"\");\n\n// Whether the user has skipped this question. Kept alongside the selection so\n// the parent's `KeepAlive` preserves it: paging back to a skipped question\n// still shows it as \"(Skipped)\".\nconst isSkipped = ref(false);\n\nconst selectedIds = computed(() => {\n if (props.question.multiple) {\n return selectedMultiIds.value;\n }\n return selectedSingleId.value ? [selectedSingleId.value] : [];\n});\n\n// The freeform input is always active for option-less questions; otherwise\n// it appears once the \"Type your own answer\" option is selected.\nconst isFreeformActive = computed(\n () => !hasOptions.value || selectedIds.value.includes(FREEFORM_OPTION_ID),\n);\n\nconst selectedOptionIds = computed(() =>\n selectedIds.value.filter((id) => id !== FREEFORM_OPTION_ID),\n);\n\nconst canAnswer = computed(\n () =>\n selectedOptionIds.value.length > 0 ||\n (isFreeformActive.value && freeformText.value.trim().length > 0),\n);\n\n// Making a real selection clears the skipped mark — the two are mutually\n// exclusive. (Skipping itself clears the selection, so this never fires on\n// skip.)\nwatch(canAnswer, (answerable) => {\n if (answerable) {\n isSkipped.value = false;\n }\n});\n\nconst isLast = computed(() => props.pageIndex === props.pageCount - 1);\n// The pager only makes sense across multiple questions. \"Back\" is shown only\n// once there is an earlier question to return to (i.e. past the first page);\n// its slot in the action row is always reserved so the layout stays stable.\nconst showPagination = computed(() => props.pageCount > 1);\nconst showBack = computed(() => props.pageIndex > 0);\n\nconst primaryLabel = computed(() => (isLast.value ? \"Submit\" : \"Next\"));\n// The current question counts as resolved once it is answered or skipped. A\n// skipped question stays resolved when the user pages back to it, so the\n// primary button must remain enabled there — not only after a fresh answer.\nconst currentResolved = computed(() => canAnswer.value || isSkipped.value);\nconst canProceed = computed(() =>\n // \"Next\" just needs the current question resolved. \"Submit\" additionally\n // needs every *other* question resolved (answered or skipped).\n isLast.value\n ? props.othersAnswered && currentResolved.value\n : currentResolved.value,\n);\n\nconst buildAnswer = (): Omit<KdsPromptInputQuestionAnswer, \"id\"> => {\n if (isSkipped.value) {\n return { selectedOptionIds: [], skipped: true };\n }\n const freeform = isFreeformActive.value ? freeformText.value.trim() : \"\";\n return {\n selectedOptionIds: selectedOptionIds.value,\n ...(freeform && { freeformInput: freeform }),\n };\n};\n\nconst proceed = () => {\n if (!canProceed.value) {\n return;\n }\n if (isLast.value) {\n emit(\"submit\", buildAnswer());\n } else {\n emit(\"next\", buildAnswer());\n }\n};\n\nconst skip = () => {\n selectedSingleId.value = \"\";\n selectedMultiIds.value = [];\n freeformText.value = \"\";\n isSkipped.value = true;\n emit(\"skip\");\n};\n\nconst handleKeydown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n proceed();\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n skip();\n }\n};\n</script>\n\n<template>\n <div class=\"inquiry-card\" @keydown=\"handleKeydown\">\n <div class=\"title-row\">\n <span class=\"title\"\n >{{ question.headline\n }}<span v-if=\"isSkipped\" class=\"skipped-tag\">(Skipped)</span></span\n >\n <span v-if=\"showPagination\" class=\"pagination\">\n {{ pageIndex + 1 }} of {{ pageCount }}\n </span>\n </div>\n\n <div class=\"content\">\n <p v-if=\"question.description\" class=\"description\">\n {{ question.description }}\n </p>\n\n <KdsRadioButtonGroup\n v-if=\"!question.multiple && hasOptions\"\n v-model=\"selectedSingleId\"\n :possible-values=\"optionValues\"\n alignment=\"vertical\"\n />\n <KdsCheckboxGroup\n v-if=\"question.multiple && hasOptions\"\n v-model=\"selectedMultiIds\"\n :possible-values=\"optionValues\"\n alignment=\"vertical\"\n />\n\n <KdsTextInput\n v-if=\"isFreeformActive\"\n v-model=\"freeformText\"\n ariaLabel=\"Type your own answer\"\n placeholder=\"Type your own answer...\"\n />\n\n <div class=\"action-buttons\">\n <div class=\"nav-secondary\">\n <KdsButton\n v-if=\"showBack\"\n variant=\"transparent\"\n size=\"small\"\n label=\"Back\"\n @click=\"emit('back')\"\n />\n </div>\n <div class=\"nav-primary\">\n <KdsButton\n variant=\"transparent\"\n size=\"small\"\n label=\"Skip\"\n @click=\"skip\"\n />\n <KdsButton\n variant=\"outlined\"\n size=\"small\"\n :label=\"primaryLabel\"\n :disabled=\"!canProceed\"\n @click=\"proceed\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.inquiry-card {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n\n & .title-row {\n display: flex;\n gap: var(--kds-spacing-container-1x);\n align-items: center;\n\n & .title {\n flex: 1 1 0;\n min-width: 0;\n font: var(--kds-font-base-title-medium-strong);\n color: var(--kds-color-text-and-icon-neutral);\n overflow-wrap: anywhere;\n\n & .skipped-tag {\n margin-left: var(--kds-spacing-container-0-25x);\n font: var(--kds-font-base-title-medium);\n color: var(--kds-color-text-and-icon-subtle);\n }\n }\n\n & .pagination {\n flex-shrink: 0;\n font: var(--kds-font-base-title-medium);\n color: var(--kds-color-text-and-icon-neutral);\n white-space: nowrap;\n }\n }\n\n & .content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-5x);\n\n & .description {\n margin: 0;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-subtle);\n overflow-wrap: anywhere;\n }\n\n & .action-buttons {\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n justify-content: space-between;\n\n & .nav-primary {\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n }\n }\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from \"vue\";\n\nimport {\n KdsButton,\n KdsCheckboxGroup,\n KdsRadioButtonGroup,\n KdsTextInput,\n} from \"@knime/kds-components\";\n\nimport type {\n KdsPromptInputQuestion,\n KdsPromptInputQuestionAnswer,\n} from \"../types\";\n\n/**\n * A single pending question, shown one at a time and paginated by the parent\n * stack.\n */\nconst props = defineProps<{\n question: KdsPromptInputQuestion;\n /** Zero-based position of this question within the stack. */\n pageIndex: number;\n /** Total number of questions in the stack. */\n pageCount: number;\n /**\n * True when every *other* question in the stack has already been resolved\n * (answered or skipped). Combined with this card's own selection it decides\n * whether \"Submit\" is enabled on the last page.\n */\n othersAnswered: boolean;\n}>();\n\nconst emit = defineEmits<{\n /** Move to the previous question without recording an answer. */\n back: [];\n /** Skip this question (records `skipped: true`) and move on. */\n skip: [];\n /** Record this answer and advance to the next question. */\n next: [answer: Omit<KdsPromptInputQuestionAnswer, \"id\">];\n /** Record this answer and finalize every inquiry. */\n submit: [answer: Omit<KdsPromptInputQuestionAnswer, \"id\">];\n}>();\n\n/**\n * Synthetic id of the trailing \"Type your own answer\" option. Prefixed so it\n * cannot collide with consumer-provided option ids.\n */\nconst FREEFORM_OPTION_ID = \"__kds-prompt-input-freeform__\";\n\nconst hasOptions = computed(() => (props.question.options ?? []).length > 0);\n\nconst optionValues = computed(() => [\n ...(props.question.options ?? []).map((option) => ({\n id: option.id,\n text: option.label,\n helperText: option.subText,\n })),\n { id: FREEFORM_OPTION_ID, text: \"Type your own answer\" },\n]);\n\nconst selectedSingleId = ref(\"\");\nconst selectedMultiIds = ref<string[]>([]);\nconst freeformText = ref(\"\");\n\n// Whether the user has skipped this question. Kept alongside the selection so\n// the parent's `KeepAlive` preserves it: paging back to a skipped question\n// still shows it as \"(Skipped)\".\nconst isSkipped = ref(false);\n\nconst selectedIds = computed(() => {\n if (props.question.multiple) {\n return selectedMultiIds.value;\n }\n return selectedSingleId.value ? [selectedSingleId.value] : [];\n});\n\n// The freeform input is always active for option-less questions; otherwise\n// it appears once the \"Type your own answer\" option is selected.\nconst isFreeformActive = computed(\n () => !hasOptions.value || selectedIds.value.includes(FREEFORM_OPTION_ID),\n);\n\nconst selectedOptionIds = computed(() =>\n selectedIds.value.filter((id) => id !== FREEFORM_OPTION_ID),\n);\n\nconst canAnswer = computed(\n () =>\n selectedOptionIds.value.length > 0 ||\n (isFreeformActive.value && freeformText.value.trim().length > 0),\n);\n\n// Making a real selection clears the skipped mark — the two are mutually\n// exclusive. (Skipping itself clears the selection, so this never fires on\n// skip.)\nwatch(canAnswer, (answerable) => {\n if (answerable) {\n isSkipped.value = false;\n }\n});\n\nconst isLast = computed(() => props.pageIndex === props.pageCount - 1);\n// The pager only makes sense across multiple questions. \"Back\" is shown only\n// once there is an earlier question to return to (i.e. past the first page);\n// its slot in the action row is always reserved so the layout stays stable.\nconst showPagination = computed(() => props.pageCount > 1);\nconst showBack = computed(() => props.pageIndex > 0);\n\nconst primaryLabel = computed(() => (isLast.value ? \"Submit\" : \"Next\"));\n// The current question counts as resolved once it is answered or skipped. A\n// skipped question stays resolved when the user pages back to it, so the\n// primary button must remain enabled there — not only after a fresh answer.\nconst currentResolved = computed(() => canAnswer.value || isSkipped.value);\nconst canProceed = computed(() =>\n // \"Next\" just needs the current question resolved. \"Submit\" additionally\n // needs every *other* question resolved (answered or skipped).\n isLast.value\n ? props.othersAnswered && currentResolved.value\n : currentResolved.value,\n);\n\nconst buildAnswer = (): Omit<KdsPromptInputQuestionAnswer, \"id\"> => {\n if (isSkipped.value) {\n return { selectedOptionIds: [], skipped: true };\n }\n const freeform = isFreeformActive.value ? freeformText.value.trim() : \"\";\n return {\n selectedOptionIds: selectedOptionIds.value,\n ...(freeform && { freeformInput: freeform }),\n };\n};\n\nconst proceed = () => {\n if (!canProceed.value) {\n return;\n }\n if (isLast.value) {\n emit(\"submit\", buildAnswer());\n } else {\n emit(\"next\", buildAnswer());\n }\n};\n\nconst skip = () => {\n selectedSingleId.value = \"\";\n selectedMultiIds.value = [];\n freeformText.value = \"\";\n isSkipped.value = true;\n emit(\"skip\");\n};\n\nconst handleKeydown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n proceed();\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n skip();\n }\n};\n</script>\n\n<template>\n <div class=\"inquiry-card\" @keydown=\"handleKeydown\">\n <div class=\"title-row\">\n <span class=\"title\"\n >{{ question.headline\n }}<span v-if=\"isSkipped\" class=\"skipped-tag\">(Skipped)</span></span\n >\n <span v-if=\"showPagination\" class=\"pagination\">\n {{ pageIndex + 1 }} of {{ pageCount }}\n </span>\n </div>\n\n <div class=\"content\">\n <p v-if=\"question.description\" class=\"description\">\n {{ question.description }}\n </p>\n\n <KdsRadioButtonGroup\n v-if=\"!question.multiple && hasOptions\"\n v-model=\"selectedSingleId\"\n :possible-values=\"optionValues\"\n alignment=\"vertical\"\n />\n <KdsCheckboxGroup\n v-if=\"question.multiple && hasOptions\"\n v-model=\"selectedMultiIds\"\n :possible-values=\"optionValues\"\n alignment=\"vertical\"\n />\n\n <KdsTextInput\n v-if=\"isFreeformActive\"\n v-model=\"freeformText\"\n ariaLabel=\"Type your own answer\"\n placeholder=\"Type your own answer...\"\n />\n\n <div class=\"action-buttons\">\n <div class=\"nav-secondary\">\n <KdsButton\n v-if=\"showBack\"\n variant=\"transparent\"\n size=\"small\"\n label=\"Back\"\n @click=\"emit('back')\"\n />\n </div>\n <div class=\"nav-primary\">\n <KdsButton\n variant=\"transparent\"\n size=\"small\"\n label=\"Skip\"\n @click=\"skip\"\n />\n <KdsButton\n variant=\"outlined\"\n size=\"small\"\n :label=\"primaryLabel\"\n :disabled=\"!canProceed\"\n @click=\"proceed\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.inquiry-card {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\n\n & .title-row {\n display: flex;\n gap: var(--kds-spacing-container-1x);\n align-items: center;\n\n & .title {\n flex: 1 1 0;\n min-width: 0;\n font: var(--kds-font-base-title-medium-strong);\n color: var(--kds-color-text-and-icon-neutral);\n overflow-wrap: anywhere;\n\n & .skipped-tag {\n margin-left: var(--kds-spacing-container-0-25x);\n font: var(--kds-font-base-title-medium);\n color: var(--kds-color-text-and-icon-subtle);\n }\n }\n\n & .pagination {\n flex-shrink: 0;\n font: var(--kds-font-base-title-medium);\n color: var(--kds-color-text-and-icon-neutral);\n white-space: nowrap;\n }\n }\n\n & .content {\n display: flex;\n flex-direction: column;\n gap: var(--kds-spacing-container-0-5x);\n\n & .description {\n margin: 0;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-subtle);\n overflow-wrap: anywhere;\n }\n\n & .action-buttons {\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n justify-content: space-between;\n\n & .nav-primary {\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n }\n }\n }\n}\n</style>\n","import { computed, reactive } from \"vue\";\n\nimport type {\n KdsPromptInputPermission,\n KdsPromptInputPermissionDecision,\n} from \"../types\";\n\ntype PermissionDecision = Omit<KdsPromptInputPermissionDecision, \"id\">;\n\n/**\n * The permission phase: pending permissions resolved like a stack, with no\n * pager. The current permission is always the first still-undecided one;\n * deciding it pops it off and reveals the next. Buffered decisions persist\n * until {@link reset} so the parent can gather them into a single `respond`\n * once every phase is complete.\n *\n * @param permissions Getter for the pending permissions (kept reactive).\n */\nexport function usePermissionPhase(\n permissions: () => KdsPromptInputPermission[],\n) {\n // Keyed by the permission's own id; never shared with the question phase.\n const decisions = reactive(new Map<string, PermissionDecision>());\n\n /** The first permission not yet decided, or `undefined` once none remain. */\n const current = computed(() =>\n permissions().find((permission) => !decisions.has(permission.id)),\n );\n\n const isComplete = computed(() =>\n permissions().every((permission) => decisions.has(permission.id)),\n );\n\n /**\n * Record a decision for `permission`. Returns `false` (a no-op) when the\n * permission was already decided — e.g. a double click landing before the\n * stack pops the card off — so the caller can avoid emitting `respond` twice.\n *\n * A remembered decision also resolves every other undecided permission of the\n * same `remember.group` (e.g. repeated calls of one tool), so the user is not\n * asked again for a choice they just saved. Those never surface — the group\n * is popped silently.\n */\n const decide = (\n permission: KdsPromptInputPermission,\n value: PermissionDecision,\n ): boolean => {\n if (decisions.has(permission.id)) {\n return false;\n }\n decisions.set(permission.id, value);\n\n const group = permission.remember?.group;\n if (group && value.remember) {\n for (const other of permissions()) {\n if (other.remember?.group === group && !decisions.has(other.id)) {\n decisions.set(other.id, value);\n }\n }\n }\n return true;\n };\n\n /** Buffered decisions in prop order, ready for the `respond` payload. */\n const toOrderedDecisions = (): KdsPromptInputPermissionDecision[] =>\n permissions().map((permission) => ({\n id: permission.id,\n ...(decisions.get(permission.id) as PermissionDecision),\n }));\n\n const reset = () => decisions.clear();\n\n return { current, isComplete, decide, toOrderedDecisions, reset };\n}\n","import { computed, reactive, ref } from \"vue\";\n\nimport type {\n KdsPromptInputQuestion,\n KdsPromptInputQuestionAnswer,\n} from \"../types\";\n\ntype QuestionAnswer = Omit<KdsPromptInputQuestionAnswer, \"id\">;\n\n/**\n * The question phase: pending questions paginated one at a time. The user moves\n * forward with {@link answer} + {@link advance} (or {@link skip}) and back with\n * {@link back}, changing earlier selections freely. Buffered answers persist\n * until {@link reset} so the parent can gather them into a single `respond`\n * once every phase is complete.\n *\n * @param questions Getter for the pending questions (kept reactive).\n */\nexport function useQuestionPhase(questions: () => KdsPromptInputQuestion[]) {\n // Keyed by the question's own id; never shared with the permission phase.\n const answers = reactive(new Map<string, QuestionAnswer>());\n const index = ref(0);\n\n const current = computed(() => questions()[index.value]);\n const pageCount = computed(() => questions().length);\n\n const isComplete = computed(() =>\n questions().every((question) => answers.has(question.id)),\n );\n\n /**\n * True when every question *other than the current one* has been resolved.\n * The current card supplies its own answer live, so together they decide\n * whether \"Submit\" is enabled on the last page.\n */\n const othersAnswered = computed(() =>\n questions().every(\n (question, i) => i === index.value || answers.has(question.id),\n ),\n );\n\n const advance = () => {\n index.value = Math.min(index.value + 1, questions().length - 1);\n };\n\n const back = () => {\n if (index.value > 0) {\n index.value -= 1;\n }\n };\n\n const answer = (question: KdsPromptInputQuestion, value: QuestionAnswer) => {\n answers.set(question.id, value);\n };\n\n const skip = (question: KdsPromptInputQuestion) => {\n answers.set(question.id, { selectedOptionIds: [], skipped: true });\n };\n\n /** Buffered answers in prop order, ready for the `respond` payload. */\n const toOrderedAnswers = (): KdsPromptInputQuestionAnswer[] =>\n questions().map((question) => ({\n id: question.id,\n ...(answers.get(question.id) as QuestionAnswer),\n }));\n\n const reset = () => {\n answers.clear();\n index.value = 0;\n };\n\n return {\n current,\n index,\n pageCount,\n isComplete,\n othersAnswered,\n advance,\n back,\n answer,\n skip,\n toOrderedAnswers,\n reset,\n };\n}\n","<script setup lang=\"ts\">\nimport { watch } from \"vue\";\n\nimport AttachableSurface from \"../AttachableSurface.vue\";\nimport type {\n KdsPromptInputInquiryResponse,\n KdsPromptInputPermission,\n KdsPromptInputPermissionDecision,\n KdsPromptInputQuestion,\n KdsPromptInputQuestionAnswer,\n} from \"../types\";\n\nimport PermissionCard from \"./PermissionCard.vue\";\nimport QuestionCard from \"./QuestionCard.vue\";\nimport { usePermissionPhase } from \"./usePermissionPhase\";\nimport { useQuestionPhase } from \"./useQuestionPhase\";\n\n/**\n * The pending permissions and questions, attached as a surface above the\n * input. The two are resolved in distinct, sequential phases, see\n * the corresponding composables:\n *\n * - {@link usePermissionPhase} is first. Deciding one permission pops it off and reveals the next.\n * - {@link useQuestionPhase} follows once every permission is addressed: a stack\n * the user moves through with \"Next\" / \"Skip\" and \"Back\".\n *\n * This component only sequences the two phases and, once both are complete,\n * emits a single `respond` carrying every decision (all-or-nothing per turn).\n * For the common single-inquiry case this degenerates to one card that responds\n * immediately.\n */\nconst { permissions, questions } = defineProps<{\n permissions: KdsPromptInputPermission[];\n questions: KdsPromptInputQuestion[];\n}>();\n\nconst emit = defineEmits<{\n respond: [response: KdsPromptInputInquiryResponse];\n}>();\n\nconst {\n current: currentPermission,\n isComplete: permissionsComplete,\n decide: decidePermission,\n toOrderedDecisions,\n reset: resetPermissions,\n} = usePermissionPhase(() => permissions);\n\nconst {\n current: currentQuestion,\n index: questionIndex,\n pageCount: questionCount,\n isComplete: questionsComplete,\n othersAnswered,\n advance: advanceQuestion,\n back: goBack,\n answer: recordAnswer,\n skip: skipQuestion,\n toOrderedAnswers,\n reset: resetQuestions,\n} = useQuestionPhase(() => questions);\n\n// A new set of pending inquiries invalidates both phases' buffered state.\nwatch(\n () => [...permissions, ...questions].map((inquiry) => inquiry.id).join(\",\"),\n () => {\n resetPermissions();\n resetQuestions();\n },\n);\n\n// Emit once both phases are resolved. Either the last permission decision (when there\n// are no questions) or the question \"Submit\".\nconst respondIfComplete = () => {\n if (!permissionsComplete.value || !questionsComplete.value) {\n return;\n }\n emit(\"respond\", {\n permissions: toOrderedDecisions(),\n questions: toOrderedAnswers(),\n });\n};\n\nconst onDecidePermission = (\n permission: KdsPromptInputPermission,\n decision: Omit<KdsPromptInputPermissionDecision, \"id\">,\n) => {\n // `decidePermission` is a no-op (returns false) for an already-resolved card,\n // so a double click cannot fire `respond` twice.\n if (decidePermission(permission, decision)) {\n respondIfComplete();\n }\n};\n\nconst onQuestionNext = (\n question: KdsPromptInputQuestion,\n answer: Omit<KdsPromptInputQuestionAnswer, \"id\">,\n) => {\n recordAnswer(question, answer);\n advanceQuestion();\n};\n\nconst onQuestionSkip = (question: KdsPromptInputQuestion) => {\n // Skip resolves the question as skipped and moves on.\n // On the last question `advanceQuestion` is a clamp\n // leaving \"Submit\" as the only way forward.\n skipQuestion(question);\n advanceQuestion();\n};\n\nconst onQuestionSubmit = (\n question: KdsPromptInputQuestion,\n answer: Omit<KdsPromptInputQuestionAnswer, \"id\">,\n) => {\n recordAnswer(question, answer);\n respondIfComplete();\n};\n</script>\n\n<template>\n <AttachableSurface v-if=\"currentPermission || currentQuestion\" tone=\"neutral\">\n <div class=\"inquiry-stack\">\n <PermissionCard\n v-if=\"currentPermission\"\n :key=\"currentPermission.id\"\n :permission=\"currentPermission\"\n @decide=\"(decision) => onDecidePermission(currentPermission!, decision)\"\n />\n\n <!-- KeepAlive preserves each question's in-progress state (selected\n options, freeform text) while the user pages back and forth before\n confirming. The cache lives only as long as the stack itself, which\n unmounts once the turn's inquiries are resolved. -->\n <KeepAlive v-else-if=\"currentQuestion\">\n <QuestionCard\n :key=\"currentQuestion.id\"\n :question=\"currentQuestion\"\n :page-index=\"questionIndex\"\n :page-count=\"questionCount\"\n :others-answered=\"othersAnswered\"\n @back=\"goBack\"\n @skip=\"onQuestionSkip(currentQuestion!)\"\n @next=\"(answer) => onQuestionNext(currentQuestion!, answer)\"\n @submit=\"(answer) => onQuestionSubmit(currentQuestion!, answer)\"\n />\n </KeepAlive>\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inquiry-stack {\n padding: calc(\n var(--kds-spacing-container-1x) - var(--kds-core-border-width-xs)\n )\n calc(var(--kds-spacing-container-1x) - var(--kds-core-border-width-xs))\n var(--kds-spacing-container-1x);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { watch } from \"vue\";\n\nimport AttachableSurface from \"../AttachableSurface.vue\";\nimport type {\n KdsPromptInputInquiryResponse,\n KdsPromptInputPermission,\n KdsPromptInputPermissionDecision,\n KdsPromptInputQuestion,\n KdsPromptInputQuestionAnswer,\n} from \"../types\";\n\nimport PermissionCard from \"./PermissionCard.vue\";\nimport QuestionCard from \"./QuestionCard.vue\";\nimport { usePermissionPhase } from \"./usePermissionPhase\";\nimport { useQuestionPhase } from \"./useQuestionPhase\";\n\n/**\n * The pending permissions and questions, attached as a surface above the\n * input. The two are resolved in distinct, sequential phases, see\n * the corresponding composables:\n *\n * - {@link usePermissionPhase} is first. Deciding one permission pops it off and reveals the next.\n * - {@link useQuestionPhase} follows once every permission is addressed: a stack\n * the user moves through with \"Next\" / \"Skip\" and \"Back\".\n *\n * This component only sequences the two phases and, once both are complete,\n * emits a single `respond` carrying every decision (all-or-nothing per turn).\n * For the common single-inquiry case this degenerates to one card that responds\n * immediately.\n */\nconst { permissions, questions } = defineProps<{\n permissions: KdsPromptInputPermission[];\n questions: KdsPromptInputQuestion[];\n}>();\n\nconst emit = defineEmits<{\n respond: [response: KdsPromptInputInquiryResponse];\n}>();\n\nconst {\n current: currentPermission,\n isComplete: permissionsComplete,\n decide: decidePermission,\n toOrderedDecisions,\n reset: resetPermissions,\n} = usePermissionPhase(() => permissions);\n\nconst {\n current: currentQuestion,\n index: questionIndex,\n pageCount: questionCount,\n isComplete: questionsComplete,\n othersAnswered,\n advance: advanceQuestion,\n back: goBack,\n answer: recordAnswer,\n skip: skipQuestion,\n toOrderedAnswers,\n reset: resetQuestions,\n} = useQuestionPhase(() => questions);\n\n// A new set of pending inquiries invalidates both phases' buffered state.\nwatch(\n () => [...permissions, ...questions].map((inquiry) => inquiry.id).join(\",\"),\n () => {\n resetPermissions();\n resetQuestions();\n },\n);\n\n// Emit once both phases are resolved. Either the last permission decision (when there\n// are no questions) or the question \"Submit\".\nconst respondIfComplete = () => {\n if (!permissionsComplete.value || !questionsComplete.value) {\n return;\n }\n emit(\"respond\", {\n permissions: toOrderedDecisions(),\n questions: toOrderedAnswers(),\n });\n};\n\nconst onDecidePermission = (\n permission: KdsPromptInputPermission,\n decision: Omit<KdsPromptInputPermissionDecision, \"id\">,\n) => {\n // `decidePermission` is a no-op (returns false) for an already-resolved card,\n // so a double click cannot fire `respond` twice.\n if (decidePermission(permission, decision)) {\n respondIfComplete();\n }\n};\n\nconst onQuestionNext = (\n question: KdsPromptInputQuestion,\n answer: Omit<KdsPromptInputQuestionAnswer, \"id\">,\n) => {\n recordAnswer(question, answer);\n advanceQuestion();\n};\n\nconst onQuestionSkip = (question: KdsPromptInputQuestion) => {\n // Skip resolves the question as skipped and moves on.\n // On the last question `advanceQuestion` is a clamp\n // leaving \"Submit\" as the only way forward.\n skipQuestion(question);\n advanceQuestion();\n};\n\nconst onQuestionSubmit = (\n question: KdsPromptInputQuestion,\n answer: Omit<KdsPromptInputQuestionAnswer, \"id\">,\n) => {\n recordAnswer(question, answer);\n respondIfComplete();\n};\n</script>\n\n<template>\n <AttachableSurface v-if=\"currentPermission || currentQuestion\" tone=\"neutral\">\n <div class=\"inquiry-stack\">\n <PermissionCard\n v-if=\"currentPermission\"\n :key=\"currentPermission.id\"\n :permission=\"currentPermission\"\n @decide=\"(decision) => onDecidePermission(currentPermission!, decision)\"\n />\n\n <!-- KeepAlive preserves each question's in-progress state (selected\n options, freeform text) while the user pages back and forth before\n confirming. The cache lives only as long as the stack itself, which\n unmounts once the turn's inquiries are resolved. -->\n <KeepAlive v-else-if=\"currentQuestion\">\n <QuestionCard\n :key=\"currentQuestion.id\"\n :question=\"currentQuestion\"\n :page-index=\"questionIndex\"\n :page-count=\"questionCount\"\n :others-answered=\"othersAnswered\"\n @back=\"goBack\"\n @skip=\"onQuestionSkip(currentQuestion!)\"\n @next=\"(answer) => onQuestionNext(currentQuestion!, answer)\"\n @submit=\"(answer) => onQuestionSubmit(currentQuestion!, answer)\"\n />\n </KeepAlive>\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inquiry-stack {\n padding: calc(\n var(--kds-spacing-container-1x) - var(--kds-core-border-width-xs)\n )\n calc(var(--kds-spacing-container-1x) - var(--kds-core-border-width-xs))\n var(--kds-spacing-container-1x);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useTemplateRef, watch } from \"vue\";\nimport { useResizeObserver } from \"@vueuse/core\";\n\nimport { KdsButton, KdsLink, KdsProgressBar } from \"@knime/kds-components\";\n\nimport InlineStatusBanner from \"./InlineStatusBanner.vue\";\nimport { InquiryStack } from \"./inquiries\";\nimport type {\n KdsPromptInputInquiryResponse,\n KdsPromptInputProps,\n} from \"./types\";\n\nconst {\n placeholder = \"\",\n disabled = false,\n loading = false,\n status = undefined,\n ariaLabel = \"Prompt\",\n leftSubtext = undefined,\n rightSubtext = undefined,\n link = undefined,\n permissions = [],\n questions = [],\n} = defineProps<KdsPromptInputProps>();\n\nconst emit = defineEmits<{\n /** User submitted the prompt (Enter or send button). */\n submit: [value: string];\n /** User clicked the stop button while loading. */\n stop: [];\n /** User clicked the action button inside the inline status banner. */\n statusButtonClick: [];\n /** User decided every pending permission and answered every question. */\n respond: [response: KdsPromptInputInquiryResponse];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst textareaRef = useTemplateRef<HTMLTextAreaElement>(\"textareaRef\");\n\nconst hasInquiries = computed(\n () => permissions.length > 0 || questions.length > 0,\n);\n\n// While permissions or questions are shown, the input is disabled, so\n// responding to them is the only way to continue the conversation.\nconst isDisabled = computed(() => disabled || hasInquiries.value);\n\nconst canSubmit = computed(\n () => !isDisabled.value && !loading && modelValue.value.trim().length > 0,\n);\n\nfunction resizeTextarea() {\n const el = textareaRef.value;\n if (!el) {\n return;\n }\n // Defer style writes to the next animation frame so they don't run inside\n // the ResizeObserver callback (which would otherwise trigger the\n // \"ResizeObserver loop completed with undelivered notifications\" warning).\n requestAnimationFrame(() => {\n if (!textareaRef.value) {\n return;\n }\n el.style.height = \"auto\";\n const scrollHeight = el.scrollHeight;\n if (scrollHeight <= el.offsetHeight) {\n return;\n }\n el.style.height = `${scrollHeight}px`;\n });\n}\n\nuseResizeObserver(textareaRef, resizeTextarea);\nwatch(modelValue, resizeTextarea, { immediate: true });\n\nfunction onSubmit() {\n if (!canSubmit.value) {\n return;\n }\n emit(\"submit\", modelValue.value.trim());\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n // Enter submits, Shift+Enter inserts a newline. Ignore Enter while an IME\n // composition session is in progress.\n if (event.key === \"Enter\" && !event.shiftKey && !event.isComposing) {\n event.preventDefault();\n onSubmit();\n }\n}\n\ndefineExpose({\n focus: () => textareaRef.value?.focus(),\n});\n</script>\n\n<template>\n <div class=\"prompt-input\">\n <InlineStatusBanner\n v-if=\"status\"\n :status=\"status\"\n @button-click=\"emit('statusButtonClick')\"\n />\n\n <InquiryStack\n v-if=\"hasInquiries\"\n :permissions=\"permissions\"\n :questions=\"questions\"\n @respond=\"emit('respond', $event)\"\n />\n\n <div\n :class=\"['input-container', { disabled: isDisabled }]\"\n @click=\"textareaRef?.focus()\"\n >\n <KdsProgressBar v-if=\"loading\" class=\"progress-bar\" indeterminate />\n\n <div class=\"text-input-wrapper\">\n <textarea\n ref=\"textareaRef\"\n v-model=\"modelValue\"\n class=\"text-input\"\n rows=\"1\"\n :placeholder=\"placeholder\"\n :disabled=\"isDisabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n />\n </div>\n\n <div class=\"action\">\n <KdsButton\n v-if=\"loading\"\n variant=\"outlined\"\n leading-icon=\"stop\"\n size=\"medium\"\n ariaLabel=\"Stop\"\n @click.stop=\"emit('stop')\"\n />\n <KdsButton\n v-else\n variant=\"filled\"\n leading-icon=\"send\"\n size=\"medium\"\n ariaLabel=\"Send\"\n :disabled=\"!canSubmit\"\n @click.stop=\"onSubmit\"\n />\n </div>\n </div>\n\n <div v-if=\"leftSubtext || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"leftSubtext\" class=\"subtext-left\">{{ leftSubtext }}</div>\n <div v-if=\"rightSubtext || link\" class=\"subtext-right\">\n <span v-if=\"rightSubtext\">{{ rightSubtext }}</span>\n <KdsLink v-if=\"link\" :label=\"link.label\" :to=\"link.to\" size=\"small\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.prompt-input {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n\n.input-container {\n position: relative;\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n align-items: flex-end;\n padding: calc(\n var(--kds-spacing-container-0-37x) - var(--kds-core-border-width-xs)\n );\n overflow: hidden;\n cursor: text;\n background: var(--kds-color-background-input-initial);\n border: var(--kds-border-action-input);\n border-radius: var(--kds-border-radius-container-0-37x);\n\n &:has(textarea:focus:not(:disabled)) {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n\n &:has(textarea:hover:not(:disabled, :focus)) {\n background: var(--kds-color-background-input-hover);\n }\n\n &.disabled {\n cursor: default;\n border: var(--kds-border-action-disabled);\n }\n}\n\n.text-input-wrapper {\n display: flex;\n flex: 1 0 0;\n align-items: center;\n min-width: 0;\n\n /*\n * Match the Send/Stop button height so a single-line textarea sits\n * vertically centered against the button.\n */\n min-height: var(--kds-dimension-component-height-1-75x);\n padding-left: var(--kds-spacing-container-0-37x);\n}\n\n.text-input {\n width: 100%;\n min-width: 0;\n\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n max-height: calc(4 * 1lh);\n padding: 0;\n overflow-y: auto;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n resize: none;\n outline: none;\n scrollbar-width: none;\n background: transparent;\n border: none;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-subtle);\n }\n\n &:disabled {\n color: var(--kds-color-text-and-icon-disabled);\n cursor: default;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-disabled);\n }\n }\n\n &::-webkit-scrollbar {\n display: none;\n }\n}\n\n.action {\n display: flex;\n flex-shrink: 0;\n align-items: flex-end;\n}\n\n.progress-bar {\n position: absolute;\n inset: -1px 0 auto;\n}\n\n.subtext {\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-5x);\n align-items: center;\n padding: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-25x)\n 0;\n font: var(--kds-font-base-subtext-small);\n color: var(--kds-color-text-and-icon-neutral);\n}\n\n.subtext-left {\n /*\n * Take all remaining inline space so the right block is pushed to the\n * end. `min-width: 0` lets the ellipsis kick in when the right block\n * grows past the container width.\n */\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.subtext-right {\n /*\n * On row 1 (alongside .subtext-left) the right block stays at its\n * intrinsic width — line-breaking in flex layout is decided from\n * `flex-basis`, so it wraps onto its own row as soon as the container\n * is narrower than that intrinsic width. On row 2 (its own row) the\n * default `flex-shrink: 1` plus `min-width: 0` lets it shrink to the\n * container width and wrap its own content (text + link).\n */\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-width: 0;\n margin-left: auto;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, useTemplateRef, watch } from \"vue\";\nimport { useResizeObserver } from \"@vueuse/core\";\n\nimport { KdsButton, KdsLink, KdsProgressBar } from \"@knime/kds-components\";\n\nimport InlineStatusBanner from \"./InlineStatusBanner.vue\";\nimport { InquiryStack } from \"./inquiries\";\nimport type {\n KdsPromptInputInquiryResponse,\n KdsPromptInputProps,\n} from \"./types\";\n\nconst {\n placeholder = \"\",\n disabled = false,\n loading = false,\n status = undefined,\n ariaLabel = \"Prompt\",\n leftSubtext = undefined,\n rightSubtext = undefined,\n link = undefined,\n permissions = [],\n questions = [],\n} = defineProps<KdsPromptInputProps>();\n\nconst emit = defineEmits<{\n /** User submitted the prompt (Enter or send button). */\n submit: [value: string];\n /** User clicked the stop button while loading. */\n stop: [];\n /** User clicked the action button inside the inline status banner. */\n statusButtonClick: [];\n /** User decided every pending permission and answered every question. */\n respond: [response: KdsPromptInputInquiryResponse];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst textareaRef = useTemplateRef<HTMLTextAreaElement>(\"textareaRef\");\n\nconst hasInquiries = computed(\n () => permissions.length > 0 || questions.length > 0,\n);\n\n// While permissions or questions are shown, the input is disabled, so\n// responding to them is the only way to continue the conversation.\nconst isDisabled = computed(() => disabled || hasInquiries.value);\n\nconst canSubmit = computed(\n () => !isDisabled.value && !loading && modelValue.value.trim().length > 0,\n);\n\nfunction resizeTextarea() {\n const el = textareaRef.value;\n if (!el) {\n return;\n }\n // Defer style writes to the next animation frame so they don't run inside\n // the ResizeObserver callback (which would otherwise trigger the\n // \"ResizeObserver loop completed with undelivered notifications\" warning).\n requestAnimationFrame(() => {\n if (!textareaRef.value) {\n return;\n }\n el.style.height = \"auto\";\n const scrollHeight = el.scrollHeight;\n if (scrollHeight <= el.offsetHeight) {\n return;\n }\n el.style.height = `${scrollHeight}px`;\n });\n}\n\nuseResizeObserver(textareaRef, resizeTextarea);\nwatch(modelValue, resizeTextarea, { immediate: true });\n\nfunction onSubmit() {\n if (!canSubmit.value) {\n return;\n }\n emit(\"submit\", modelValue.value.trim());\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n // Enter submits, Shift+Enter inserts a newline. Ignore Enter while an IME\n // composition session is in progress.\n if (event.key === \"Enter\" && !event.shiftKey && !event.isComposing) {\n event.preventDefault();\n onSubmit();\n }\n}\n\ndefineExpose({\n focus: () => textareaRef.value?.focus(),\n});\n</script>\n\n<template>\n <div class=\"prompt-input\">\n <InlineStatusBanner\n v-if=\"status\"\n :status=\"status\"\n @button-click=\"emit('statusButtonClick')\"\n />\n\n <InquiryStack\n v-if=\"hasInquiries\"\n :permissions=\"permissions\"\n :questions=\"questions\"\n @respond=\"emit('respond', $event)\"\n />\n\n <div\n :class=\"['input-container', { disabled: isDisabled }]\"\n @click=\"textareaRef?.focus()\"\n >\n <KdsProgressBar v-if=\"loading\" class=\"progress-bar\" indeterminate />\n\n <div class=\"text-input-wrapper\">\n <textarea\n ref=\"textareaRef\"\n v-model=\"modelValue\"\n class=\"text-input\"\n rows=\"1\"\n :placeholder=\"placeholder\"\n :disabled=\"isDisabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"onKeydown\"\n />\n </div>\n\n <div class=\"action\">\n <KdsButton\n v-if=\"loading\"\n variant=\"outlined\"\n leading-icon=\"stop\"\n size=\"medium\"\n ariaLabel=\"Stop\"\n @click.stop=\"emit('stop')\"\n />\n <KdsButton\n v-else\n variant=\"filled\"\n leading-icon=\"send\"\n size=\"medium\"\n ariaLabel=\"Send\"\n :disabled=\"!canSubmit\"\n @click.stop=\"onSubmit\"\n />\n </div>\n </div>\n\n <div v-if=\"leftSubtext || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"leftSubtext\" class=\"subtext-left\">{{ leftSubtext }}</div>\n <div v-if=\"rightSubtext || link\" class=\"subtext-right\">\n <span v-if=\"rightSubtext\">{{ rightSubtext }}</span>\n <KdsLink v-if=\"link\" :label=\"link.label\" :to=\"link.to\" size=\"small\" />\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.prompt-input {\n display: flex;\n flex-direction: column;\n width: 100%;\n}\n\n.input-container {\n position: relative;\n display: flex;\n gap: var(--kds-spacing-container-0-5x);\n align-items: flex-end;\n padding: calc(\n var(--kds-spacing-container-0-37x) - var(--kds-core-border-width-xs)\n );\n overflow: hidden;\n cursor: text;\n background: var(--kds-color-background-input-initial);\n border: var(--kds-border-action-input);\n border-radius: var(--kds-border-radius-container-0-37x);\n\n &:has(textarea:focus:not(:disabled)) {\n outline: var(--kds-border-action-focused);\n outline-offset: var(--kds-spacing-offset-focus);\n }\n\n &:has(textarea:hover:not(:disabled, :focus)) {\n background: var(--kds-color-background-input-hover);\n }\n\n &.disabled {\n cursor: default;\n border: var(--kds-border-action-disabled);\n }\n}\n\n.text-input-wrapper {\n display: flex;\n flex: 1 0 0;\n align-items: center;\n min-width: 0;\n\n /*\n * Match the Send/Stop button height so a single-line textarea sits\n * vertically centered against the button.\n */\n min-height: var(--kds-dimension-component-height-1-75x);\n padding-left: var(--kds-spacing-container-0-37x);\n}\n\n.text-input {\n width: 100%;\n min-width: 0;\n\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n max-height: calc(4 * 1lh);\n padding: 0;\n overflow-y: auto;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n resize: none;\n outline: none;\n scrollbar-width: none;\n background: transparent;\n border: none;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-subtle);\n }\n\n &:disabled {\n color: var(--kds-color-text-and-icon-disabled);\n cursor: default;\n\n &::placeholder {\n color: var(--kds-color-text-and-icon-disabled);\n }\n }\n\n &::-webkit-scrollbar {\n display: none;\n }\n}\n\n.action {\n display: flex;\n flex-shrink: 0;\n align-items: flex-end;\n}\n\n.progress-bar {\n position: absolute;\n inset: -1px 0 auto;\n}\n\n.subtext {\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-5x);\n align-items: center;\n padding: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-25x)\n 0;\n font: var(--kds-font-base-subtext-small);\n color: var(--kds-color-text-and-icon-neutral);\n}\n\n.subtext-left {\n /*\n * Take all remaining inline space so the right block is pushed to the\n * end. `min-width: 0` lets the ellipsis kick in when the right block\n * grows past the container width.\n */\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.subtext-right {\n /*\n * On row 1 (alongside .subtext-left) the right block stays at its\n * intrinsic width — line-breaking in flex layout is decided from\n * `flex-basis`, so it wraps onto its own row as soon as the container\n * is narrower than that intrinsic width. On row 2 (its own row) the\n * default `flex-shrink: 1` plus `min-width: 0` lets it shrink to the\n * container width and wrap its own content (text + link).\n */\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n min-width: 0;\n margin-left: auto;\n}\n</style>\n","export const kdsPromptInputStatusType = {\n ERROR: \"error\",\n NOTE: \"note\",\n} as const;\n\nexport const kdsPromptInputStatusTypes = Object.values(\n kdsPromptInputStatusType,\n);\n\nexport const kdsPromptInputPermissionDecision = {\n ALLOW: \"allow\",\n DENY: \"deny\",\n} as const;\n\nexport const kdsPromptInputPermissionDecisions = Object.values(\n kdsPromptInputPermissionDecision,\n);\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BOaGB7Aw.js";
2
- import { a as getFileExtension, c as promise_exports, l as sleep, o as getFileMimeType, r as formatFileSize, s as knimeFileFormats } from "./dist-BrNh9vG-.js";
3
- import { t as rfcErrors } from "./rfcErrors-Dw7NLSDp.js";
2
+ import { a as getFileExtension, c as promise_exports, l as sleep, o as getFileMimeType, r as formatFileSize, s as knimeFileFormats } from "./dist-D6REgugT.js";
3
+ import { t as rfcErrors } from "./rfcErrors-DZsECPcA.js";
4
4
  import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, openBlock, reactive, readonly, ref, renderList, renderSlot, toDisplayString, unref, useId, useModel, vShow, watch, withCtx, withDirectives } from "vue";
5
5
  import { KdsBadge, KdsButton, KdsIcon, KdsLoadingSkeleton, KdsProgressBar, vKdsTooltip } from "@knime/kds-components";
6
6
  import { FetchError, ofetch } from "ofetch";
@@ -112,8 +112,8 @@ function prettyBytes(number, options) {
112
112
  return applyFixedWidth(result, options.fixedWidth);
113
113
  }
114
114
  //#endregion
115
- //#region ../../node_modules/.pnpm/dompurify@3.4.11/node_modules/dompurify/dist/purify.es.mjs
116
- /*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE */
115
+ //#region ../../node_modules/.pnpm/dompurify@3.4.12/node_modules/dompurify/dist/purify.es.mjs
116
+ /*! @license DOMPurify 3.4.12 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.12/LICENSE */
117
117
  function _arrayLikeToArray(r, a) {
118
118
  (null == a || a > r.length) && (a = r.length);
119
119
  for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
@@ -752,6 +752,7 @@ var svg = freeze([
752
752
  "direction",
753
753
  "display",
754
754
  "divisor",
755
+ "dominant-baseline",
755
756
  "dur",
756
757
  "edgemode",
757
758
  "elevation",
@@ -879,6 +880,7 @@ var svg = freeze([
879
880
  "transform-origin",
880
881
  "text-anchor",
881
882
  "text-decoration",
883
+ "text-orientation",
882
884
  "text-rendering",
883
885
  "textlength",
884
886
  "type",
@@ -1061,7 +1063,7 @@ var _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options)
1061
1063
  function createDOMPurify() {
1062
1064
  let window = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
1063
1065
  const DOMPurify = (root) => createDOMPurify(root);
1064
- DOMPurify.version = "3.4.11";
1066
+ DOMPurify.version = "3.4.12";
1065
1067
  DOMPurify.removed = [];
1066
1068
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
1067
1069
  DOMPurify.isSupported = false;
@@ -1528,6 +1530,7 @@ function createDOMPurify() {
1528
1530
  * @param root the in-place root to empty
1529
1531
  */
1530
1532
  const _neutralizeRoot = function _neutralizeRoot(root) {
1533
+ _neutralizeSubtree(root);
1531
1534
  const childNodes = getChildNodes(root);
1532
1535
  if (childNodes) {
1533
1536
  const snapshot = [];
@@ -1629,6 +1632,65 @@ function createDOMPurify() {
1629
1632
  }
1630
1633
  };
1631
1634
  /**
1635
+ * _neutralizePatchLinkage
1636
+ *
1637
+ * IN_PLACE entry pre-pass (declarative-partial-updates / streaming
1638
+ * hardening, https://github.com/WICG/declarative-partial-updates).
1639
+ *
1640
+ * The main walk strips patch linkage (`for`/`patchsrc`) and removes range
1641
+ * markers (PIs / markup comments) node-by-node, in document order, AS it
1642
+ * reaches each node. On a live in-place root that leaves a window: from the
1643
+ * moment the root is connected until the walk arrives at a given node, that
1644
+ * node's linkage is live. A patch applied on connection/stream can fire as
1645
+ * a microtask during the walk and inject or teleport an unsanitized DOM
1646
+ * range into a region the iterator has already passed and will not revisit,
1647
+ * so the post-return "tree is sanitized" contract is violated. Sweep the
1648
+ * whole tree once up front and sever every linkage before the walk begins,
1649
+ * closing that window.
1650
+ *
1651
+ * This CANNOT undo a patch that already fired before sanitize ran — that is
1652
+ * the irreducible "do not IN_PLACE a live-connected attacker tree" caveat —
1653
+ * but it closes everything from sanitize-start onward. Gated on SAFE_FOR_XML
1654
+ * to group with the rest of the declarative-partial-updates handling and
1655
+ * stay overridable, consistent with the codebase.
1656
+ *
1657
+ * Clobber-safe traversal (cached childNodes getter); per-node try/catch so a
1658
+ * clobbered root cannot defeat the sweep of its non-clobbered descendants.
1659
+ *
1660
+ * NOTE (pending real-Chrome confirmation, see test/declarative-patch-probe
1661
+ * .html Q1): this mirrors the existing policy of keeping `for` on
1662
+ * <label>/<output>. If the shipping feature can drive a patch through a
1663
+ * surviving `for`-on-label/output + `id` pair, this pre-pass and the
1664
+ * attribute check at _isBasicCustomElement's caller must additionally drop
1665
+ * that pair on the IN_PLACE path. Left as-is until the taxonomy is verified.
1666
+ *
1667
+ * @param root the in-place root to sweep
1668
+ */
1669
+ const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
1670
+ if (!SAFE_FOR_XML) return;
1671
+ const stack = [root];
1672
+ while (stack.length > 0) {
1673
+ const node = stack.pop();
1674
+ const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
1675
+ if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
1676
+ try {
1677
+ remove(node);
1678
+ } catch (_) {}
1679
+ continue;
1680
+ }
1681
+ if (nodeType === NODE_TYPE.element) {
1682
+ const element = node;
1683
+ const lcTag = transformCaseFunc(getNodeName ? getNodeName(node) : node.nodeName);
1684
+ try {
1685
+ if (element.hasAttribute && element.hasAttribute("patchsrc")) element.removeAttribute("patchsrc");
1686
+ if (element.hasAttribute && element.hasAttribute("for") && lcTag !== "label" && lcTag !== "output") element.removeAttribute("for");
1687
+ } catch (_) {}
1688
+ }
1689
+ const childNodes = getChildNodes(node);
1690
+ if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]);
1691
+ }
1692
+ };
1693
+ /**
1632
1694
  * _initDocument
1633
1695
  *
1634
1696
  * @param dirty - a string of dirty markup
@@ -1796,9 +1858,15 @@ function createDOMPurify() {
1796
1858
  /**
1797
1859
  * Handle a node whose tag is forbidden or not allowlisted: keep
1798
1860
  * allowed custom elements (false return exits _sanitizeElements
1799
- * early - namespace/fallback checks and the afterSanitizeElements
1800
- * hook are intentionally skipped for kept custom elements), else
1801
- * hoist content per KEEP_CONTENT and remove.
1861
+ * early - the namespace and fallback-tag removal checks are
1862
+ * intentionally skipped for kept custom elements), else hoist
1863
+ * content per KEEP_CONTENT and remove.
1864
+ *
1865
+ * A kept custom element is the ONLY case in which this function
1866
+ * returns false, so the caller uses that return value to run the
1867
+ * afterSanitizeElements hook on the kept element and keep the
1868
+ * element-hook lifecycle consistent with normal allowlisted
1869
+ * elements (GHSA-c2j3-45gr-mqc4).
1802
1870
  *
1803
1871
  * @param currentNode the disallowed node
1804
1872
  * @param tagName the node's transformCaseFunc'd tag name
@@ -1832,8 +1900,9 @@ function createDOMPurify() {
1832
1900
  * @param currentNode to check for permission to exist
1833
1901
  * @return true if node was killed, false if left alive
1834
1902
  */
1835
- const _sanitizeElements = function _sanitizeElements(currentNode) {
1903
+ const _sanitizeElements = function _sanitizeElements(currentNode, root) {
1836
1904
  _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
1905
+ if (currentNode !== root && getParentNode(currentNode) === null) return true;
1837
1906
  if (_isClobbered(currentNode)) {
1838
1907
  _forceRemove(currentNode);
1839
1908
  return true;
@@ -1843,11 +1912,16 @@ function createDOMPurify() {
1843
1912
  tagName,
1844
1913
  allowedTags: ALLOWED_TAGS
1845
1914
  });
1915
+ if (currentNode !== root && getParentNode(currentNode) === null) return true;
1846
1916
  if (_isUnsafeNode(currentNode, tagName)) {
1847
1917
  _forceRemove(currentNode);
1848
1918
  return true;
1849
1919
  }
1850
- if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) return _sanitizeDisallowedNode(currentNode, tagName);
1920
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
1921
+ const removed = _sanitizeDisallowedNode(currentNode, tagName);
1922
+ if (removed === false) _executeHooks(hooks.afterSanitizeElements, currentNode, null);
1923
+ return removed;
1924
+ }
1851
1925
  if ((getNodeType ? getNodeType(currentNode) : currentNode.nodeType) === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
1852
1926
  _forceRemove(currentNode);
1853
1927
  return true;
@@ -1876,6 +1950,8 @@ function createDOMPurify() {
1876
1950
  */
1877
1951
  const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
1878
1952
  if (FORBID_ATTR[lcName]) return false;
1953
+ if (SAFE_FOR_XML && lcName === "patchsrc") return false;
1954
+ if (SAFE_FOR_XML && lcName === "for" && lcTag !== "label" && lcTag !== "output") return false;
1879
1955
  if (SANITIZE_DOM && (lcName === "id" || lcName === "name") && (value in document || value in formElement)) return false;
1880
1956
  const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
1881
1957
  if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName));
@@ -2029,7 +2105,7 @@ function createDOMPurify() {
2029
2105
  _executeHooks(hooks.beforeSanitizeShadowDOM, fragment, null);
2030
2106
  while (shadowNode = shadowIterator.nextNode()) {
2031
2107
  _executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
2032
- _sanitizeElements(shadowNode);
2108
+ _sanitizeElements(shadowNode, fragment);
2033
2109
  _sanitizeAttributes(shadowNode);
2034
2110
  if (_isDocumentFragment(shadowNode.content)) _sanitizeShadowDOM2(shadowNode.content);
2035
2111
  if ((getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType) === NODE_TYPE.element) {
@@ -2123,12 +2199,19 @@ function createDOMPurify() {
2123
2199
  DOMPurify.removed = [];
2124
2200
  const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
2125
2201
  if (inPlace) {
2202
+ _neutralizePatchLinkage(dirty);
2126
2203
  const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
2127
2204
  if (typeof nn === "string") {
2128
2205
  const tagName = transformCaseFunc(nn);
2129
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
2206
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
2207
+ _neutralizeRoot(dirty);
2208
+ throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
2209
+ }
2210
+ }
2211
+ if (_isClobbered(dirty)) {
2212
+ _neutralizeRoot(dirty);
2213
+ throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
2130
2214
  }
2131
- if (_isClobbered(dirty)) throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
2132
2215
  try {
2133
2216
  _sanitizeAttachedShadowRoots(dirty);
2134
2217
  } catch (error) {
@@ -2148,15 +2231,21 @@ function createDOMPurify() {
2148
2231
  if (!body) return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : "";
2149
2232
  }
2150
2233
  if (body && FORCE_BODY) _forceRemove(body.firstChild);
2151
- const nodeIterator = _createNodeIterator(inPlace ? dirty : body);
2234
+ const walkRoot = inPlace ? dirty : body;
2235
+ const nodeIterator = _createNodeIterator(walkRoot);
2152
2236
  try {
2153
2237
  while (currentNode = nodeIterator.nextNode()) {
2154
- _sanitizeElements(currentNode);
2238
+ _sanitizeElements(currentNode, walkRoot);
2155
2239
  _sanitizeAttributes(currentNode);
2156
2240
  if (_isDocumentFragment(currentNode.content)) _sanitizeShadowDOM2(currentNode.content);
2157
2241
  }
2158
2242
  } catch (error) {
2159
- if (inPlace) _neutralizeRoot(dirty);
2243
+ if (inPlace) {
2244
+ _neutralizeRoot(dirty);
2245
+ arrayForEach(DOMPurify.removed, (entry) => {
2246
+ if (entry.element) _neutralizeSubtree(entry.element);
2247
+ });
2248
+ }
2160
2249
  throw error;
2161
2250
  }
2162
2251
  if (inPlace) {
@@ -2557,4 +2646,4 @@ Object.values({
2557
2646
  //#endregion
2558
2647
  export { getFileExtension as a, promise_exports as c, formatLocalDateTimeString as i, sleep as l, formatDateTimeString as n, getFileMimeType as o, formatFileSize as r, knimeFileFormats as s, capitalize as t, truncateString_default as u };
2559
2648
 
2560
- //# sourceMappingURL=dist-BrNh9vG-.js.map
2649
+ //# sourceMappingURL=dist-D6REgugT.js.map