@knime/product-features 6.4.0 → 6.5.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 +7 -0
- package/dist/ai.css +21 -21
- package/dist/ai.js +1 -1
- package/dist/ai.js.map +1 -1
- package/dist/{core-CIwP11Fc.js → core-CkhE7SJE.js} +2 -2
- package/dist/{core-CIwP11Fc.js.map → core-CkhE7SJE.js.map} +1 -1
- package/dist/dataTransfer/core.js +1 -1
- package/dist/dataTransfer.js +3 -31
- package/dist/dataTransfer.js.map +1 -1
- package/dist/{dist-htLRPkoq.js → dist-CGsTYtOF.js} +27 -2
- package/dist/{dist-htLRPkoq.js.map → dist-CGsTYtOF.js.map} +1 -1
- package/dist/embedding.js +30 -6
- package/dist/embedding.js.map +1 -1
- package/dist/embeddingSDK.js +1 -1
- package/dist/{rfcErrors-CNWg8BiD.js → rfcErrors-D60RdfXR.js} +2 -2
- package/dist/{rfcErrors-CNWg8BiD.js.map → rfcErrors-D60RdfXR.js.map} +1 -1
- package/dist/rfcErrors.js +1 -1
- package/dist/src/dataTransfer/upload/useFileUpload.d.ts.map +1 -1
- package/dist/src/embedding/index.d.ts +1 -1
- package/dist/src/embedding/index.d.ts.map +1 -1
- package/dist/src/embedding/outlets/index.d.ts +6 -1
- package/dist/src/embedding/outlets/index.d.ts.map +1 -1
- package/dist/src/embedding/outlets/versions.d.ts +93 -0
- package/dist/src/embedding/outlets/versions.d.ts.map +1 -0
- package/dist/toasts.js +1 -1
- package/dist/versions.js +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @knime/product-features
|
|
2
2
|
|
|
3
|
+
## 6.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fcf0836: `embedding`: Add the `versions` outlet, so the host can inject its version history feature into the embedded editor the same way it injects `spaceExplorer`. Guest capabilities: `switchVersion` (load a version live), `prepareContentChange` (save/discard unsaved edits before the host restores or discards), and per-action reload notifications `onVersionRestored`/`onChangesDiscarded`/`onVersionDeleted`. Host capabilities: `getVersionTitle` (resolve a version's title for the editor's own UI), `reportVersionRestored` (the editor tells the host it restored a version through its own UI, so the host can refresh its panel), and `refreshVersions` (the editor tells the host its content was saved, so the host can refresh its panel's data if needed).
|
|
8
|
+
- 59e09e9: `embedding`: Rename the `versions` outlet's `prepareContentChange` guest capability to `syncUnsavedChanges`, and change its return type from `Promise<boolean>` to `Promise<{ didSync: boolean }>`.
|
|
9
|
+
|
|
3
10
|
## 6.4.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
package/dist/ai.css
CHANGED
|
@@ -194,13 +194,13 @@
|
|
|
194
194
|
var(--kds-spacing-container-1x);
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
.prompt-input[data-v-
|
|
197
|
+
.prompt-input[data-v-733ae57a] {
|
|
198
198
|
display: flex;
|
|
199
199
|
flex-direction: column;
|
|
200
200
|
width: 100%;
|
|
201
201
|
}
|
|
202
202
|
.input-container {
|
|
203
|
-
&[data-v-
|
|
203
|
+
&[data-v-733ae57a] {
|
|
204
204
|
position: relative;
|
|
205
205
|
display: flex;
|
|
206
206
|
flex-direction: column;
|
|
@@ -214,75 +214,75 @@
|
|
|
214
214
|
border: var(--kds-border-action-input);
|
|
215
215
|
border-radius: var(--kds-border-radius-container-0-37x);
|
|
216
216
|
}
|
|
217
|
-
&[data-v-
|
|
217
|
+
&[data-v-733ae57a]:has(textarea:focus:not(:disabled)) {
|
|
218
218
|
outline: var(--kds-border-action-focused);
|
|
219
219
|
outline-offset: var(--kds-spacing-offset-focus);
|
|
220
220
|
}
|
|
221
|
-
&[data-v-
|
|
221
|
+
&[data-v-733ae57a]:has(textarea:hover:not(:disabled, :focus)) {
|
|
222
222
|
background: var(--kds-color-background-input-hover);
|
|
223
223
|
}
|
|
224
|
-
&.disabled[data-v-
|
|
224
|
+
&.disabled[data-v-733ae57a] {
|
|
225
225
|
cursor: default;
|
|
226
226
|
border: var(--kds-border-action-disabled);
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
|
-
.context-chips[data-v-
|
|
229
|
+
.context-chips[data-v-733ae57a] {
|
|
230
230
|
display: flex;
|
|
231
231
|
flex-wrap: wrap;
|
|
232
232
|
gap: var(--kds-spacing-container-0-25x);
|
|
233
233
|
}
|
|
234
|
-
.text-input-wrapper[data-v-
|
|
234
|
+
.text-input-wrapper[data-v-733ae57a] {
|
|
235
235
|
display: flex;
|
|
236
236
|
min-height: var(--kds-dimension-component-height-2-5x);
|
|
237
237
|
padding: var(--kds-spacing-container-0-12x);
|
|
238
238
|
}
|
|
239
239
|
.text-input {
|
|
240
|
-
&[data-v-
|
|
240
|
+
&[data-v-733ae57a] {
|
|
241
241
|
width: 100%;
|
|
242
242
|
min-width: 0;
|
|
243
|
-
|
|
244
|
-
/* Cap textarea growth at 4 lines, scroll beyond that. */
|
|
245
|
-
field-sizing: content;
|
|
246
243
|
max-height: calc(4 * 1lh);
|
|
247
244
|
padding: 0;
|
|
248
245
|
overflow-y: auto;
|
|
249
246
|
font: var(--kds-font-base-body-small);
|
|
250
247
|
color: var(--kds-color-text-and-icon-neutral);
|
|
248
|
+
|
|
249
|
+
/* Cap textarea growth at 4 lines, scroll beyond that. */
|
|
250
|
+
field-sizing: content;
|
|
251
251
|
resize: none;
|
|
252
252
|
outline: none;
|
|
253
253
|
scrollbar-width: none;
|
|
254
254
|
background: transparent;
|
|
255
255
|
border: none;
|
|
256
256
|
}
|
|
257
|
-
&[data-v-
|
|
257
|
+
&[data-v-733ae57a]::placeholder {
|
|
258
258
|
color: var(--kds-color-text-and-icon-subtle);
|
|
259
259
|
}
|
|
260
260
|
&:disabled {
|
|
261
|
-
&[data-v-
|
|
261
|
+
&[data-v-733ae57a] {
|
|
262
262
|
color: var(--kds-color-text-and-icon-disabled);
|
|
263
263
|
cursor: default;
|
|
264
264
|
}
|
|
265
|
-
&[data-v-
|
|
265
|
+
&[data-v-733ae57a]::placeholder {
|
|
266
266
|
color: var(--kds-color-text-and-icon-disabled);
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
|
-
&[data-v-
|
|
269
|
+
&[data-v-733ae57a]::-webkit-scrollbar {
|
|
270
270
|
display: none;
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
|
-
.bottom-row[data-v-
|
|
273
|
+
.bottom-row[data-v-733ae57a] {
|
|
274
274
|
display: flex;
|
|
275
275
|
align-items: center;
|
|
276
276
|
}
|
|
277
|
-
.action[data-v-
|
|
277
|
+
.action[data-v-733ae57a] {
|
|
278
278
|
display: flex;
|
|
279
279
|
margin-left: auto;
|
|
280
280
|
}
|
|
281
|
-
.progress-bar[data-v-
|
|
281
|
+
.progress-bar[data-v-733ae57a] {
|
|
282
282
|
position: absolute;
|
|
283
283
|
inset: -1px 0 auto;
|
|
284
284
|
}
|
|
285
|
-
.subtext[data-v-
|
|
285
|
+
.subtext[data-v-733ae57a] {
|
|
286
286
|
display: flex;
|
|
287
287
|
flex-wrap: wrap;
|
|
288
288
|
gap: var(--kds-spacing-container-0-25x) var(--kds-spacing-container-0-5x);
|
|
@@ -292,12 +292,12 @@
|
|
|
292
292
|
font: var(--kds-font-base-subtext-small);
|
|
293
293
|
color: var(--kds-color-text-and-icon-neutral);
|
|
294
294
|
}
|
|
295
|
-
.subtext-left[data-v-
|
|
295
|
+
.subtext-left[data-v-733ae57a] {
|
|
296
296
|
/* Take all remaining inline space so the right block is pushed to the end. */
|
|
297
297
|
flex: 1 1 0;
|
|
298
298
|
min-width: 0;
|
|
299
299
|
}
|
|
300
|
-
.subtext-right[data-v-
|
|
300
|
+
.subtext-right[data-v-733ae57a] {
|
|
301
301
|
/*
|
|
302
302
|
* On row 1 (alongside .subtext-left) the right block stays at its
|
|
303
303
|
* intrinsic width — line-breaking in flex layout is decided from
|
package/dist/ai.js
CHANGED
|
@@ -671,7 +671,7 @@ var KdsPromptInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
671
671
|
]);
|
|
672
672
|
};
|
|
673
673
|
}
|
|
674
|
-
}), [["__scopeId", "data-v-
|
|
674
|
+
}), [["__scopeId", "data-v-733ae57a"]]);
|
|
675
675
|
//#endregion
|
|
676
676
|
//#region src/ai/KdsPromptInput/enums.ts
|
|
677
677
|
var kdsPromptInputStatusType = {
|
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 {\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 maxlength = undefined,\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 :maxlength=\"maxlength\"\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 maxlength = undefined,\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 :maxlength=\"maxlength\"\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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"}
|
|
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 maxlength = undefined,\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 :maxlength=\"maxlength\"\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 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\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n field-sizing: content;\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 maxlength = undefined,\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 :maxlength=\"maxlength\"\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 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\n /* Cap textarea growth at 4 lines, scroll beyond that. */\n field-sizing: content;\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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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as promise_exports, s as getFileMimeType, u as sleep } from "./dist-CGsTYtOF.js";
|
|
2
2
|
import { ofetch } from "ofetch";
|
|
3
3
|
//#region ../../node_modules/.pnpm/es-toolkit@1.49.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
|
|
4
4
|
/**
|
|
@@ -733,4 +733,4 @@ function createUploadStatusPoller(uploadClient, options) {
|
|
|
733
733
|
//#endregion
|
|
734
734
|
export { createUploadManager as a, API_BASE_URL as c, createStreamingUploadManager as i, createUploadClient as n, createHttpClient as o, UploadStates as r, defaultConfig as s, createUploadStatusPoller as t };
|
|
735
735
|
|
|
736
|
-
//# sourceMappingURL=core-
|
|
736
|
+
//# sourceMappingURL=core-CkhE7SJE.js.map
|