@knime/product-features 2.0.1 → 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.
- package/CHANGELOG.md +26 -0
- package/dist/ai.css +151 -52
- package/dist/ai.js +480 -32
- package/dist/ai.js.map +1 -1
- package/dist/dataTransfer.js +2 -2
- package/dist/{dist-BrNh9vG-.js → dist-D6REgugT.js} +104 -15
- package/dist/dist-D6REgugT.js.map +1 -0
- package/dist/{rfcErrors-Dw7NLSDp.js → rfcErrors-DZsECPcA.js} +2 -2
- package/dist/{rfcErrors-Dw7NLSDp.js.map → rfcErrors-DZsECPcA.js.map} +1 -1
- package/dist/rfcErrors.js +1 -1
- package/dist/src/ai/KdsPromptInput/AttachableSurface.vue.d.ts +29 -0
- package/dist/src/ai/KdsPromptInput/AttachableSurface.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/InlineStatusBanner.vue.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts +3 -1
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/enums.d.ts +5 -0
- package/dist/src/ai/KdsPromptInput/enums.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/inquiries/InquiryStack.vue.d.ts +27 -0
- package/dist/src/ai/KdsPromptInput/inquiries/InquiryStack.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/PermissionCard.vue.d.ts +12 -0
- package/dist/src/ai/KdsPromptInput/inquiries/PermissionCard.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/QuestionCard.vue.d.ts +32 -0
- package/dist/src/ai/KdsPromptInput/inquiries/QuestionCard.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/index.d.ts +2 -0
- package/dist/src/ai/KdsPromptInput/inquiries/index.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/usePermissionPhase.d.ts +20 -0
- package/dist/src/ai/KdsPromptInput/inquiries/usePermissionPhase.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/useQuestionPhase.d.ts +26 -0
- package/dist/src/ai/KdsPromptInput/inquiries/useQuestionPhase.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/types.d.ts +96 -1
- package/dist/src/ai/KdsPromptInput/types.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/VersionHistory.vue.d.ts +2 -2
- package/dist/toasts.js +1 -1
- package/dist/versions.js +1 -1
- package/package.json +7 -4
- 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":";;;;;;;;;;;;;;;;;uBAmBE,mBAEM,OAAA,EAFA,OAAK,eAAA,CAAA,sBAAyB,QAAA,IAAI,CAAA,EAAA,GAAA,CACtC,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,KAAA,GAAA,IAAA,CAAA,GAAA,CAAA;;;;;;;;;;;;;;;;;;EEVZ,MAAM,OAAO;;uBAMX,YAwBoB,2BAAA,EAxBA,MAAM,QAAA,OAAO,SAAI,UAAA,WAAA,OAAA,GAAA;2BAuB7B,CAtBN,mBAsBM,OAAA;KArBH,OAAK,eAAA,CAAA,wBAA2B,QAAA,OAAO,IAAI,CAAA;KAC3C,MAAM,QAAA,OAAO,SAAI,UAAA,UAAA;;KAElB,YAIE,MAAA,OAAA,GAAA;MAHC,MAAM,QAAA,OAAO,SAAI,UAAA,iBAAA;MAClB,MAAK;MACL,OAAM;;KAMR,mBAA2D,QAA3D,cAA2D,gBAAxB,QAAA,OAAO,OAAO,GAAA,CAAA;KAEzC,QAAA,OAAO,eAAA,UAAA,GADf,YAOE,MAAA,SAAA,GAAA;;MALA,OAAM;MACN,MAAK;MACL,SAAQ;MACP,OAAO,QAAA,OAAO;MACd,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,aAAA;;;;;;;;;;;;;;;;;;;;;;;;EEvBpB,MAAM,OAAO;EAIb,MAAM,0BAA0B,IAAI,KAAK;EAEzC,MAAM,UAAU,aAA+B;GAC7C,KAAK,UAAU;IACb;IACA,GAAI,QAAA,WAAW,YAAY,EACzB,UAAU,wBAAwB,MACpC;GACF,CAAC;EACH;EAIA,MAAM,iBACJ,eAA+C,aAAa;EAC9D,gBAAgB;GACd,CAAC,eAAe,OAAO,IAAA,EAAiC,MAAM;EAChE,CAAC;EAED,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,QAAQ,SAAS;IACzB,MAAM,eAAe;IACrB,OAAO,OAAO;GAChB,OAAO,IAAI,MAAM,QAAQ,UAAU;IACjC,MAAM,eAAe;IACrB,OAAO,MAAM;GACf;EACF;;uBAIE,mBA+BM,OAAA;IA/BD,OAAM;IAAgB,WAAS;OAClC,mBAAoD,QAApD,cAAoD,gBAA7B,QAAA,WAAW,QAAQ,GAAA,CAAA,GAE1C,mBA2BM,OA3BN,cA2BM;IA1BK,QAAA,WAAW,eAAA,UAAA,GAApB,mBAEI,KAFJ,cAEI,gBADC,QAAA,WAAW,WAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAInB,QAAA,WAAW,YAAA,UAAA,GADnB,YAKE,MAAA,WAAA,GAAA;;iBAHS,wBAAA;0FAAuB,QAAA;KAC/B,OAAO,QAAA,WAAW,SAAS;KAC3B,YAAU,QAAA,WAAW,SAAS;;;;;;IAGjC,mBAcM,OAdN,cAcM,CAbJ,YAKE,MAAA,SAAA,GAAA;KAJA,SAAQ;KACR,MAAK;KACL,OAAM;KACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,OAAM,MAAA;QAEhB,YAME,MAAA,SAAA,GAAA;KALA,KAAI;KACJ,SAAQ;KACR,MAAK;KACL,OAAM;KACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,OAAM,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AE5BxB,IAAM,qBAAqB;;;;;;;;;;;;;;;;;;EA7B3B,MAAM,QAAQ;EAcd,MAAM,OAAO;;;;;EAiBb,MAAM,aAAa,gBAAgB,MAAM,SAAS,WAAW,CAAC,EAAA,CAAG,SAAS,CAAC;EAE3E,MAAM,eAAe,eAAe,CAClC,IAAI,MAAM,SAAS,WAAW,CAAC,EAAA,CAAG,KAAK,YAAY;GACjD,IAAI,OAAO;GACX,MAAM,OAAO;GACb,YAAY,OAAO;EACrB,EAAE,GACF;GAAE,IAAI;GAAoB,MAAM;EAAuB,CACzD,CAAC;EAED,MAAM,mBAAmB,IAAI,EAAE;EAC/B,MAAM,mBAAmB,IAAc,CAAC,CAAC;EACzC,MAAM,eAAe,IAAI,EAAE;EAK3B,MAAM,YAAY,IAAI,KAAK;EAE3B,MAAM,cAAc,eAAe;GACjC,IAAI,MAAM,SAAS,UACjB,OAAO,iBAAiB;GAE1B,OAAO,iBAAiB,QAAQ,CAAC,iBAAiB,KAAK,IAAI,CAAC;EAC9D,CAAC;EAID,MAAM,mBAAmB,eACjB,CAAC,WAAW,SAAS,YAAY,MAAM,SAAS,kBAAkB,CAC1E;EAEA,MAAM,oBAAoB,eACxB,YAAY,MAAM,QAAQ,OAAO,OAAO,kBAAkB,CAC5D;EAEA,MAAM,YAAY,eAEd,kBAAkB,MAAM,SAAS,KAChC,iBAAiB,SAAS,aAAa,MAAM,KAAK,CAAC,CAAC,SAAS,CAClE;EAKA,MAAM,YAAY,eAAe;GAC/B,IAAI,YACF,UAAU,QAAQ;EAEtB,CAAC;EAED,MAAM,SAAS,eAAe,MAAM,cAAc,MAAM,YAAY,CAAC;EAIrE,MAAM,iBAAiB,eAAe,MAAM,YAAY,CAAC;EACzD,MAAM,WAAW,eAAe,MAAM,YAAY,CAAC;EAEnD,MAAM,eAAe,eAAgB,OAAO,QAAQ,WAAW,MAAO;EAItE,MAAM,kBAAkB,eAAe,UAAU,SAAS,UAAU,KAAK;EACzE,MAAM,aAAa,eAGjB,OAAO,QACH,MAAM,kBAAkB,gBAAgB,QACxC,gBAAgB,KACtB;EAEA,MAAM,oBAA8D;GAClE,IAAI,UAAU,OACZ,OAAO;IAAE,mBAAmB,CAAC;IAAG,SAAS;GAAK;GAEhD,MAAM,WAAW,iBAAiB,QAAQ,aAAa,MAAM,KAAK,IAAI;GACtE,OAAO;IACL,mBAAmB,kBAAkB;IACrC,GAAI,YAAY,EAAE,eAAe,SAAS;GAC5C;EACF;EAEA,MAAM,gBAAgB;GACpB,IAAI,CAAC,WAAW,OACd;GAEF,IAAI,OAAO,OACT,KAAK,UAAU,YAAY,CAAC;QAE5B,KAAK,QAAQ,YAAY,CAAC;EAE9B;EAEA,MAAM,aAAa;GACjB,iBAAiB,QAAQ;GACzB,iBAAiB,QAAQ,CAAC;GAC1B,aAAa,QAAQ;GACrB,UAAU,QAAQ;GAClB,KAAK,MAAM;EACb;EAEA,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,QAAQ,SAAS;IACzB,MAAM,eAAe;IACrB,QAAQ;GACV,OAAO,IAAI,MAAM,QAAQ,UAAU;IACjC,MAAM,eAAe;IACrB,KAAK;GACP;EACF;;uBAIE,mBA+DM,OAAA;IA/DD,OAAM;IAAgB,WAAS;OAClC,mBAQM,OARN,cAQM,CAPJ,mBAGC,QAHD,cAGC,CAAA,gBAAA,gBAFK,QAAA,SAAS,QAAQ,GAAA,CAAA,GACP,UAAA,SAAA,UAAA,GAAZ,mBAA2D,QAA3D,cAA2C,WAAS,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,GAE5C,eAAA,SAAA,UAAA,GAAZ,mBAEO,QAFP,cAEO,gBADF,QAAA,YAAS,CAAA,IAAO,SAAI,gBAAG,QAAA,SAAS,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,GAIvC,mBAmDM,OAnDN,cAmDM;IAlDK,QAAA,SAAS,eAAA,UAAA,GAAlB,mBAEI,KAFJ,cAEI,gBADC,QAAA,SAAS,WAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAIhB,QAAA,SAAS,YAAY,WAAA,SAAA,UAAA,GAD9B,YAKE,MAAA,mBAAA,GAAA;;iBAHS,iBAAA;mFAAgB,QAAA;KACxB,mBAAiB,aAAA;KAClB,WAAU;;IAGJ,QAAA,SAAS,YAAY,WAAA,SAAA,UAAA,GAD7B,YAKE,MAAA,gBAAA,GAAA;;iBAHS,iBAAA;mFAAgB,QAAA;KACxB,mBAAiB,aAAA;KAClB,WAAU;;IAIJ,iBAAA,SAAA,UAAA,GADR,YAKE,MAAA,YAAA,GAAA;;iBAHS,aAAA;+EAAY,QAAA;KACrB,WAAU;KACV,aAAY;;IAGd,mBAyBM,OAzBN,cAyBM,CAxBJ,mBAQM,OARN,cAQM,CANI,SAAA,SAAA,UAAA,GADR,YAME,MAAA,SAAA,GAAA;;KAJA,SAAQ;KACR,MAAK;KACL,OAAM;KACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,MAAA;0CAGhB,mBAcM,OAdN,YAcM,CAbJ,YAKE,MAAA,SAAA,GAAA;KAJA,SAAQ;KACR,MAAK;KACL,OAAM;KACL,SAAO;QAEV,YAME,MAAA,SAAA,GAAA;KALA,SAAQ;KACR,MAAK;KACJ,OAAO,aAAA;KACP,UAAQ,CAAG,WAAA;KACX,SAAO;;;;;;;;;;;;;;;;;AE5MpB,SAAgB,mBACd,aACA;CAEA,MAAM,YAAY,yBAAS,IAAI,IAAgC,CAAC;;CAGhE,MAAM,UAAU,eACd,YAAY,CAAC,CAAC,MAAM,eAAe,CAAC,UAAU,IAAI,WAAW,EAAE,CAAC,CAClE;CAEA,MAAM,aAAa,eACjB,YAAY,CAAC,CAAC,OAAO,eAAe,UAAU,IAAI,WAAW,EAAE,CAAC,CAClE;;;;;;;;;;;CAYA,MAAM,UACJ,YACA,UACY;EACZ,IAAI,UAAU,IAAI,WAAW,EAAE,GAC7B,OAAO;EAET,UAAU,IAAI,WAAW,IAAI,KAAK;EAElC,MAAM,QAAQ,WAAW,UAAU;EACnC,IAAI,SAAS,MAAM;QACZ,MAAM,SAAS,YAAY,GAC9B,IAAI,MAAM,UAAU,UAAU,SAAS,CAAC,UAAU,IAAI,MAAM,EAAE,GAC5D,UAAU,IAAI,MAAM,IAAI,KAAK;EAAA;EAInC,OAAO;CACT;;CAGA,MAAM,2BACJ,YAAY,CAAC,CAAC,KAAK,gBAAgB;EACjC,IAAI,WAAW;EACf,GAAI,UAAU,IAAI,WAAW,EAAE;CACjC,EAAE;CAEJ,MAAM,cAAc,UAAU,MAAM;CAEpC,OAAO;EAAE;EAAS;EAAY;EAAQ;EAAoB;CAAM;AAClE;;;;;;;;;;;;ACvDA,SAAgB,iBAAiB,WAA2C;CAE1E,MAAM,UAAU,yBAAS,IAAI,IAA4B,CAAC;CAC1D,MAAM,QAAQ,IAAI,CAAC;CAEnB,MAAM,UAAU,eAAe,UAAU,CAAC,CAAC,MAAM,MAAM;CACvD,MAAM,YAAY,eAAe,UAAU,CAAC,CAAC,MAAM;CAEnD,MAAM,aAAa,eACjB,UAAU,CAAC,CAAC,OAAO,aAAa,QAAQ,IAAI,SAAS,EAAE,CAAC,CAC1D;;;;;;CAOA,MAAM,iBAAiB,eACrB,UAAU,CAAC,CAAC,OACT,UAAU,MAAM,MAAM,MAAM,SAAS,QAAQ,IAAI,SAAS,EAAE,CAC/D,CACF;CAEA,MAAM,gBAAgB;EACpB,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,SAAS,CAAC;CAChE;CAEA,MAAM,aAAa;EACjB,IAAI,MAAM,QAAQ,GAChB,MAAM,SAAS;CAEnB;CAEA,MAAM,UAAU,UAAkC,UAA0B;EAC1E,QAAQ,IAAI,SAAS,IAAI,KAAK;CAChC;CAEA,MAAM,QAAQ,aAAqC;EACjD,QAAQ,IAAI,SAAS,IAAI;GAAE,mBAAmB,CAAC;GAAG,SAAS;EAAK,CAAC;CACnE;;CAGA,MAAM,yBACJ,UAAU,CAAC,CAAC,KAAK,cAAc;EAC7B,IAAI,SAAS;EACb,GAAI,QAAQ,IAAI,SAAS,EAAE;CAC7B,EAAE;CAEJ,MAAM,cAAc;EAClB,QAAQ,MAAM;EACd,MAAM,QAAQ;CAChB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;;;EChDA,MAAM,OAAO;EAIb,MAAM,EACJ,SAAS,mBACT,YAAY,qBACZ,QAAQ,kBACR,oBACA,OAAO,qBACL,yBAAyB,QAAA,WAAW;EAExC,MAAM,EACJ,SAAS,iBACT,OAAO,eACP,WAAW,eACX,YAAY,mBACZ,gBACA,SAAS,iBACT,MAAM,QACN,QAAQ,cACR,MAAM,cACN,kBACA,OAAO,mBACL,uBAAuB,QAAA,SAAS;EAGpC,YACQ,CAAC,GAAG,QAAA,aAAa,GAAG,QAAA,SAAS,CAAC,CAAC,KAAK,YAAY,QAAQ,EAAE,CAAC,CAAC,KAAK,GAAG,SACpE;GACJ,iBAAiB;GACjB,eAAe;EACjB,CACF;EAIA,MAAM,0BAA0B;GAC9B,IAAI,CAAC,oBAAoB,SAAS,CAAC,kBAAkB,OACnD;GAEF,KAAK,WAAW;IACd,aAAa,mBAAmB;IAChC,WAAW,iBAAiB;GAC9B,CAAC;EACH;EAEA,MAAM,sBACJ,YACA,aACG;GAGH,IAAI,iBAAiB,YAAY,QAAQ,GACvC,kBAAkB;EAEtB;EAEA,MAAM,kBACJ,UACA,WACG;GACH,aAAa,UAAU,MAAM;GAC7B,gBAAgB;EAClB;EAEA,MAAM,kBAAkB,aAAqC;GAI3D,aAAa,QAAQ;GACrB,gBAAgB;EAClB;EAEA,MAAM,oBACJ,UACA,WACG;GACH,aAAa,UAAU,MAAM;GAC7B,kBAAkB;EACpB;;UAI2B,MAAA,iBAAA,KAAqB,MAAA,eAAA,KAAA,UAAA,GAA9C,YA2BoB,2BAAA;;IA3B2C,MAAK;;2BA0B5D,CAzBN,mBAyBM,OAzBN,cAyBM,CAvBI,MAAA,iBAAA,KAAA,UAAA,GADR,YAKE,wBAAA;KAHC,KAAK,MAAA,iBAAA,CAAiB,CAAC;KACvB,YAAY,MAAA,iBAAA;KACZ,UAAM,OAAA,OAAA,OAAA,MAAG,aAAa,mBAAmB,MAAA,iBAAA,GAAoB,QAAQ;mCAOlD,MAAA,eAAA,KAAA,UAAA,GAAtB,YAYY,WAAA,EAAA,KAAA,EAAA,GAAA,EAAA,UAAA,GAXV,YAUE,sBAAA;KATC,KAAK,MAAA,eAAA,CAAe,CAAC;KACrB,UAAU,MAAA,eAAA;KACV,cAAY,MAAA,aAAA;KACZ,cAAY,MAAA,aAAA;KACZ,mBAAiB,MAAA,cAAA;KACjB,QAAM,MAAA,MAAA;KACN,QAAI,OAAA,OAAA,OAAA,MAAA,WAAE,eAAe,MAAA,eAAA,CAAe;KACpC,QAAI,OAAA,OAAA,OAAA,MAAG,WAAW,eAAe,MAAA,eAAA,GAAkB,MAAM;KACzD,UAAM,OAAA,OAAA,OAAA,MAAG,WAAW,iBAAiB,MAAA,eAAA,GAAkB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EErHxE,MAAM,OAAO;EAWb,MAAM,aAAa,SAAmB,SAAA,YAAgB;EAEtD,MAAM,cAAc,eAAoC,aAAa;EAErE,MAAM,eAAe,eACb,QAAA,YAAY,SAAS,KAAK,QAAA,UAAU,SAAS,CACrD;EAIA,MAAM,aAAa,eAAe,QAAA,YAAY,aAAa,KAAK;EAEhE,MAAM,YAAY,eACV,CAAC,WAAW,SAAS,CAAC,QAAA,WAAW,WAAW,MAAM,KAAK,CAAC,CAAC,SAAS,CAC1E;EAEA,SAAS,iBAAiB;GACxB,MAAM,KAAK,YAAY;GACvB,IAAI,CAAC,IACH;GAKF,4BAA4B;IAC1B,IAAI,CAAC,YAAY,OACf;IAEF,GAAG,MAAM,SAAS;IAClB,MAAM,eAAe,GAAG;IACxB,IAAI,gBAAgB,GAAG,cACrB;IAEF,GAAG,MAAM,SAAS,GAAG,aAAa;GACpC,CAAC;EACH;EAEA,kBAAkB,aAAa,cAAc;EAC7C,MAAM,YAAY,gBAAgB,EAAE,WAAW,KAAK,CAAC;EAErD,SAAS,WAAW;GAClB,IAAI,CAAC,UAAU,OACb;GAEF,KAAK,UAAU,WAAW,MAAM,KAAK,CAAC;EACxC;EAEA,SAAS,UAAU,OAAsB;GAGvC,IAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,YAAY,CAAC,MAAM,aAAa;IAClE,MAAM,eAAe;IACrB,SAAS;GACX;EACF;EAEA,SAAa,EACX,aAAa,YAAY,OAAO,MAAM,EACxC,CAAC;;uBAIC,mBA6DM,OA7DN,YA6DM;IA3DI,QAAA,UAAA,UAAA,GADR,YAIE,4BAAA;;KAFC,QAAQ,QAAA;KACR,eAAY,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,mBAAA;;IAIb,aAAA,SAAA,UAAA,GADR,YAKE,MAAA,oBAAA,GAAA;;KAHC,aAAa,QAAA;KACb,WAAW,QAAA;KACX,WAAO,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,WAAY,MAAM;;IAGlC,mBAsCM,OAAA;KArCH,OAAK,eAAA,CAAA,mBAAA,EAAA,UAAkC,WAAA,MAAU,CAAA,CAAA;KACjD,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,YAAA,OAAa,MAAK;;KAEJ,QAAA,WAAA,UAAA,GAAtB,YAAoE,MAAA,cAAA,GAAA;;MAArC,OAAM;MAAe,eAAA;;KAEpD,mBAWM,OAXN,YAWM,CAAA,eAVJ,mBASE,YAAA;eARI;MAAJ,KAAI;8EACe,QAAA;MACnB,OAAM;MACN,MAAK;MACJ,aAAa,QAAA;MACb,UAAU,WAAA;MACV,cAAY,QAAA;MACH;6CAND,WAAA,KAAU,CAAA,CAAA,CAAA,CAAA;KAUvB,mBAkBM,OAlBN,YAkBM,CAhBI,QAAA,WAAA,UAAA,GADR,YAOE,MAAA,SAAA,GAAA;;MALA,SAAQ;MACR,gBAAa;MACb,MAAK;MACL,WAAU;MACT,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,WAAO,KAAI,MAAA,GAAA,CAAA,MAAA,CAAA;yBAEnB,YAQE,MAAA,SAAA,GAAA;;MANA,SAAQ;MACR,gBAAa;MACb,MAAK;MACL,WAAU;MACT,UAAQ,CAAG,UAAA;MACX,SAAK,cAAO,UAAQ,CAAA,MAAA,CAAA;;;IAKhB,QAAA,eAAe,QAAA,gBAAgB,QAAA,QAAA,UAAA,GAA1C,mBAMM,OANN,YAMM,CALO,QAAA,eAAA,UAAA,GAAX,mBAAoE,OAApE,YAAoE,gBAApB,QAAA,WAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAChD,QAAA,gBAAgB,QAAA,QAAA,UAAA,GAA3B,mBAGM,OAHN,YAGM,CAFQ,QAAA,gBAAA,UAAA,GAAZ,mBAAmD,QAAA,YAAA,gBAAtB,QAAA,YAAY,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAC1B,QAAA,QAAA,UAAA,GAAf,YAAsE,MAAA,OAAA,GAAA;;KAAhD,OAAO,QAAA,KAAK;KAAQ,IAAI,QAAA,KAAK;KAAI,MAAK;;;;;;;;AE7JpE,IAAa,2BAA2B;CACtC,OAAO;CACP,MAAM;AACR;AAEA,IAAa,4BAA4B,OAAO,OAC9C,wBACF;AAEA,IAAa,mCAAmC;CAC9C,OAAO;CACP,MAAM;AACR;AAEA,IAAa,oCAAoC,OAAO,OACtD,gCACF"}
|
package/dist/dataTransfer.js
CHANGED
|
@@ -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-
|
|
3
|
-
import { t as rfcErrors } from "./rfcErrors-
|
|
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.
|
|
116
|
-
/*! @license DOMPurify 3.4.
|
|
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.
|
|
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
|
|
1800
|
-
*
|
|
1801
|
-
*
|
|
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])
|
|
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])
|
|
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
|
|
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)
|
|
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-
|
|
2649
|
+
//# sourceMappingURL=dist-D6REgugT.js.map
|