@knime/product-features 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/ai.css +151 -52
- package/dist/ai.js +480 -32
- package/dist/ai.js.map +1 -1
- package/dist/dataTransfer.js +2 -2
- package/dist/{dist-BrNh9vG-.js → dist-D6REgugT.js} +104 -15
- package/dist/dist-D6REgugT.js.map +1 -0
- package/dist/{rfcErrors-Dw7NLSDp.js → rfcErrors-DZsECPcA.js} +2 -2
- package/dist/{rfcErrors-Dw7NLSDp.js.map → rfcErrors-DZsECPcA.js.map} +1 -1
- package/dist/rfcErrors.js +1 -1
- package/dist/src/ai/KdsPromptInput/AttachableSurface.vue.d.ts +29 -0
- package/dist/src/ai/KdsPromptInput/AttachableSurface.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/InlineStatusBanner.vue.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts +3 -1
- package/dist/src/ai/KdsPromptInput/KdsPromptInput.vue.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/enums.d.ts +5 -0
- package/dist/src/ai/KdsPromptInput/enums.d.ts.map +1 -1
- package/dist/src/ai/KdsPromptInput/inquiries/InquiryStack.vue.d.ts +27 -0
- package/dist/src/ai/KdsPromptInput/inquiries/InquiryStack.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/PermissionCard.vue.d.ts +12 -0
- package/dist/src/ai/KdsPromptInput/inquiries/PermissionCard.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/QuestionCard.vue.d.ts +32 -0
- package/dist/src/ai/KdsPromptInput/inquiries/QuestionCard.vue.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/index.d.ts +2 -0
- package/dist/src/ai/KdsPromptInput/inquiries/index.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/usePermissionPhase.d.ts +20 -0
- package/dist/src/ai/KdsPromptInput/inquiries/usePermissionPhase.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/inquiries/useQuestionPhase.d.ts +26 -0
- package/dist/src/ai/KdsPromptInput/inquiries/useQuestionPhase.d.ts.map +1 -0
- package/dist/src/ai/KdsPromptInput/types.d.ts +96 -1
- package/dist/src/ai/KdsPromptInput/types.d.ts.map +1 -1
- package/dist/src/versions/VersionHistory/VersionHistory.vue.d.ts +2 -2
- package/dist/toasts.js +1 -1
- package/dist/versions.js +1 -1
- package/package.json +7 -4
- package/dist/dist-BrNh9vG-.js.map +0 -1
package/dist/ai.js
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import { t as _plugin_vue_export_helper_default } from "./_plugin-vue_export-helper-BOaGB7Aw.js";
|
|
2
|
-
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, mergeModels, normalizeClass, openBlock, toDisplayString, unref, useModel, useTemplateRef, vModelText, watch, withDirectives, withModifiers } from "vue";
|
|
2
|
+
import { KeepAlive, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, onMounted, openBlock, reactive, ref, renderSlot, toDisplayString, unref, useModel, useTemplateRef, vModelText, watch, withCtx, withDirectives, withModifiers } from "vue";
|
|
3
3
|
import { useResizeObserver } from "@vueuse/core";
|
|
4
|
-
import { KdsButton, KdsIcon, KdsLink, KdsProgressBar } from "@knime/kds-components";
|
|
5
|
-
import './ai.css';//#
|
|
6
|
-
|
|
7
|
-
var
|
|
4
|
+
import { KdsButton, KdsCheckbox, KdsCheckboxGroup, KdsIcon, KdsLink, KdsProgressBar, KdsRadioButtonGroup, KdsTextInput } from "@knime/kds-components";
|
|
5
|
+
import './ai.css';//#endregion
|
|
6
|
+
//#region src/ai/KdsPromptInput/AttachableSurface.vue
|
|
7
|
+
var AttachableSurface_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
8
|
+
__name: "AttachableSurface",
|
|
9
|
+
props: { tone: { default: "neutral" } },
|
|
10
|
+
setup(__props) {
|
|
11
|
+
/**
|
|
12
|
+
* Wrapper for a surface that attaches directly above the prompt input
|
|
13
|
+
* (or above another attachable surface).
|
|
14
|
+
*
|
|
15
|
+
* Used by the status banner and inquiry cards.
|
|
16
|
+
*/
|
|
17
|
+
return (_ctx, _cache) => {
|
|
18
|
+
return openBlock(), createElementBlock("div", { class: normalizeClass(["attachable-surface", __props.tone]) }, [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 2);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}), [["__scopeId", "data-v-4cf4affc"]]);
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/ai/KdsPromptInput/InlineStatusBanner.vue?vue&type=script&setup=true&lang.ts
|
|
24
|
+
var _hoisted_1$4 = ["role"];
|
|
25
|
+
var _hoisted_2$3 = {
|
|
8
26
|
class: "text",
|
|
9
27
|
tabindex: "0"
|
|
10
28
|
};
|
|
@@ -16,29 +34,443 @@ var InlineStatusBanner_default = /*#__PURE__*/ _plugin_vue_export_helper_default
|
|
|
16
34
|
emits: ["buttonClick"],
|
|
17
35
|
setup(__props, { emit: __emit }) {
|
|
18
36
|
const emit = __emit;
|
|
37
|
+
return (_ctx, _cache) => {
|
|
38
|
+
return openBlock(), createBlock(AttachableSurface_default, { tone: __props.status.type === "error" ? "danger" : "info" }, {
|
|
39
|
+
default: withCtx(() => [createElementVNode("div", {
|
|
40
|
+
class: normalizeClass(["inline-status-banner", __props.status.type]),
|
|
41
|
+
role: __props.status.type === "error" ? "alert" : "status"
|
|
42
|
+
}, [
|
|
43
|
+
createVNode(unref(KdsIcon), {
|
|
44
|
+
name: __props.status.type === "error" ? "circle-error" : "circle-info",
|
|
45
|
+
size: "small",
|
|
46
|
+
class: "icon"
|
|
47
|
+
}, null, 8, ["name"]),
|
|
48
|
+
createElementVNode("span", _hoisted_2$3, toDisplayString(__props.status.message), 1),
|
|
49
|
+
__props.status.buttonLabel ? (openBlock(), createBlock(unref(KdsButton), {
|
|
50
|
+
key: 0,
|
|
51
|
+
class: "action",
|
|
52
|
+
size: "small",
|
|
53
|
+
variant: "filled",
|
|
54
|
+
label: __props.status.buttonLabel,
|
|
55
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("buttonClick"))
|
|
56
|
+
}, null, 8, ["label"])) : createCommentVNode("", true)
|
|
57
|
+
], 10, _hoisted_1$4)]),
|
|
58
|
+
_: 1
|
|
59
|
+
}, 8, ["tone"]);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}), [["__scopeId", "data-v-df3926bc"]]);
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/ai/KdsPromptInput/inquiries/PermissionCard.vue?vue&type=script&setup=true&lang.ts
|
|
65
|
+
var _hoisted_1$3 = { class: "title" };
|
|
66
|
+
var _hoisted_2$2 = { class: "content" };
|
|
67
|
+
var _hoisted_3$2 = {
|
|
68
|
+
key: 0,
|
|
69
|
+
class: "description"
|
|
70
|
+
};
|
|
71
|
+
var _hoisted_4$2 = { class: "action-buttons" };
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/ai/KdsPromptInput/inquiries/PermissionCard.vue
|
|
74
|
+
var PermissionCard_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
75
|
+
__name: "PermissionCard",
|
|
76
|
+
props: { permission: {} },
|
|
77
|
+
emits: ["decide"],
|
|
78
|
+
setup(__props, { emit: __emit }) {
|
|
79
|
+
const emit = __emit;
|
|
80
|
+
const isRememberChoiceChecked = ref(false);
|
|
81
|
+
const decide = (decision) => {
|
|
82
|
+
emit("decide", {
|
|
83
|
+
decision,
|
|
84
|
+
...__props.permission.remember && { remember: isRememberChoiceChecked.value }
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
const allowButtonRef = useTemplateRef("allowButton");
|
|
88
|
+
onMounted(() => {
|
|
89
|
+
(allowButtonRef.value?.$el)?.focus();
|
|
90
|
+
});
|
|
91
|
+
const handleKeydown = (event) => {
|
|
92
|
+
if (event.key === "Enter") {
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
decide("allow");
|
|
95
|
+
} else if (event.key === "Escape") {
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
decide("deny");
|
|
98
|
+
}
|
|
99
|
+
};
|
|
19
100
|
return (_ctx, _cache) => {
|
|
20
101
|
return openBlock(), createElementBlock("div", {
|
|
21
|
-
class:
|
|
22
|
-
|
|
23
|
-
}, [
|
|
24
|
-
|
|
25
|
-
|
|
102
|
+
class: "inquiry-card",
|
|
103
|
+
onKeydown: handleKeydown
|
|
104
|
+
}, [createElementVNode("span", _hoisted_1$3, toDisplayString(__props.permission.headline), 1), createElementVNode("div", _hoisted_2$2, [
|
|
105
|
+
__props.permission.description ? (openBlock(), createElementBlock("p", _hoisted_3$2, toDisplayString(__props.permission.description), 1)) : createCommentVNode("", true),
|
|
106
|
+
__props.permission.remember ? (openBlock(), createBlock(unref(KdsCheckbox), {
|
|
107
|
+
key: 1,
|
|
108
|
+
modelValue: isRememberChoiceChecked.value,
|
|
109
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRememberChoiceChecked.value = $event),
|
|
110
|
+
label: __props.permission.remember.label,
|
|
111
|
+
"sub-text": __props.permission.remember.subtext
|
|
112
|
+
}, null, 8, [
|
|
113
|
+
"modelValue",
|
|
114
|
+
"label",
|
|
115
|
+
"sub-text"
|
|
116
|
+
])) : createCommentVNode("", true),
|
|
117
|
+
createElementVNode("div", _hoisted_4$2, [createVNode(unref(KdsButton), {
|
|
118
|
+
variant: "transparent",
|
|
119
|
+
size: "small",
|
|
120
|
+
label: "Deny",
|
|
121
|
+
onClick: _cache[1] || (_cache[1] = ($event) => decide("deny"))
|
|
122
|
+
}), createVNode(unref(KdsButton), {
|
|
123
|
+
ref: "allowButton",
|
|
124
|
+
variant: "outlined",
|
|
26
125
|
size: "small",
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
126
|
+
label: "Allow",
|
|
127
|
+
onClick: _cache[2] || (_cache[2] = ($event) => decide("allow"))
|
|
128
|
+
}, null, 512)])
|
|
129
|
+
])], 32);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}), [["__scopeId", "data-v-495449c9"]]);
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/ai/KdsPromptInput/inquiries/QuestionCard.vue?vue&type=script&setup=true&lang.ts
|
|
135
|
+
var _hoisted_1$2 = { class: "title-row" };
|
|
136
|
+
var _hoisted_2$1 = { class: "title" };
|
|
137
|
+
var _hoisted_3$1 = {
|
|
138
|
+
key: 0,
|
|
139
|
+
class: "skipped-tag"
|
|
140
|
+
};
|
|
141
|
+
var _hoisted_4$1 = {
|
|
142
|
+
key: 0,
|
|
143
|
+
class: "pagination"
|
|
144
|
+
};
|
|
145
|
+
var _hoisted_5$1 = { class: "content" };
|
|
146
|
+
var _hoisted_6$1 = {
|
|
147
|
+
key: 0,
|
|
148
|
+
class: "description"
|
|
149
|
+
};
|
|
150
|
+
var _hoisted_7$1 = { class: "action-buttons" };
|
|
151
|
+
var _hoisted_8$1 = { class: "nav-secondary" };
|
|
152
|
+
var _hoisted_9 = { class: "nav-primary" };
|
|
153
|
+
/**
|
|
154
|
+
* A single pending question, shown one at a time and paginated by the parent
|
|
155
|
+
* stack.
|
|
156
|
+
*/
|
|
157
|
+
var FREEFORM_OPTION_ID = "__kds-prompt-input-freeform__";
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/ai/KdsPromptInput/inquiries/QuestionCard.vue
|
|
160
|
+
var QuestionCard_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
161
|
+
__name: "QuestionCard",
|
|
162
|
+
props: {
|
|
163
|
+
question: {},
|
|
164
|
+
pageIndex: {},
|
|
165
|
+
pageCount: {},
|
|
166
|
+
othersAnswered: { type: Boolean }
|
|
167
|
+
},
|
|
168
|
+
emits: [
|
|
169
|
+
"back",
|
|
170
|
+
"skip",
|
|
171
|
+
"next",
|
|
172
|
+
"submit"
|
|
173
|
+
],
|
|
174
|
+
setup(__props, { emit: __emit }) {
|
|
175
|
+
const props = __props;
|
|
176
|
+
const emit = __emit;
|
|
177
|
+
/**
|
|
178
|
+
* Synthetic id of the trailing "Type your own answer" option. Prefixed so it
|
|
179
|
+
* cannot collide with consumer-provided option ids.
|
|
180
|
+
*/
|
|
181
|
+
const hasOptions = computed(() => (props.question.options ?? []).length > 0);
|
|
182
|
+
const optionValues = computed(() => [...(props.question.options ?? []).map((option) => ({
|
|
183
|
+
id: option.id,
|
|
184
|
+
text: option.label,
|
|
185
|
+
helperText: option.subText
|
|
186
|
+
})), {
|
|
187
|
+
id: FREEFORM_OPTION_ID,
|
|
188
|
+
text: "Type your own answer"
|
|
189
|
+
}]);
|
|
190
|
+
const selectedSingleId = ref("");
|
|
191
|
+
const selectedMultiIds = ref([]);
|
|
192
|
+
const freeformText = ref("");
|
|
193
|
+
const isSkipped = ref(false);
|
|
194
|
+
const selectedIds = computed(() => {
|
|
195
|
+
if (props.question.multiple) return selectedMultiIds.value;
|
|
196
|
+
return selectedSingleId.value ? [selectedSingleId.value] : [];
|
|
197
|
+
});
|
|
198
|
+
const isFreeformActive = computed(() => !hasOptions.value || selectedIds.value.includes(FREEFORM_OPTION_ID));
|
|
199
|
+
const selectedOptionIds = computed(() => selectedIds.value.filter((id) => id !== FREEFORM_OPTION_ID));
|
|
200
|
+
const canAnswer = computed(() => selectedOptionIds.value.length > 0 || isFreeformActive.value && freeformText.value.trim().length > 0);
|
|
201
|
+
watch(canAnswer, (answerable) => {
|
|
202
|
+
if (answerable) isSkipped.value = false;
|
|
203
|
+
});
|
|
204
|
+
const isLast = computed(() => props.pageIndex === props.pageCount - 1);
|
|
205
|
+
const showPagination = computed(() => props.pageCount > 1);
|
|
206
|
+
const showBack = computed(() => props.pageIndex > 0);
|
|
207
|
+
const primaryLabel = computed(() => isLast.value ? "Submit" : "Next");
|
|
208
|
+
const currentResolved = computed(() => canAnswer.value || isSkipped.value);
|
|
209
|
+
const canProceed = computed(() => isLast.value ? props.othersAnswered && currentResolved.value : currentResolved.value);
|
|
210
|
+
const buildAnswer = () => {
|
|
211
|
+
if (isSkipped.value) return {
|
|
212
|
+
selectedOptionIds: [],
|
|
213
|
+
skipped: true
|
|
214
|
+
};
|
|
215
|
+
const freeform = isFreeformActive.value ? freeformText.value.trim() : "";
|
|
216
|
+
return {
|
|
217
|
+
selectedOptionIds: selectedOptionIds.value,
|
|
218
|
+
...freeform && { freeformInput: freeform }
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
const proceed = () => {
|
|
222
|
+
if (!canProceed.value) return;
|
|
223
|
+
if (isLast.value) emit("submit", buildAnswer());
|
|
224
|
+
else emit("next", buildAnswer());
|
|
225
|
+
};
|
|
226
|
+
const skip = () => {
|
|
227
|
+
selectedSingleId.value = "";
|
|
228
|
+
selectedMultiIds.value = [];
|
|
229
|
+
freeformText.value = "";
|
|
230
|
+
isSkipped.value = true;
|
|
231
|
+
emit("skip");
|
|
232
|
+
};
|
|
233
|
+
const handleKeydown = (event) => {
|
|
234
|
+
if (event.key === "Enter") {
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
proceed();
|
|
237
|
+
} else if (event.key === "Escape") {
|
|
238
|
+
event.preventDefault();
|
|
239
|
+
skip();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
return (_ctx, _cache) => {
|
|
243
|
+
return openBlock(), createElementBlock("div", {
|
|
244
|
+
class: "inquiry-card",
|
|
245
|
+
onKeydown: handleKeydown
|
|
246
|
+
}, [createElementVNode("div", _hoisted_1$2, [createElementVNode("span", _hoisted_2$1, [createTextVNode(toDisplayString(__props.question.headline), 1), isSkipped.value ? (openBlock(), createElementBlock("span", _hoisted_3$1, "(Skipped)")) : createCommentVNode("", true)]), showPagination.value ? (openBlock(), createElementBlock("span", _hoisted_4$1, toDisplayString(__props.pageIndex + 1) + " of " + toDisplayString(__props.pageCount), 1)) : createCommentVNode("", true)]), createElementVNode("div", _hoisted_5$1, [
|
|
247
|
+
__props.question.description ? (openBlock(), createElementBlock("p", _hoisted_6$1, toDisplayString(__props.question.description), 1)) : createCommentVNode("", true),
|
|
248
|
+
!__props.question.multiple && hasOptions.value ? (openBlock(), createBlock(unref(KdsRadioButtonGroup), {
|
|
249
|
+
key: 1,
|
|
250
|
+
modelValue: selectedSingleId.value,
|
|
251
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedSingleId.value = $event),
|
|
252
|
+
"possible-values": optionValues.value,
|
|
253
|
+
alignment: "vertical"
|
|
254
|
+
}, null, 8, ["modelValue", "possible-values"])) : createCommentVNode("", true),
|
|
255
|
+
__props.question.multiple && hasOptions.value ? (openBlock(), createBlock(unref(KdsCheckboxGroup), {
|
|
256
|
+
key: 2,
|
|
257
|
+
modelValue: selectedMultiIds.value,
|
|
258
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selectedMultiIds.value = $event),
|
|
259
|
+
"possible-values": optionValues.value,
|
|
260
|
+
alignment: "vertical"
|
|
261
|
+
}, null, 8, ["modelValue", "possible-values"])) : createCommentVNode("", true),
|
|
262
|
+
isFreeformActive.value ? (openBlock(), createBlock(unref(KdsTextInput), {
|
|
263
|
+
key: 3,
|
|
264
|
+
modelValue: freeformText.value,
|
|
265
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => freeformText.value = $event),
|
|
266
|
+
ariaLabel: "Type your own answer",
|
|
267
|
+
placeholder: "Type your own answer..."
|
|
268
|
+
}, null, 8, ["modelValue"])) : createCommentVNode("", true),
|
|
269
|
+
createElementVNode("div", _hoisted_7$1, [createElementVNode("div", _hoisted_8$1, [showBack.value ? (openBlock(), createBlock(unref(KdsButton), {
|
|
31
270
|
key: 0,
|
|
32
|
-
|
|
271
|
+
variant: "transparent",
|
|
272
|
+
size: "small",
|
|
273
|
+
label: "Back",
|
|
274
|
+
onClick: _cache[3] || (_cache[3] = ($event) => emit("back"))
|
|
275
|
+
})) : createCommentVNode("", true)]), createElementVNode("div", _hoisted_9, [createVNode(unref(KdsButton), {
|
|
276
|
+
variant: "transparent",
|
|
33
277
|
size: "small",
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
278
|
+
label: "Skip",
|
|
279
|
+
onClick: skip
|
|
280
|
+
}), createVNode(unref(KdsButton), {
|
|
281
|
+
variant: "outlined",
|
|
282
|
+
size: "small",
|
|
283
|
+
label: primaryLabel.value,
|
|
284
|
+
disabled: !canProceed.value,
|
|
285
|
+
onClick: proceed
|
|
286
|
+
}, null, 8, ["label", "disabled"])])])
|
|
287
|
+
])], 32);
|
|
39
288
|
};
|
|
40
289
|
}
|
|
41
|
-
}), [["__scopeId", "data-v-
|
|
290
|
+
}), [["__scopeId", "data-v-3293effe"]]);
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/ai/KdsPromptInput/inquiries/usePermissionPhase.ts
|
|
293
|
+
/**
|
|
294
|
+
* The permission phase: pending permissions resolved like a stack, with no
|
|
295
|
+
* pager. The current permission is always the first still-undecided one;
|
|
296
|
+
* deciding it pops it off and reveals the next. Buffered decisions persist
|
|
297
|
+
* until {@link reset} so the parent can gather them into a single `respond`
|
|
298
|
+
* once every phase is complete.
|
|
299
|
+
*
|
|
300
|
+
* @param permissions Getter for the pending permissions (kept reactive).
|
|
301
|
+
*/
|
|
302
|
+
function usePermissionPhase(permissions) {
|
|
303
|
+
const decisions = reactive(/* @__PURE__ */ new Map());
|
|
304
|
+
/** The first permission not yet decided, or `undefined` once none remain. */
|
|
305
|
+
const current = computed(() => permissions().find((permission) => !decisions.has(permission.id)));
|
|
306
|
+
const isComplete = computed(() => permissions().every((permission) => decisions.has(permission.id)));
|
|
307
|
+
/**
|
|
308
|
+
* Record a decision for `permission`. Returns `false` (a no-op) when the
|
|
309
|
+
* permission was already decided — e.g. a double click landing before the
|
|
310
|
+
* stack pops the card off — so the caller can avoid emitting `respond` twice.
|
|
311
|
+
*
|
|
312
|
+
* A remembered decision also resolves every other undecided permission of the
|
|
313
|
+
* same `remember.group` (e.g. repeated calls of one tool), so the user is not
|
|
314
|
+
* asked again for a choice they just saved. Those never surface — the group
|
|
315
|
+
* is popped silently.
|
|
316
|
+
*/
|
|
317
|
+
const decide = (permission, value) => {
|
|
318
|
+
if (decisions.has(permission.id)) return false;
|
|
319
|
+
decisions.set(permission.id, value);
|
|
320
|
+
const group = permission.remember?.group;
|
|
321
|
+
if (group && value.remember) {
|
|
322
|
+
for (const other of permissions()) if (other.remember?.group === group && !decisions.has(other.id)) decisions.set(other.id, value);
|
|
323
|
+
}
|
|
324
|
+
return true;
|
|
325
|
+
};
|
|
326
|
+
/** Buffered decisions in prop order, ready for the `respond` payload. */
|
|
327
|
+
const toOrderedDecisions = () => permissions().map((permission) => ({
|
|
328
|
+
id: permission.id,
|
|
329
|
+
...decisions.get(permission.id)
|
|
330
|
+
}));
|
|
331
|
+
const reset = () => decisions.clear();
|
|
332
|
+
return {
|
|
333
|
+
current,
|
|
334
|
+
isComplete,
|
|
335
|
+
decide,
|
|
336
|
+
toOrderedDecisions,
|
|
337
|
+
reset
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/ai/KdsPromptInput/inquiries/useQuestionPhase.ts
|
|
342
|
+
/**
|
|
343
|
+
* The question phase: pending questions paginated one at a time. The user moves
|
|
344
|
+
* forward with {@link answer} + {@link advance} (or {@link skip}) and back with
|
|
345
|
+
* {@link back}, changing earlier selections freely. Buffered answers persist
|
|
346
|
+
* until {@link reset} so the parent can gather them into a single `respond`
|
|
347
|
+
* once every phase is complete.
|
|
348
|
+
*
|
|
349
|
+
* @param questions Getter for the pending questions (kept reactive).
|
|
350
|
+
*/
|
|
351
|
+
function useQuestionPhase(questions) {
|
|
352
|
+
const answers = reactive(/* @__PURE__ */ new Map());
|
|
353
|
+
const index = ref(0);
|
|
354
|
+
const current = computed(() => questions()[index.value]);
|
|
355
|
+
const pageCount = computed(() => questions().length);
|
|
356
|
+
const isComplete = computed(() => questions().every((question) => answers.has(question.id)));
|
|
357
|
+
/**
|
|
358
|
+
* True when every question *other than the current one* has been resolved.
|
|
359
|
+
* The current card supplies its own answer live, so together they decide
|
|
360
|
+
* whether "Submit" is enabled on the last page.
|
|
361
|
+
*/
|
|
362
|
+
const othersAnswered = computed(() => questions().every((question, i) => i === index.value || answers.has(question.id)));
|
|
363
|
+
const advance = () => {
|
|
364
|
+
index.value = Math.min(index.value + 1, questions().length - 1);
|
|
365
|
+
};
|
|
366
|
+
const back = () => {
|
|
367
|
+
if (index.value > 0) index.value -= 1;
|
|
368
|
+
};
|
|
369
|
+
const answer = (question, value) => {
|
|
370
|
+
answers.set(question.id, value);
|
|
371
|
+
};
|
|
372
|
+
const skip = (question) => {
|
|
373
|
+
answers.set(question.id, {
|
|
374
|
+
selectedOptionIds: [],
|
|
375
|
+
skipped: true
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
/** Buffered answers in prop order, ready for the `respond` payload. */
|
|
379
|
+
const toOrderedAnswers = () => questions().map((question) => ({
|
|
380
|
+
id: question.id,
|
|
381
|
+
...answers.get(question.id)
|
|
382
|
+
}));
|
|
383
|
+
const reset = () => {
|
|
384
|
+
answers.clear();
|
|
385
|
+
index.value = 0;
|
|
386
|
+
};
|
|
387
|
+
return {
|
|
388
|
+
current,
|
|
389
|
+
index,
|
|
390
|
+
pageCount,
|
|
391
|
+
isComplete,
|
|
392
|
+
othersAnswered,
|
|
393
|
+
advance,
|
|
394
|
+
back,
|
|
395
|
+
answer,
|
|
396
|
+
skip,
|
|
397
|
+
toOrderedAnswers,
|
|
398
|
+
reset
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/ai/KdsPromptInput/inquiries/InquiryStack.vue?vue&type=script&setup=true&lang.ts
|
|
403
|
+
var _hoisted_1$1 = { class: "inquiry-stack" };
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/ai/KdsPromptInput/inquiries/InquiryStack.vue
|
|
406
|
+
var InquiryStack_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
407
|
+
__name: "InquiryStack",
|
|
408
|
+
props: {
|
|
409
|
+
permissions: {},
|
|
410
|
+
questions: {}
|
|
411
|
+
},
|
|
412
|
+
emits: ["respond"],
|
|
413
|
+
setup(__props, { emit: __emit }) {
|
|
414
|
+
const emit = __emit;
|
|
415
|
+
const { current: currentPermission, isComplete: permissionsComplete, decide: decidePermission, toOrderedDecisions, reset: resetPermissions } = usePermissionPhase(() => __props.permissions);
|
|
416
|
+
const { current: currentQuestion, index: questionIndex, pageCount: questionCount, isComplete: questionsComplete, othersAnswered, advance: advanceQuestion, back: goBack, answer: recordAnswer, skip: skipQuestion, toOrderedAnswers, reset: resetQuestions } = useQuestionPhase(() => __props.questions);
|
|
417
|
+
watch(() => [...__props.permissions, ...__props.questions].map((inquiry) => inquiry.id).join(","), () => {
|
|
418
|
+
resetPermissions();
|
|
419
|
+
resetQuestions();
|
|
420
|
+
});
|
|
421
|
+
const respondIfComplete = () => {
|
|
422
|
+
if (!permissionsComplete.value || !questionsComplete.value) return;
|
|
423
|
+
emit("respond", {
|
|
424
|
+
permissions: toOrderedDecisions(),
|
|
425
|
+
questions: toOrderedAnswers()
|
|
426
|
+
});
|
|
427
|
+
};
|
|
428
|
+
const onDecidePermission = (permission, decision) => {
|
|
429
|
+
if (decidePermission(permission, decision)) respondIfComplete();
|
|
430
|
+
};
|
|
431
|
+
const onQuestionNext = (question, answer) => {
|
|
432
|
+
recordAnswer(question, answer);
|
|
433
|
+
advanceQuestion();
|
|
434
|
+
};
|
|
435
|
+
const onQuestionSkip = (question) => {
|
|
436
|
+
skipQuestion(question);
|
|
437
|
+
advanceQuestion();
|
|
438
|
+
};
|
|
439
|
+
const onQuestionSubmit = (question, answer) => {
|
|
440
|
+
recordAnswer(question, answer);
|
|
441
|
+
respondIfComplete();
|
|
442
|
+
};
|
|
443
|
+
return (_ctx, _cache) => {
|
|
444
|
+
return unref(currentPermission) || unref(currentQuestion) ? (openBlock(), createBlock(AttachableSurface_default, {
|
|
445
|
+
key: 0,
|
|
446
|
+
tone: "neutral"
|
|
447
|
+
}, {
|
|
448
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_1$1, [unref(currentPermission) ? (openBlock(), createBlock(PermissionCard_default, {
|
|
449
|
+
key: unref(currentPermission).id,
|
|
450
|
+
permission: unref(currentPermission),
|
|
451
|
+
onDecide: _cache[0] || (_cache[0] = (decision) => onDecidePermission(unref(currentPermission), decision))
|
|
452
|
+
}, null, 8, ["permission"])) : unref(currentQuestion) ? (openBlock(), createBlock(KeepAlive, { key: 1 }, [(openBlock(), createBlock(QuestionCard_default, {
|
|
453
|
+
key: unref(currentQuestion).id,
|
|
454
|
+
question: unref(currentQuestion),
|
|
455
|
+
"page-index": unref(questionIndex),
|
|
456
|
+
"page-count": unref(questionCount),
|
|
457
|
+
"others-answered": unref(othersAnswered),
|
|
458
|
+
onBack: unref(goBack),
|
|
459
|
+
onSkip: _cache[1] || (_cache[1] = ($event) => onQuestionSkip(unref(currentQuestion))),
|
|
460
|
+
onNext: _cache[2] || (_cache[2] = (answer) => onQuestionNext(unref(currentQuestion), answer)),
|
|
461
|
+
onSubmit: _cache[3] || (_cache[3] = (answer) => onQuestionSubmit(unref(currentQuestion), answer))
|
|
462
|
+
}, null, 8, [
|
|
463
|
+
"question",
|
|
464
|
+
"page-index",
|
|
465
|
+
"page-count",
|
|
466
|
+
"others-answered",
|
|
467
|
+
"onBack"
|
|
468
|
+
]))], 1024)) : createCommentVNode("", true)])]),
|
|
469
|
+
_: 1
|
|
470
|
+
})) : createCommentVNode("", true);
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
}), [["__scopeId", "data-v-fa0d07a4"]]);
|
|
42
474
|
//#endregion
|
|
43
475
|
//#region src/ai/KdsPromptInput/KdsPromptInput.vue?vue&type=script&setup=true&lang.ts
|
|
44
476
|
var _hoisted_1 = { class: "prompt-input" };
|
|
@@ -50,7 +482,7 @@ var _hoisted_3 = [
|
|
|
50
482
|
];
|
|
51
483
|
var _hoisted_4 = { class: "action" };
|
|
52
484
|
var _hoisted_5 = {
|
|
53
|
-
key:
|
|
485
|
+
key: 2,
|
|
54
486
|
class: "subtext"
|
|
55
487
|
};
|
|
56
488
|
var _hoisted_6 = {
|
|
@@ -80,7 +512,9 @@ var KdsPromptInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
80
512
|
ariaLabel: { default: "Prompt" },
|
|
81
513
|
leftSubtext: { default: () => void 0 },
|
|
82
514
|
rightSubtext: { default: () => void 0 },
|
|
83
|
-
link: { default: () => void 0 }
|
|
515
|
+
link: { default: () => void 0 },
|
|
516
|
+
permissions: { default: () => [] },
|
|
517
|
+
questions: { default: () => [] }
|
|
84
518
|
}, {
|
|
85
519
|
"modelValue": { default: "" },
|
|
86
520
|
"modelModifiers": {}
|
|
@@ -88,13 +522,16 @@ var KdsPromptInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
88
522
|
emits: /*@__PURE__*/ mergeModels([
|
|
89
523
|
"submit",
|
|
90
524
|
"stop",
|
|
91
|
-
"statusButtonClick"
|
|
525
|
+
"statusButtonClick",
|
|
526
|
+
"respond"
|
|
92
527
|
], ["update:modelValue"]),
|
|
93
528
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
94
529
|
const emit = __emit;
|
|
95
530
|
const modelValue = useModel(__props, "modelValue");
|
|
96
531
|
const textareaRef = useTemplateRef("textareaRef");
|
|
97
|
-
const
|
|
532
|
+
const hasInquiries = computed(() => __props.permissions.length > 0 || __props.questions.length > 0);
|
|
533
|
+
const isDisabled = computed(() => __props.disabled || hasInquiries.value);
|
|
534
|
+
const canSubmit = computed(() => !isDisabled.value && !__props.loading && modelValue.value.trim().length > 0);
|
|
98
535
|
function resizeTextarea() {
|
|
99
536
|
const el = textareaRef.value;
|
|
100
537
|
if (!el) return;
|
|
@@ -126,9 +563,15 @@ var KdsPromptInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
126
563
|
status: __props.status,
|
|
127
564
|
onButtonClick: _cache[0] || (_cache[0] = ($event) => emit("statusButtonClick"))
|
|
128
565
|
}, null, 8, ["status"])) : createCommentVNode("", true),
|
|
566
|
+
hasInquiries.value ? (openBlock(), createBlock(unref(InquiryStack_default), {
|
|
567
|
+
key: 1,
|
|
568
|
+
permissions: __props.permissions,
|
|
569
|
+
questions: __props.questions,
|
|
570
|
+
onRespond: _cache[1] || (_cache[1] = ($event) => emit("respond", $event))
|
|
571
|
+
}, null, 8, ["permissions", "questions"])) : createCommentVNode("", true),
|
|
129
572
|
createElementVNode("div", {
|
|
130
|
-
class: normalizeClass(["input-container", { disabled:
|
|
131
|
-
onClick: _cache[
|
|
573
|
+
class: normalizeClass(["input-container", { disabled: isDisabled.value }]),
|
|
574
|
+
onClick: _cache[4] || (_cache[4] = ($event) => textareaRef.value?.focus())
|
|
132
575
|
}, [
|
|
133
576
|
__props.loading ? (openBlock(), createBlock(unref(KdsProgressBar), {
|
|
134
577
|
key: 0,
|
|
@@ -138,11 +581,11 @@ var KdsPromptInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
138
581
|
createElementVNode("div", _hoisted_2, [withDirectives(createElementVNode("textarea", {
|
|
139
582
|
ref_key: "textareaRef",
|
|
140
583
|
ref: textareaRef,
|
|
141
|
-
"onUpdate:modelValue": _cache[
|
|
584
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => modelValue.value = $event),
|
|
142
585
|
class: "text-input",
|
|
143
586
|
rows: "1",
|
|
144
587
|
placeholder: __props.placeholder,
|
|
145
|
-
disabled:
|
|
588
|
+
disabled: isDisabled.value,
|
|
146
589
|
"aria-label": __props.ariaLabel,
|
|
147
590
|
onKeydown
|
|
148
591
|
}, null, 40, _hoisted_3), [[vModelText, modelValue.value]])]),
|
|
@@ -152,7 +595,7 @@ var KdsPromptInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
152
595
|
"leading-icon": "stop",
|
|
153
596
|
size: "medium",
|
|
154
597
|
ariaLabel: "Stop",
|
|
155
|
-
onClick: _cache[
|
|
598
|
+
onClick: _cache[3] || (_cache[3] = withModifiers(($event) => emit("stop"), ["stop"]))
|
|
156
599
|
})) : (openBlock(), createBlock(unref(KdsButton), {
|
|
157
600
|
key: 1,
|
|
158
601
|
variant: "filled",
|
|
@@ -172,7 +615,7 @@ var KdsPromptInput_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
172
615
|
]);
|
|
173
616
|
};
|
|
174
617
|
}
|
|
175
|
-
}), [["__scopeId", "data-v-
|
|
618
|
+
}), [["__scopeId", "data-v-92a6ac4b"]]);
|
|
176
619
|
//#endregion
|
|
177
620
|
//#region src/ai/KdsPromptInput/enums.ts
|
|
178
621
|
var kdsPromptInputStatusType = {
|
|
@@ -180,7 +623,12 @@ var kdsPromptInputStatusType = {
|
|
|
180
623
|
NOTE: "note"
|
|
181
624
|
};
|
|
182
625
|
var kdsPromptInputStatusTypes = Object.values(kdsPromptInputStatusType);
|
|
626
|
+
var kdsPromptInputPermissionDecision = {
|
|
627
|
+
ALLOW: "allow",
|
|
628
|
+
DENY: "deny"
|
|
629
|
+
};
|
|
630
|
+
var kdsPromptInputPermissionDecisions = Object.values(kdsPromptInputPermissionDecision);
|
|
183
631
|
//#endregion
|
|
184
|
-
export { KdsPromptInput_default as KdsPromptInput, kdsPromptInputStatusType, kdsPromptInputStatusTypes };
|
|
632
|
+
export { KdsPromptInput_default as KdsPromptInput, kdsPromptInputPermissionDecision, kdsPromptInputPermissionDecisions, kdsPromptInputStatusType, kdsPromptInputStatusTypes };
|
|
185
633
|
|
|
186
634
|
//# sourceMappingURL=ai.js.map
|