@knime/product-features 6.1.2 → 6.3.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 +17 -0
- package/README.md +13 -12
- package/dist/ai.css +30 -30
- package/dist/ai.js +58 -21
- package/dist/ai.js.map +1 -1
- package/dist/dataTransfer.js +1 -1
- package/dist/embedding.js +74 -0
- package/dist/embedding.js.map +1 -0
- package/dist/{rfcErrors-C59cEDGj.js → rfcErrors-CNWg8BiD.js} +13 -3
- package/dist/{rfcErrors-C59cEDGj.js.map → rfcErrors-CNWg8BiD.js.map} +1 -1
- package/dist/rfcErrors.js +2 -2
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts +6 -0
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/types.d.ts +33 -0
- package/dist/src/ai/KdsPromptInput/types.d.ts.map +1 -1
- package/dist/src/embedding/capabilities.d.ts +40 -0
- package/dist/src/embedding/capabilities.d.ts.map +1 -0
- package/dist/src/embedding/index.d.ts +6 -0
- package/dist/src/embedding/index.d.ts.map +1 -0
- package/dist/src/embedding/outlets/index.d.ts +23 -0
- package/dist/src/embedding/outlets/index.d.ts.map +1 -0
- package/dist/src/embedding/outlets/spaceExplorer.d.ts +74 -0
- package/dist/src/embedding/outlets/spaceExplorer.d.ts.map +1 -0
- package/dist/src/embedding/toasts.d.ts +14 -0
- package/dist/src/embedding/toasts.d.ts.map +1 -0
- package/dist/src/embedding/types.d.ts +131 -0
- package/dist/src/embedding/types.d.ts.map +1 -0
- package/dist/src/rfcErrors/index.d.ts +10 -0
- package/dist/src/rfcErrors/index.d.ts.map +1 -1
- package/dist/src/toasts/Toast/Toast.vue.d.ts +2 -2
- package/package.json +8 -4
package/dist/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 * - `warning` — warning-muted background (warning status).\n * - `info` — info-muted background (note status).\n */\n tone?: \"neutral\" | \"danger\" | \"warning\" | \"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 &.warning {\n --surface-background: var(--kds-color-background-static-warning-muted);\n --surface-border: var(--kds-border-base-warning);\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 * - `warning` — warning-muted background (warning status).\n * - `info` — info-muted background (note status).\n */\n tone?: \"neutral\" | \"danger\" | \"warning\" | \"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 &.warning {\n --surface-background: var(--kds-color-background-static-warning-muted);\n --surface-border: var(--kds-border-base-warning);\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 { type HTMLAttributes, computed } from \"vue\";\n\nimport { KdsButton, KdsIcon, type KdsIconName } from \"@knime/kds-components\";\n\nimport AttachableSurface from \"./AttachableSurface.vue\";\nimport type { KdsPromptInputStatus, KdsPromptInputStatusType } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n secondaryButtonClick: [];\n}>();\n\n// `error` and `warning` interrupt the user, so they are announced\n// assertively; a `note` is ambient and only announced politely.\nconst statusMapper = {\n error: { tone: \"danger\", icon: \"circle-error\", role: \"alert\" },\n warning: { tone: \"warning\", icon: \"warning\", role: \"alert\" },\n note: { tone: \"info\", icon: \"circle-info\", role: \"status\" },\n} as const satisfies Record<\n KdsPromptInputStatusType,\n { tone: string; icon: KdsIconName; role: HTMLAttributes[\"role\"] }\n>;\n\nconst appearance = computed(() => statusMapper[status.type]);\n\nconst hasBothButtons = computed(\n () => Boolean(status.buttonLabel) && Boolean(status.secondaryButtonLabel),\n);\n</script>\n\n<template>\n <AttachableSurface :tone=\"appearance.tone\">\n <div :class=\"['inline-status-banner', status.type]\" :role=\"appearance.role\">\n <span class=\"icon-wrapper\">\n <KdsIcon :name=\"appearance.icon\" size=\"small\" class=\"icon\" />\n </span>\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 4-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <div\n v-if=\"status.secondaryButtonLabel || status.buttonLabel\"\n :class=\"['actions', { 'both-buttons': hasBothButtons }]\"\n >\n <KdsButton\n v-if=\"status.secondaryButtonLabel\"\n size=\"small\"\n variant=\"transparent\"\n :label=\"status.secondaryButtonLabel\"\n @click=\"emit('secondaryButtonClick')\"\n />\n <KdsButton\n v-if=\"status.buttonLabel\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n flex-wrap: wrap;\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 container-type: inline-size;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n\n .icon-wrapper,\n .actions {\n flex-shrink: 0;\n }\n\n .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n justify-content: flex-end;\n }\n\n @container (width < 480px) {\n .actions.both-buttons {\n flex-basis: 100%;\n }\n }\n\n .icon-wrapper {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-1-5x);\n }\n\n &.error .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n\n &.warning .icon {\n color: var(--kds-color-text-and-icon-warning);\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 4 lines, scroll beyond that. */\n max-height: calc(4 * 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 { type HTMLAttributes, computed } from \"vue\";\n\nimport { KdsButton, KdsIcon, type KdsIconName } from \"@knime/kds-components\";\n\nimport AttachableSurface from \"./AttachableSurface.vue\";\nimport type { KdsPromptInputStatus, KdsPromptInputStatusType } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n secondaryButtonClick: [];\n}>();\n\n// `error` and `warning` interrupt the user, so they are announced\n// assertively; a `note` is ambient and only announced politely.\nconst statusMapper = {\n error: { tone: \"danger\", icon: \"circle-error\", role: \"alert\" },\n warning: { tone: \"warning\", icon: \"warning\", role: \"alert\" },\n note: { tone: \"info\", icon: \"circle-info\", role: \"status\" },\n} as const satisfies Record<\n KdsPromptInputStatusType,\n { tone: string; icon: KdsIconName; role: HTMLAttributes[\"role\"] }\n>;\n\nconst appearance = computed(() => statusMapper[status.type]);\n\nconst hasBothButtons = computed(\n () => Boolean(status.buttonLabel) && Boolean(status.secondaryButtonLabel),\n);\n</script>\n\n<template>\n <AttachableSurface :tone=\"appearance.tone\">\n <div :class=\"['inline-status-banner', status.type]\" :role=\"appearance.role\">\n <span class=\"icon-wrapper\">\n <KdsIcon :name=\"appearance.icon\" size=\"small\" class=\"icon\" />\n </span>\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 4-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <div\n v-if=\"status.secondaryButtonLabel || status.buttonLabel\"\n :class=\"['actions', { 'both-buttons': hasBothButtons }]\"\n >\n <KdsButton\n v-if=\"status.secondaryButtonLabel\"\n size=\"small\"\n variant=\"transparent\"\n :label=\"status.secondaryButtonLabel\"\n @click=\"emit('secondaryButtonClick')\"\n />\n <KdsButton\n v-if=\"status.buttonLabel\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n flex-wrap: wrap;\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 container-type: inline-size;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n\n .icon-wrapper,\n .actions {\n flex-shrink: 0;\n }\n\n .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n justify-content: flex-end;\n }\n\n @container (width < 480px) {\n .actions.both-buttons {\n flex-basis: 100%;\n }\n }\n\n .icon-wrapper {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-1-5x);\n }\n\n &.error .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n\n &.warning .icon {\n color: var(--kds-color-text-and-icon-warning);\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 4 lines, scroll beyond that. */\n max-height: calc(4 * 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 } from \"vue\";\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 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 clicked the secondary button inside the inline status banner. */\n statusSecondaryButtonClick: [];\n /** User decided every pending permission and answered every question. */\n respond: [response: KdsPromptInputInquiryResponse];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst slots = defineSlots<{\n /**\n * Content shown on the left of the subtext row below the input\n * (e.g. an \"Auto approve\" toggle switch). Note that:\n * - truncating text content should be handled by the consumer\n * - conditional rendering of the slot content should be done with a v-if\n * directly on the <template> rather than the content element itself\n */\n \"left-subtext\"?(): unknown;\n}>();\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 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 @secondary-button-click=\"emit('statusSecondaryButtonClick')\"\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 <div v-if=\"loading\" class=\"progress-bar\">\n <KdsProgressBar indeterminate />\n </div>\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=\"slots['left-subtext'] || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"slots['left-subtext']\" class=\"subtext-left\">\n <slot name=\"left-subtext\" />\n </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 field-sizing: content;\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 /* Take all remaining inline space so the right block is pushed to the end. */\n flex: 1 1 0;\n min-width: 0;\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 } from \"vue\";\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 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 clicked the secondary button inside the inline status banner. */\n statusSecondaryButtonClick: [];\n /** User decided every pending permission and answered every question. */\n respond: [response: KdsPromptInputInquiryResponse];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst slots = defineSlots<{\n /**\n * Content shown on the left of the subtext row below the input\n * (e.g. an \"Auto approve\" toggle switch). Note that:\n * - truncating text content should be handled by the consumer\n * - conditional rendering of the slot content should be done with a v-if\n * directly on the <template> rather than the content element itself\n */\n \"left-subtext\"?(): unknown;\n}>();\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 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 @secondary-button-click=\"emit('statusSecondaryButtonClick')\"\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 <div v-if=\"loading\" class=\"progress-bar\">\n <KdsProgressBar indeterminate />\n </div>\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=\"slots['left-subtext'] || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"slots['left-subtext']\" class=\"subtext-left\">\n <slot name=\"left-subtext\" />\n </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 field-sizing: content;\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 /* Take all remaining inline space so the right block is pushed to the end. */\n flex: 1 1 0;\n min-width: 0;\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 WARNING: \"warning\",\n NOTE: \"note\",\n} as const;\n\nexport const kdsPromptInputStatusTypes = Object.values(\n kdsPromptInputStatusType,\n);\n\nexport const kdsPromptInputPermissionDecision = {\n ALLOW: \"allow\",\n DENY: \"deny\",\n} as const;\n\nexport const kdsPromptInputPermissionDecisions = Object.values(\n kdsPromptInputPermissionDecision,\n);\n"],"mappings":"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"}
|
|
1
|
+
{"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 * - `warning` — warning-muted background (warning status).\n * - `info` — info-muted background (note status).\n */\n tone?: \"neutral\" | \"danger\" | \"warning\" | \"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 &.warning {\n --surface-background: var(--kds-color-background-static-warning-muted);\n --surface-border: var(--kds-border-base-warning);\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 * - `warning` — warning-muted background (warning status).\n * - `info` — info-muted background (note status).\n */\n tone?: \"neutral\" | \"danger\" | \"warning\" | \"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 &.warning {\n --surface-background: var(--kds-color-background-static-warning-muted);\n --surface-border: var(--kds-border-base-warning);\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 { type HTMLAttributes, computed } from \"vue\";\n\nimport { KdsButton, KdsIcon, type KdsIconName } from \"@knime/kds-components\";\n\nimport AttachableSurface from \"./AttachableSurface.vue\";\nimport type { KdsPromptInputStatus, KdsPromptInputStatusType } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n secondaryButtonClick: [];\n}>();\n\n// `error` and `warning` interrupt the user, so they are announced\n// assertively; a `note` is ambient and only announced politely.\nconst statusMapper = {\n error: { tone: \"danger\", icon: \"circle-error\", role: \"alert\" },\n warning: { tone: \"warning\", icon: \"warning\", role: \"alert\" },\n note: { tone: \"info\", icon: \"circle-info\", role: \"status\" },\n} as const satisfies Record<\n KdsPromptInputStatusType,\n { tone: string; icon: KdsIconName; role: HTMLAttributes[\"role\"] }\n>;\n\nconst appearance = computed(() => statusMapper[status.type]);\n\nconst hasBothButtons = computed(\n () => Boolean(status.buttonLabel) && Boolean(status.secondaryButtonLabel),\n);\n</script>\n\n<template>\n <AttachableSurface :tone=\"appearance.tone\">\n <div :class=\"['inline-status-banner', status.type]\" :role=\"appearance.role\">\n <span class=\"icon-wrapper\">\n <KdsIcon :name=\"appearance.icon\" size=\"small\" class=\"icon\" />\n </span>\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 4-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <div\n v-if=\"status.secondaryButtonLabel || status.buttonLabel\"\n :class=\"['actions', { 'both-buttons': hasBothButtons }]\"\n >\n <KdsButton\n v-if=\"status.secondaryButtonLabel\"\n size=\"small\"\n variant=\"transparent\"\n :label=\"status.secondaryButtonLabel\"\n @click=\"emit('secondaryButtonClick')\"\n />\n <KdsButton\n v-if=\"status.buttonLabel\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n flex-wrap: wrap;\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 container-type: inline-size;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n\n .icon-wrapper,\n .actions {\n flex-shrink: 0;\n }\n\n .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n justify-content: flex-end;\n }\n\n @container (width < 480px) {\n .actions.both-buttons {\n flex-basis: 100%;\n }\n }\n\n .icon-wrapper {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-1-5x);\n }\n\n &.error .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n\n &.warning .icon {\n color: var(--kds-color-text-and-icon-warning);\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 4 lines, scroll beyond that. */\n max-height: calc(4 * 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 { type HTMLAttributes, computed } from \"vue\";\n\nimport { KdsButton, KdsIcon, type KdsIconName } from \"@knime/kds-components\";\n\nimport AttachableSurface from \"./AttachableSurface.vue\";\nimport type { KdsPromptInputStatus, KdsPromptInputStatusType } from \"./types\";\n\nconst { status } = defineProps<{\n status: KdsPromptInputStatus;\n}>();\n\nconst emit = defineEmits<{\n buttonClick: [];\n secondaryButtonClick: [];\n}>();\n\n// `error` and `warning` interrupt the user, so they are announced\n// assertively; a `note` is ambient and only announced politely.\nconst statusMapper = {\n error: { tone: \"danger\", icon: \"circle-error\", role: \"alert\" },\n warning: { tone: \"warning\", icon: \"warning\", role: \"alert\" },\n note: { tone: \"info\", icon: \"circle-info\", role: \"status\" },\n} as const satisfies Record<\n KdsPromptInputStatusType,\n { tone: string; icon: KdsIconName; role: HTMLAttributes[\"role\"] }\n>;\n\nconst appearance = computed(() => statusMapper[status.type]);\n\nconst hasBothButtons = computed(\n () => Boolean(status.buttonLabel) && Boolean(status.secondaryButtonLabel),\n);\n</script>\n\n<template>\n <AttachableSurface :tone=\"appearance.tone\">\n <div :class=\"['inline-status-banner', status.type]\" :role=\"appearance.role\">\n <span class=\"icon-wrapper\">\n <KdsIcon :name=\"appearance.icon\" size=\"small\" class=\"icon\" />\n </span>\n <!--\n tabindex=\"0\" so keyboard users can scroll the message when it\n overflows the 4-line cap (axe rule: scrollable-region-focusable).\n -->\n <span class=\"text\" tabindex=\"0\">{{ status.message }}</span>\n <div\n v-if=\"status.secondaryButtonLabel || status.buttonLabel\"\n :class=\"['actions', { 'both-buttons': hasBothButtons }]\"\n >\n <KdsButton\n v-if=\"status.secondaryButtonLabel\"\n size=\"small\"\n variant=\"transparent\"\n :label=\"status.secondaryButtonLabel\"\n @click=\"emit('secondaryButtonClick')\"\n />\n <KdsButton\n v-if=\"status.buttonLabel\"\n size=\"small\"\n variant=\"filled\"\n :label=\"status.buttonLabel\"\n @click=\"emit('buttonClick')\"\n />\n </div>\n </div>\n </AttachableSurface>\n</template>\n\n<style scoped>\n.inline-status-banner {\n display: flex;\n flex-wrap: wrap;\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 container-type: inline-size;\n font: var(--kds-font-base-body-small);\n color: var(--kds-color-text-and-icon-neutral);\n\n .icon-wrapper,\n .actions {\n flex-shrink: 0;\n }\n\n .actions {\n display: flex;\n gap: var(--kds-spacing-container-0-25x);\n align-items: center;\n justify-content: flex-end;\n }\n\n @container (width < 480px) {\n .actions.both-buttons {\n flex-basis: 100%;\n }\n }\n\n .icon-wrapper {\n display: flex;\n align-items: center;\n height: var(--kds-dimension-component-height-1-5x);\n }\n\n &.error .icon {\n color: var(--kds-color-text-and-icon-danger);\n }\n\n &.warning .icon {\n color: var(--kds-color-text-and-icon-warning);\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 4 lines, scroll beyond that. */\n max-height: calc(4 * 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 } from \"vue\";\n\nimport {\n KdsButton,\n KdsChip,\n KdsLink,\n KdsMenuButton,\n KdsProgressBar,\n} 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 rightSubtext = undefined,\n link = undefined,\n permissions = [],\n questions = [],\n menu = undefined,\n contextChips = [],\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 clicked the secondary button inside the inline status banner. */\n statusSecondaryButtonClick: [];\n /** User decided every pending permission and answered every question. */\n respond: [response: KdsPromptInputInquiryResponse];\n /** User clicked an item of the \"+\" menu. */\n menuItemClick: [id: string];\n /** User clicked the remove button of the context chip with the given `id`. */\n contextChipRemove: [id: string];\n /** User clicked the label of the clickable context chip with the given `id`. */\n contextChipLabelClick: [id: string];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst slots = defineSlots<{\n /**\n * Content shown on the left of the subtext row below the input\n * (e.g. an \"Auto approve\" toggle switch). Note that:\n * - truncating text content should be handled by the consumer\n * - conditional rendering of the slot content should be done with a v-if\n * directly on the <template> rather than the content element itself\n */\n \"left-subtext\"?(): unknown;\n}>();\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 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 @secondary-button-click=\"emit('statusSecondaryButtonClick')\"\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 <div v-if=\"loading\" class=\"progress-bar\">\n <KdsProgressBar indeterminate />\n </div>\n\n <div v-if=\"contextChips.length > 0\" class=\"context-chips\">\n <KdsChip\n v-for=\"{ id, ...chipProps } in contextChips\"\n :key=\"id\"\n v-bind=\"chipProps\"\n :disabled=\"isDisabled || chipProps.disabled\"\n @remove=\"emit('contextChipRemove', id)\"\n @label-click=\"emit('contextChipLabelClick', id)\"\n />\n </div>\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=\"bottom-row\">\n <div v-if=\"menu\" class=\"menu\" @click.stop>\n <KdsMenuButton\n variant=\"transparent\"\n size=\"small\"\n leading-icon=\"plus\"\n :ariaLabel=\"menu.ariaLabel ?? 'Add'\"\n :items=\"menu.items\"\n :disabled=\"isDisabled\"\n @item-click=\"emit('menuItemClick', $event)\"\n />\n </div>\n\n <div class=\"action\">\n <KdsButton\n v-if=\"loading\"\n variant=\"outlined\"\n leading-icon=\"stop\"\n size=\"small\"\n ariaLabel=\"Stop\"\n @click.stop=\"emit('stop')\"\n />\n <KdsButton\n v-else\n variant=\"filled\"\n leading-icon=\"send\"\n size=\"small\"\n ariaLabel=\"Send\"\n :disabled=\"!canSubmit\"\n @click.stop=\"onSubmit\"\n />\n </div>\n </div>\n </div>\n\n <div v-if=\"slots['left-subtext'] || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"slots['left-subtext']\" class=\"subtext-left\">\n <slot name=\"left-subtext\" />\n </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 flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\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.context-chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x);\n}\n\n.text-input-wrapper {\n display: flex;\n min-height: var(--kds-dimension-component-height-2-5x);\n padding: var(--kds-spacing-container-0-12x);\n}\n\n.text-input {\n width: 100%;\n min-width: 0;\n\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n field-sizing: content;\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.bottom-row {\n display: flex;\n align-items: center;\n}\n\n.action {\n display: flex;\n margin-left: auto;\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 /* Take all remaining inline space so the right block is pushed to the end. */\n flex: 1 1 0;\n min-width: 0;\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 } from \"vue\";\n\nimport {\n KdsButton,\n KdsChip,\n KdsLink,\n KdsMenuButton,\n KdsProgressBar,\n} 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 rightSubtext = undefined,\n link = undefined,\n permissions = [],\n questions = [],\n menu = undefined,\n contextChips = [],\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 clicked the secondary button inside the inline status banner. */\n statusSecondaryButtonClick: [];\n /** User decided every pending permission and answered every question. */\n respond: [response: KdsPromptInputInquiryResponse];\n /** User clicked an item of the \"+\" menu. */\n menuItemClick: [id: string];\n /** User clicked the remove button of the context chip with the given `id`. */\n contextChipRemove: [id: string];\n /** User clicked the label of the clickable context chip with the given `id`. */\n contextChipLabelClick: [id: string];\n}>();\n\nconst modelValue = defineModel<string>({ default: \"\" });\n\nconst slots = defineSlots<{\n /**\n * Content shown on the left of the subtext row below the input\n * (e.g. an \"Auto approve\" toggle switch). Note that:\n * - truncating text content should be handled by the consumer\n * - conditional rendering of the slot content should be done with a v-if\n * directly on the <template> rather than the content element itself\n */\n \"left-subtext\"?(): unknown;\n}>();\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 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 @secondary-button-click=\"emit('statusSecondaryButtonClick')\"\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 <div v-if=\"loading\" class=\"progress-bar\">\n <KdsProgressBar indeterminate />\n </div>\n\n <div v-if=\"contextChips.length > 0\" class=\"context-chips\">\n <KdsChip\n v-for=\"{ id, ...chipProps } in contextChips\"\n :key=\"id\"\n v-bind=\"chipProps\"\n :disabled=\"isDisabled || chipProps.disabled\"\n @remove=\"emit('contextChipRemove', id)\"\n @label-click=\"emit('contextChipLabelClick', id)\"\n />\n </div>\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=\"bottom-row\">\n <div v-if=\"menu\" class=\"menu\" @click.stop>\n <KdsMenuButton\n variant=\"transparent\"\n size=\"small\"\n leading-icon=\"plus\"\n :ariaLabel=\"menu.ariaLabel ?? 'Add'\"\n :items=\"menu.items\"\n :disabled=\"isDisabled\"\n @item-click=\"emit('menuItemClick', $event)\"\n />\n </div>\n\n <div class=\"action\">\n <KdsButton\n v-if=\"loading\"\n variant=\"outlined\"\n leading-icon=\"stop\"\n size=\"small\"\n ariaLabel=\"Stop\"\n @click.stop=\"emit('stop')\"\n />\n <KdsButton\n v-else\n variant=\"filled\"\n leading-icon=\"send\"\n size=\"small\"\n ariaLabel=\"Send\"\n :disabled=\"!canSubmit\"\n @click.stop=\"onSubmit\"\n />\n </div>\n </div>\n </div>\n\n <div v-if=\"slots['left-subtext'] || rightSubtext || link\" class=\"subtext\">\n <div v-if=\"slots['left-subtext']\" class=\"subtext-left\">\n <slot name=\"left-subtext\" />\n </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 flex-direction: column;\n gap: var(--kds-spacing-container-0-25x);\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.context-chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--kds-spacing-container-0-25x);\n}\n\n.text-input-wrapper {\n display: flex;\n min-height: var(--kds-dimension-component-height-2-5x);\n padding: var(--kds-spacing-container-0-12x);\n}\n\n.text-input {\n width: 100%;\n min-width: 0;\n\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n field-sizing: content;\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.bottom-row {\n display: flex;\n align-items: center;\n}\n\n.action {\n display: flex;\n margin-left: auto;\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 /* Take all remaining inline space so the right block is pushed to the end. */\n flex: 1 1 0;\n min-width: 0;\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 WARNING: \"warning\",\n NOTE: \"note\",\n} as const;\n\nexport const kdsPromptInputStatusTypes = Object.values(\n kdsPromptInputStatusType,\n);\n\nexport const kdsPromptInputPermissionDecision = {\n ALLOW: \"allow\",\n DENY: \"deny\",\n} as const;\n\nexport const kdsPromptInputPermissionDecisions = Object.values(\n kdsPromptInputPermissionDecision,\n);\n"],"mappings":"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"}
|
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
2
|
import { a as getFileExtension, c as promise_exports, l as sleep, r as formatFileSize, s as knimeFileFormats } from "./dist-htLRPkoq.js";
|
|
3
|
-
import {
|
|
3
|
+
import { n as rfcErrors } from "./rfcErrors-CNWg8BiD.js";
|
|
4
4
|
import { a as createUploadManager, c as API_BASE_URL, n as createUploadClient, o as createHttpClient, s as defaultConfig, t as createUploadStatusPoller } from "./core-CIwP11Fc.js";
|
|
5
5
|
import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, openBlock, ref, renderList, renderSlot, toDisplayString, unref, useId, useModel, vShow, watch, withCtx, withDirectives } from "vue";
|
|
6
6
|
import { KdsBadge, KdsButton, KdsIcon, KdsLoadingSkeleton, KdsProgressBar, vKdsTooltip } from "@knime/kds-components";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { n as rfcErrors, t as RFC_ERROR_TOAST_META_KEY } from "./rfcErrors-CNWg8BiD.js";
|
|
2
|
+
//#region src/embedding/outlets/spaceExplorer.ts
|
|
3
|
+
/**
|
|
4
|
+
* Keep only the capabilities that this contract version knows.
|
|
5
|
+
*/
|
|
6
|
+
var resolveSpaceExplorerGuestCapabilities = (declared) => ({ ...declared.itemDrop && { itemDrop: true } });
|
|
7
|
+
var resolveSpaceExplorerHostCapabilities = (declared) => ({ ...declared.revealItem && { revealItem: true } });
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/embedding/outlets/index.ts
|
|
10
|
+
var outletCapabilityResolvers = { spaceExplorer: {
|
|
11
|
+
guest: resolveSpaceExplorerGuestCapabilities,
|
|
12
|
+
host: resolveSpaceExplorerHostCapabilities
|
|
13
|
+
} };
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/embedding/capabilities.ts
|
|
16
|
+
/**
|
|
17
|
+
* Version of `@knime/product-features` that this build uses. Informational
|
|
18
|
+
* only: logs, support, deprecation tracking. It never gates behavior.
|
|
19
|
+
*/
|
|
20
|
+
var contractVersion = "6.3.0";
|
|
21
|
+
/**
|
|
22
|
+
* Outlets that both sides declare. Capabilities that this contract version does not know are
|
|
23
|
+
* dropped.
|
|
24
|
+
*/
|
|
25
|
+
var resolveOutlets = (guest, host) => {
|
|
26
|
+
const resolved = {};
|
|
27
|
+
for (const [name, resolvers] of Object.entries(outletCapabilityResolvers)) {
|
|
28
|
+
const guestDeclared = guest.outlets?.[name];
|
|
29
|
+
const hostDeclared = host.outlets?.[name];
|
|
30
|
+
if (guestDeclared && hostDeclared) resolved[name] = {
|
|
31
|
+
guest: resolvers.guest(guestDeclared),
|
|
32
|
+
host: resolvers.host(hostDeclared)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return resolved;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/embedding/toasts.ts
|
|
39
|
+
/**
|
|
40
|
+
* Guest: turns a toast into data the host can render. An RFC error toast
|
|
41
|
+
* keeps its error data. Any other component is dropped, because the host
|
|
42
|
+
* cannot render a component from the guest's Vue runtime.
|
|
43
|
+
*/
|
|
44
|
+
var toHostToast = (toast) => {
|
|
45
|
+
const { component, meta, ...rest } = toast;
|
|
46
|
+
const rfcError = meta?.[RFC_ERROR_TOAST_META_KEY];
|
|
47
|
+
if (component && !rfcError) consola.warn("Embedding: dropped the component of a toast", toast);
|
|
48
|
+
return {
|
|
49
|
+
...rest,
|
|
50
|
+
...meta && { meta },
|
|
51
|
+
...rfcError && { rfcError }
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Host: turns a toast from the guest into a toast for its own
|
|
56
|
+
* toast service context.
|
|
57
|
+
*/
|
|
58
|
+
var fromHostToast = (hostToast) => {
|
|
59
|
+
const { rfcError, ...toast } = hostToast;
|
|
60
|
+
if (!rfcError) return toast;
|
|
61
|
+
return {
|
|
62
|
+
...toast,
|
|
63
|
+
...rfcErrors.toToast({
|
|
64
|
+
headline: toast.headline ?? "",
|
|
65
|
+
rfcError: new rfcErrors.RFCError(rfcError.data),
|
|
66
|
+
canCopyToClipboard: rfcError.canCopyToClipboard,
|
|
67
|
+
serializeErrorForClipboard: rfcError.serializeErrorForClipboard
|
|
68
|
+
})
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { contractVersion, fromHostToast, resolveOutlets, toHostToast };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=embedding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.js","names":[],"sources":["../src/embedding/outlets/spaceExplorer.ts","../src/embedding/outlets/index.ts","../src/embedding/capabilities.ts","../src/embedding/toasts.ts"],"sourcesContent":["/**\n * An item that the space explorer drags onto the canvas.\n */\nexport type SpaceExplorerDragItem = {\n spaceId: string;\n itemId: string;\n name: string;\n type: string;\n};\n\n/**\n * Drags items from the space explorer onto the another container. This allows custom control\n * over rendering a drag preview and also running custom logic when an item is dropped onto another\n * container.\n */\nexport type SpaceExplorerItemDrop = {\n start: (item: SpaceExplorerDragItem) => void;\n /**\n * When `accepts` is true, the space explorer hides its own drag ghost.\n */\n move: (clientX: number, clientY: number) => { accepts: boolean };\n /**\n * Resolves to whether the guest added the item.\n */\n end: (clientX: number, clientY: number) => Promise<boolean>;\n cancel: () => void;\n};\n\n/**\n * Announced by the guest when the element the outlet will render to exists.\n */\nexport type SpaceExplorerOutletHandle = {\n name: \"spaceExplorer\";\n /**\n * A stable, empty element. Must not be patched/removed by the guest\n */\n element: HTMLElement;\n /**\n * Present when both sides declare `itemDrop`.\n */\n itemDrop?: SpaceExplorerItemDrop;\n};\n\n/**\n * The space explorer's public api. Returned after `announce`ing the feature.\n */\nexport type SpaceExplorerFeatureAPI = {\n /**\n * Shows the item: its parent folder, with the item selected. Present when\n * the host declares `revealItem`.\n */\n revealItem?: (item: { spaceId: string; itemId: string }) => void;\n};\n\n/**\n * Capabilities the space explorer expects the guest to implement\n */\nexport type SpaceExplorerGuestCapabilities = {\n itemDrop?: true;\n};\n\n/**\n * Capabilities the space explorer of the host offers to the guest for public/direct usage\n * (all behavior is normally self-contained).\n */\nexport type SpaceExplorerHostCapabilities = {\n revealItem?: true;\n};\n\n/**\n * Keep only the capabilities that this contract version knows.\n */\nexport const resolveSpaceExplorerGuestCapabilities = (\n declared: SpaceExplorerGuestCapabilities,\n): SpaceExplorerGuestCapabilities => ({\n ...(declared.itemDrop && { itemDrop: true }),\n});\n\nexport const resolveSpaceExplorerHostCapabilities = (\n declared: SpaceExplorerHostCapabilities,\n): SpaceExplorerHostCapabilities => ({\n ...(declared.revealItem && { revealItem: true }),\n});\n","import {\n type SpaceExplorerFeatureAPI,\n type SpaceExplorerGuestCapabilities,\n type SpaceExplorerHostCapabilities,\n type SpaceExplorerOutletHandle,\n resolveSpaceExplorerGuestCapabilities,\n resolveSpaceExplorerHostCapabilities,\n} from \"./spaceExplorer\";\n\n/**\n * Outlets the guest can expose (the editor in this case). Each outlet contains one feature of the host.\n */\nexport type OutletHandle = SpaceExplorerOutletHandle;\n\nexport type OutletName = OutletHandle[\"name\"];\n\nexport type OutletRegistry = {\n spaceExplorer: SpaceExplorerFeatureAPI;\n};\n\nexport type GuestCapabilitiesForOutlet = {\n spaceExplorer: SpaceExplorerGuestCapabilities;\n};\n\nexport type HostCapabilitiesForOutlet = {\n spaceExplorer: SpaceExplorerHostCapabilities;\n};\n\nexport const outletCapabilityResolvers: {\n [N in OutletName]: {\n guest: (\n declared: GuestCapabilitiesForOutlet[N],\n ) => GuestCapabilitiesForOutlet[N];\n host: (\n declared: HostCapabilitiesForOutlet[N],\n ) => HostCapabilitiesForOutlet[N];\n };\n} = {\n spaceExplorer: {\n guest: resolveSpaceExplorerGuestCapabilities,\n host: resolveSpaceExplorerHostCapabilities,\n },\n};\n\nexport type {\n SpaceExplorerDragItem,\n SpaceExplorerFeatureAPI,\n SpaceExplorerItemDrop,\n SpaceExplorerOutletHandle,\n} from \"./spaceExplorer\";\n","import {\n type GuestCapabilitiesForOutlet,\n type HostCapabilitiesForOutlet,\n type OutletName,\n outletCapabilityResolvers,\n} from \"./outlets\";\n\n/**\n * Version of `@knime/product-features` that this build uses. Informational\n * only: logs, support, deprecation tracking. It never gates behavior.\n */\nexport const contractVersion: string = __PRODUCT_FEATURES_VERSION__;\n\n/**\n * Static declaration of the guest app. The host reads it before embedding.\n */\nexport type GuestCapabilities = {\n contractVersion: string;\n /**\n * Outlets the guest declares and supports. The host does not send a feature\n * for an outlet the guest does not support\n */\n outlets: Partial<GuestCapabilitiesForOutlet>;\n};\n\n/**\n * Static declaration of the host, passed as context for mounting the embedded guest\n */\nexport type HostCapabilities = {\n contractVersion: string;\n /**\n * Outlets the host has features for. The guest does not render an outlet that the\n * host cannot provide.\n */\n outlets: Partial<HostCapabilitiesForOutlet>;\n};\n\nexport type ResolvedOutlets = {\n [N in OutletName]?: {\n guest: GuestCapabilitiesForOutlet[N];\n host: HostCapabilitiesForOutlet[N];\n };\n};\n\n/**\n * Outlets that both sides declare. Capabilities that this contract version does not know are\n * dropped.\n */\nexport const resolveOutlets = (\n guest: GuestCapabilities,\n host: HostCapabilities,\n): ResolvedOutlets => {\n const resolved: ResolvedOutlets = {};\n\n for (const [name, resolvers] of Object.entries(outletCapabilityResolvers)) {\n const guestDeclared = guest.outlets?.[name as OutletName];\n const hostDeclared = host.outlets?.[name as OutletName];\n if (guestDeclared && hostDeclared) {\n resolved[name as OutletName] = {\n guest: resolvers.guest(guestDeclared),\n host: resolvers.host(hostDeclared),\n };\n }\n }\n\n return resolved;\n};\n","import {\n type RFCErrorToastMeta,\n RFC_ERROR_TOAST_META_KEY,\n rfcErrors,\n} from \"../rfcErrors\";\nimport type { Toast } from \"../toasts\";\n\nimport type { HostToast } from \"./types\";\n\n/**\n * Guest: turns a toast into data the host can render. An RFC error toast\n * keeps its error data. Any other component is dropped, because the host\n * cannot render a component from the guest's Vue runtime.\n */\nexport const toHostToast = (toast: Toast): HostToast => {\n const { component, meta, ...rest } = toast;\n const rfcError = meta?.[RFC_ERROR_TOAST_META_KEY] as\n RFCErrorToastMeta | undefined;\n\n if (component && !rfcError) {\n consola.warn(\"Embedding: dropped the component of a toast\", toast);\n }\n\n return {\n ...rest,\n ...(meta && { meta }),\n ...(rfcError && { rfcError }),\n };\n};\n\n/**\n * Host: turns a toast from the guest into a toast for its own\n * toast service context.\n */\nexport const fromHostToast = (hostToast: HostToast): Toast => {\n const { rfcError, ...toast } = hostToast;\n\n if (!rfcError) {\n return toast;\n }\n\n return {\n ...toast,\n ...rfcErrors.toToast({\n headline: toast.headline ?? \"\",\n rfcError: new rfcErrors.RFCError(rfcError.data),\n canCopyToClipboard: rfcError.canCopyToClipboard,\n serializeErrorForClipboard: rfcError.serializeErrorForClipboard,\n }),\n };\n};\n"],"mappings":";;;;;AAwEA,IAAa,yCACX,cACoC,EACpC,GAAI,SAAS,YAAY,EAAE,UAAU,KAAK,EAC5C;AAEA,IAAa,wCACX,cACmC,EACnC,GAAI,SAAS,cAAc,EAAE,YAAY,KAAK,EAChD;;;ACtDA,IAAa,4BAST,EACF,eAAe;CACb,OAAO;CACP,MAAM;AACR,EACF;;;;;;;AC/BA,IAAa,kBAAA;;;;;AAqCb,IAAa,kBACX,OACA,SACoB;CACpB,MAAM,WAA4B,CAAC;CAEnC,KAAK,MAAM,CAAC,MAAM,cAAc,OAAO,QAAQ,yBAAyB,GAAG;EACzE,MAAM,gBAAgB,MAAM,UAAU;EACtC,MAAM,eAAe,KAAK,UAAU;EACpC,IAAI,iBAAiB,cACnB,SAAS,QAAsB;GAC7B,OAAO,UAAU,MAAM,aAAa;GACpC,MAAM,UAAU,KAAK,YAAY;EACnC;CAEJ;CAEA,OAAO;AACT;;;;;;;;ACpDA,IAAa,eAAe,UAA4B;CACtD,MAAM,EAAE,WAAW,MAAM,GAAG,SAAS;CACrC,MAAM,WAAW,OAAO;CAGxB,IAAI,aAAa,CAAC,UAChB,QAAQ,KAAK,+CAA+C,KAAK;CAGnE,OAAO;EACL,GAAG;EACH,GAAI,QAAQ,EAAE,KAAK;EACnB,GAAI,YAAY,EAAE,SAAS;CAC7B;AACF;;;;;AAMA,IAAa,iBAAiB,cAAgC;CAC5D,MAAM,EAAE,UAAU,GAAG,UAAU;CAE/B,IAAI,CAAC,UACH,OAAO;CAGT,OAAO;EACL,GAAG;EACH,GAAG,UAAU,QAAQ;GACnB,UAAU,MAAM,YAAY;GAC5B,UAAU,IAAI,UAAU,SAAS,SAAS,IAAI;GAC9C,oBAAoB,SAAS;GAC7B,4BAA4B,SAAS;EACvC,CAAC;CACH;AACF"}
|
|
@@ -125,6 +125,11 @@ var RFCError = class extends Error {
|
|
|
125
125
|
//#endregion
|
|
126
126
|
//#region src/rfcErrors/index.ts
|
|
127
127
|
/**
|
|
128
|
+
* Key in `Toast.meta` under which `toToast` keeps the error data, so the toast
|
|
129
|
+
* can be rebuilt where its component cannot be rendered.
|
|
130
|
+
*/
|
|
131
|
+
var RFC_ERROR_TOAST_META_KEY = "rfcError";
|
|
132
|
+
/**
|
|
128
133
|
* Map to a toast template component compatible with an RFCError format
|
|
129
134
|
*/
|
|
130
135
|
var toToast = ({ headline, rfcError, canCopyToClipboard = true, serializeErrorForClipboard = void 0 }) => {
|
|
@@ -138,7 +143,12 @@ var toToast = ({ headline, rfcError, canCopyToClipboard = true, serializeErrorFo
|
|
|
138
143
|
canCopyToClipboard,
|
|
139
144
|
serializeErrorForClipboard
|
|
140
145
|
}),
|
|
141
|
-
autoRemove: false
|
|
146
|
+
autoRemove: false,
|
|
147
|
+
meta: { [RFC_ERROR_TOAST_META_KEY]: {
|
|
148
|
+
data,
|
|
149
|
+
canCopyToClipboard,
|
|
150
|
+
serializeErrorForClipboard
|
|
151
|
+
} }
|
|
142
152
|
};
|
|
143
153
|
};
|
|
144
154
|
var isFetchError = (error) => {
|
|
@@ -167,6 +177,6 @@ var rfcErrors = {
|
|
|
167
177
|
RFCError
|
|
168
178
|
};
|
|
169
179
|
//#endregion
|
|
170
|
-
export { rfcErrors as t };
|
|
180
|
+
export { rfcErrors as n, RFC_ERROR_TOAST_META_KEY as t };
|
|
171
181
|
|
|
172
|
-
//# sourceMappingURL=rfcErrors-
|
|
182
|
+
//# sourceMappingURL=rfcErrors-CNWg8BiD.js.map
|