@8wave/ai-elements 0.98.0 → 0.99.0-beta.1
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/README.md +24 -5
- package/dist/_chunks/{PkToolShowForm-BNV4Ja6g.js → PkToolShowForm-dFuoqTPs.js} +84 -80
- package/dist/_chunks/PkToolShowForm-dFuoqTPs.js.map +1 -0
- package/dist/ai-elements.es.js +3153 -2982
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotStepToolDetail.js +1 -1
- package/dist-vue/PkChatbotSteps.js +1 -1
- package/dist-vue/PkChatbotViewChat.js +1 -1
- package/dist-vue/PkChatbotViewConversations.js +1 -1
- package/dist-vue/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/{PkChatbot-DHkhT0Jf.js → PkChatbot-BlG1WlAO.js} +7 -7
- package/dist-vue/_chunks/{PkChatbot-DHkhT0Jf.js.map → PkChatbot-BlG1WlAO.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotMessages-BrufA7o1.js +752 -0
- package/dist-vue/_chunks/PkChatbotMessages-BrufA7o1.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-ldThv0t5.js → PkChatbotStepToolDetail-BVI72gyx.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotStepToolDetail-ldThv0t5.js.map → PkChatbotStepToolDetail-BVI72gyx.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotSteps-Dv9NkYER.js +172 -0
- package/dist-vue/_chunks/{PkChatbotSteps-BpqdFhsG.js.map → PkChatbotSteps-Dv9NkYER.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-C08TsXiL.js → PkChatbotViewChat-CAqvbbfN.js} +66 -66
- package/dist-vue/_chunks/PkChatbotViewChat-CAqvbbfN.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewConversations-BQZi5kSC.js → PkChatbotViewConversations-Cq41H-tc.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewConversations-BQZi5kSC.js.map → PkChatbotViewConversations-Cq41H-tc.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewFiles-TLKE_tRw.js → PkChatbotViewFiles-DNuzmBJc.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewFiles-TLKE_tRw.js.map → PkChatbotViewFiles-DNuzmBJc.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-B6ANZY96.js → PkChatbotViewProfile-C0BXIY2N.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-B6ANZY96.js.map → PkChatbotViewProfile-C0BXIY2N.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowForm-C-mWyeka.js → PkToolShowForm-CQuX08lk.js} +62 -58
- package/dist-vue/_chunks/PkToolShowForm-CQuX08lk.js.map +1 -0
- package/dist-vue/_chunks/{createChatbotApiClient-BOBdBxmf.js → createChatbotApiClient-BMZltBLh.js} +367 -363
- package/dist-vue/_chunks/createChatbotApiClient-BMZltBLh.js.map +1 -0
- package/dist-vue/_chunks/{useChatbotStore-BJ-EDmND.js → useChatbotStore-DZiorgLW.js} +113 -103
- package/dist-vue/_chunks/{useChatbotStore-BJ-EDmND.js.map → useChatbotStore-DZiorgLW.js.map} +1 -1
- package/dist-vue/_chunks/{utils-BMa8jwJS.js → utils-Cru8TJxe.js} +48 -30
- package/dist-vue/_chunks/utils-Cru8TJxe.js.map +1 -0
- package/dist-vue/api.js +1 -1
- package/dist-vue/composables.js +3 -3
- package/dist-vue/index.js +4351 -4041
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/locales.js +16 -0
- package/dist-vue/packages/components/src/PkCheckboxField.d.ts +45 -0
- package/dist-vue/packages/components/src/PkEditorRepeater.d.ts +2 -2
- package/dist-vue/packages/components/src/PkPwaInstallPrompt.d.ts +4 -0
- package/dist-vue/packages/components/src/PkPwaOfflineNotice.d.ts +4 -0
- package/dist-vue/packages/components/src/chat/PkAgentSettingsPanel.d.ts +11 -2
- package/dist-vue/packages/components/src/chat/PkChatbotMessages.d.ts +24 -2
- package/dist-vue/packages/components/src/chat/PkToolApprovalBadge.d.ts +4 -0
- package/dist-vue/packages/components/src/chat/PkToolApprovalCard.d.ts +35 -0
- package/dist-vue/packages/components/src/chat/PkToolApprovalPreviewMessage.d.ts +11 -0
- package/dist-vue/packages/components/src/chat/index.d.ts +2 -1
- package/dist-vue/packages/components/src/chat/toolApprovalPreviews.d.ts +12 -0
- package/dist-vue/packages/components/src/chat/toolComponentMap.d.ts +5 -3
- package/dist-vue/packages/components/src/chat/useChatScroll.d.ts +6 -0
- package/dist-vue/packages/components/src/chat/utils.d.ts +13 -0
- package/dist-vue/packages/components/src/index.d.ts +4 -1
- package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +34 -1
- package/dist-vue/packages/models/src/schema/Agent.d.ts +48 -0
- package/dist-vue/packages/models/src/schema/ReasoningChat.d.ts +6 -0
- package/dist-vue/packages/models/src/schema/SubAgent.d.ts +6 -0
- package/dist-vue/style.css +1 -1
- package/package.json +1 -1
- package/dist/_chunks/PkToolRequestConfirm-FQ9nDFXy.js +0 -48
- package/dist/_chunks/PkToolRequestConfirm-FQ9nDFXy.js.map +0 -1
- package/dist/_chunks/PkToolShowForm-BNV4Ja6g.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotMessages-B4qfTNOJ.js +0 -630
- package/dist-vue/_chunks/PkChatbotMessages-B4qfTNOJ.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotSteps-BpqdFhsG.js +0 -172
- package/dist-vue/_chunks/PkChatbotViewChat-C08TsXiL.js.map +0 -1
- package/dist-vue/_chunks/PkToolRequestConfirm-CQNa3seA.js +0 -48
- package/dist-vue/_chunks/PkToolRequestConfirm-CQNa3seA.js.map +0 -1
- package/dist-vue/_chunks/PkToolShowForm-C-mWyeka.js.map +0 -1
- package/dist-vue/_chunks/createChatbotApiClient-BOBdBxmf.js.map +0 -1
- package/dist-vue/_chunks/utils-BMa8jwJS.js.map +0 -1
- package/dist-vue/packages/components/src/chat/PkToolRequestConfirm.d.ts +0 -11
package/README.md
CHANGED
|
@@ -122,7 +122,23 @@ widget.setAttribute('external-token', jwt) // Single session created
|
|
|
122
122
|
|
|
123
123
|
> **Note:** The `external-token` value is validated server-side. On a 401 response the component silently re-authenticates with the active external token; if that fails it emits `external-auth-expired` and applies the fallback above. Listen for that event to hand the widget a fresh JWT via `signIn(token)`.
|
|
124
124
|
|
|
125
|
-
> **Note:** In external token mode (`external-token` set, or `await-external-token` present) the host application owns the authentication lifecycle: the widget hides its own sign-out button
|
|
125
|
+
> **Note:** In external token mode (`external-token` set, or `await-external-token` present) the host application owns the authentication lifecycle: the widget hides its own sign-out button, the profile management link in the profile panel, and the GDPR "delete my conversations" action. That action erases every conversation of the account platform-wide, well beyond the widget, so it belongs to the host application. Deleting a single conversation from the list stays available.
|
|
126
|
+
|
|
127
|
+
### Host session (same-origin embeds)
|
|
128
|
+
|
|
129
|
+
When the page already holds an Eight Wave session cookie for the backend origin, add the `use-host-session` attribute: the widget reuses that session instead of creating one of its own. No anonymous sign-in happens, requests travel with the cookie (`credentials: 'include'`) rather than a widget Bearer, and the chat history belongs to the logged-in account instead of a per-browser anonymous user.
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<floating-chat-widget
|
|
133
|
+
organization-slug="your-org-slug"
|
|
134
|
+
agent-id="your-agent-id"
|
|
135
|
+
use-host-session
|
|
136
|
+
></floating-chat-widget>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
This only works where that cookie is actually sent, so it is meant for applications authenticating against the same Eight Wave backend (the Agent Studio copilot is the reference case) and not for third-party embeds. On a site with no such session the widget stays signed out: use `external-token` instead.
|
|
140
|
+
|
|
141
|
+
The attribute is reactive, so bind it to your login state and the widget re-reads the session when it flips. As with external token mode the host owns the auth lifecycle, so sign-out, profile management and the bulk GDPR erase are hidden, and `signOut()` is a no-op: revoking the session would log the user out of the host application too.
|
|
126
142
|
|
|
127
143
|
## Attributes
|
|
128
144
|
|
|
@@ -135,6 +151,7 @@ widget.setAttribute('external-token', jwt) // Single session created
|
|
|
135
151
|
| `base-url` | `string` | ❌ | `https://ai-api.8wave.it` (build-time) | Base URL of the Eight Wave backend API. Override when self-hosting or pointing to a different environment. |
|
|
136
152
|
| `external-token` | `string` | ❌ | — | Pre-existing auth token. **Reactive** — can be set or changed at any time. When set, the widget authenticates with this token instead of anonymous sign-in. |
|
|
137
153
|
| `await-external-token` | `boolean` | ❌ | `false` | When present, the widget waits for `external-token` instead of signing in anonymously. Use when the token is fetched asynchronously after mount. |
|
|
154
|
+
| `use-host-session` | `boolean` | ❌ | `false` | Reuse the Eight Wave session cookie already present on the backend origin instead of creating a widget session. Same-origin embeds only, see Host session. |
|
|
138
155
|
| `floating-button-position` | `'left' \| 'right'` | ❌ | `'right'` | Position of the floating toggle button on the screen. |
|
|
139
156
|
| `system-theme` | `'light' \| 'dark'` | ❌ | OS `prefers-color-scheme` | Force the chat color scheme instead of following the OS preference. Reactive: update it when the host app's theme changes. |
|
|
140
157
|
| `custom-css` | `string` | ❌ | — | Raw CSS string injected into the shadow root to override default styles. |
|
|
@@ -148,6 +165,7 @@ widget.setAttribute('external-token', jwt) // Single session created
|
|
|
148
165
|
| `base-url` | `string` | ❌ | `https://ai-api.8wave.it` (build-time) | Base URL of the Eight Wave backend API. Override when self-hosting or pointing to a different environment. |
|
|
149
166
|
| `external-token` | `string` | ❌ | — | Pre-existing auth token. **Reactive** — can be set or changed at any time. When set, the widget authenticates with this token instead of anonymous sign-in. |
|
|
150
167
|
| `await-external-token` | `boolean` | ❌ | `false` | When present, the widget waits for `external-token` instead of signing in anonymously. Use when the token is fetched asynchronously after mount. |
|
|
168
|
+
| `use-host-session` | `boolean` | ❌ | `false` | Reuse the Eight Wave session cookie already present on the backend origin instead of creating a widget session. Same-origin embeds only, see Host session. |
|
|
151
169
|
| `forwarded-slots` | `string` | ❌ | — | Comma-separated slot names to forward to `PkChatbot` (e.g. `"tool-showArtifact,subagent"`). |
|
|
152
170
|
| `system-theme` | `'light' \| 'dark'` | ❌ | OS `prefers-color-scheme` | Force the chat color scheme instead of following the OS preference. Reactive: update it when the host app's theme changes. |
|
|
153
171
|
| `custom-css` | `string` | ❌ | — | Raw CSS string injected into the shadow root to override default styles. |
|
|
@@ -243,7 +261,7 @@ widget.setContext({
|
|
|
243
261
|
|
|
244
262
|
## How it works
|
|
245
263
|
|
|
246
|
-
1. On mount the component automatically signs in as an anonymous user via the Eight Wave Auth service.
|
|
264
|
+
1. On mount the component automatically signs in as an anonymous user via the Eight Wave Auth service, unless the host provides the identity (`external-token`) or lends its own session (`use-host-session`).
|
|
247
265
|
2. Once authenticated it fetches the agent configuration (name, interface colors, etc.) from the public API.
|
|
248
266
|
3. A circular floating button appears in the bottom-right corner, styled with the agent's `mainColor`.
|
|
249
267
|
4. Clicking the button toggles the chat panel open/closed.
|
|
@@ -423,6 +441,7 @@ const { client, session, isAuthenticating, token, headers, onError, userId } = u
|
|
|
423
441
|
| `basePath` | `string` | ❌ | Auth base path appended to `baseUrl`. Defaults to `auth`. |
|
|
424
442
|
| `externalToken` | `MaybeRefOrGetter<string \| undefined>` | ❌ | Pre-existing auth token. Accepts a static string, a ref, or a getter. When the value is truthy, the composable authenticates with it instead of signing in anonymously. Reactive — authentication triggers automatically when the value changes. |
|
|
425
443
|
| `awaitExternalToken` | `boolean` | ❌ | When `true`, the composable does not fall back to anonymous sign-in while `externalToken` is falsy. Authentication triggers automatically when the token value becomes available. |
|
|
444
|
+
| `useHostSession` | `MaybeRefOrGetter<boolean \| undefined>` | ❌ | When `true`, reuses the session cookie the host page already holds on the backend origin: no sign-in of any kind, requests carry the cookie instead of the widget Bearer, and the stored Bearer is never sent or overwritten. Reactive: the session is re-read when the value changes. Same-origin embeds only. |
|
|
426
445
|
| `isActive` | `MaybeRefOrGetter<boolean>` | ❌ | Reactive signal that triggers authentication when it becomes `true`. Defaults to always active. |
|
|
427
446
|
| `cookieName` | `string` | ❌ | Cookie name for session persistence. Defaults to `eight-wave-ai`. Use different names to isolate multiple sessions on the same page. |
|
|
428
447
|
| `onSignedIn` | `(detail: { userId, isAnonymous }) => void` | ❌ | Called when a session is established or the user changes (any flow). |
|
|
@@ -436,13 +455,13 @@ const { client, session, isAuthenticating, token, headers, onError, userId } = u
|
|
|
436
455
|
| `client` | `AuthClient` | The underlying Better Auth client instance. Exposes `signIn.anonymous()`, `signIn.externalToken({ token })`, and `useSession()`. |
|
|
437
456
|
| `session` | `Ref<Session>` | Reactive session state from the auth client. |
|
|
438
457
|
| `isAuthenticating`| `Ref<boolean>` | `true` while a sign-in request is in flight. |
|
|
439
|
-
| `isExternalAuth` | `ComputedRef<boolean>` | `true`
|
|
458
|
+
| `isExternalAuth` | `ComputedRef<boolean>` | `true` when the host application owns the session: external token mode (`externalToken` set or `awaitExternalToken` enabled) or `useHostSession`. The profile panel then hides sign-out, profile management and the bulk GDPR erase. |
|
|
440
459
|
| `token` | `WritableComputedRef<string>` | Reactive Bearer token, synced to the first-party cookie. |
|
|
441
|
-
| `headers` | `ComputedRef<Record<string, string> \| undefined>` | Reactive `Authorization` header derived from `token`. Pass directly to `useChatbotAgent` or `PkChatbot`. |
|
|
460
|
+
| `headers` | `ComputedRef<Record<string, string> \| undefined>` | Reactive `Authorization` header derived from `token`. Pass directly to `useChatbotAgent` or `PkChatbot`. `undefined` in host-session mode, where the cookie carries the identity. |
|
|
442
461
|
| `onError` | `(error) => void` | Pass to `PkChatbot`'s `@error` event to trigger automatic re-auth on 401. |
|
|
443
462
|
| `userId` | `ComputedRef<string \| undefined>` | Reactive user ID extracted from the session. Pass to `PkChatbot`'s `:user-id`. |
|
|
444
463
|
| `signInWithExternalToken` | `(externalToken: string) => Promise<void>` | Authenticates with an external JWT (imperative equivalent of the `externalToken` option). |
|
|
445
|
-
| `signOut` | `() => Promise<void>` | Signs out and withdraws the active external token, so no automatic re-login happens with it. |
|
|
464
|
+
| `signOut` | `() => Promise<void>` | Signs out and withdraws the active external token, so no automatic re-login happens with it. No-op in host-session mode: revoking the session would sign the user out of the host application too. |
|
|
446
465
|
|
|
447
466
|
#### End-to-end example (Vue + external JWT)
|
|
448
467
|
|
|
@@ -118,22 +118,22 @@ var ue = {
|
|
|
118
118
|
}, fe = {
|
|
119
119
|
key: 1,
|
|
120
120
|
class: "flex flex-col gap-4 p-sm"
|
|
121
|
-
},
|
|
121
|
+
}, pe = { class: "flex justify-between text-10 text-word-3" }, j = { class: "h-4 bg-surface-3 rounded-full overflow-hidden" }, M = { class: "px-sm py-16 flex flex-col gap-12" }, N = { class: "leading-normal text-12" }, P = { class: "font-semibold" }, F = { class: "text-12" }, I = { class: "font-semibold" }, L = {
|
|
122
122
|
key: 0,
|
|
123
123
|
class: "text-word-3"
|
|
124
|
-
},
|
|
124
|
+
}, R = { class: "text-12" }, z = { class: "font-semibold" }, B = {
|
|
125
125
|
key: 0,
|
|
126
126
|
class: "text-word-3"
|
|
127
|
-
},
|
|
127
|
+
}, me = {
|
|
128
128
|
key: 2,
|
|
129
129
|
class: "flex flex-col gap-6 w-full"
|
|
130
|
-
},
|
|
130
|
+
}, V = {
|
|
131
131
|
key: 0,
|
|
132
132
|
class: "px-sm pb-sm border-t border-surface-3"
|
|
133
|
-
},
|
|
133
|
+
}, he = {
|
|
134
134
|
key: 1,
|
|
135
135
|
class: "px-sm py-10 bg-surface border-t border-surface-3 flex justify-between gap-12"
|
|
136
|
-
},
|
|
136
|
+
}, ge = { key: 1 }, _e = { class: "px-sm py-12 bg-surface space-y-10" }, ve = { class: "flex flex-col gap-8" }, ye = { class: "text-12 text-word-3" }, be = { class: "text-12 text-word-2 font-semibold" }, xe = { class: "px-sm py-12 bg-surface border-t border-surface-3 flex items-center gap-10" }, Se = { class: "w-20 h-20 rounded-full border border-success text-success flex items-center justify-center" }, Ce = { class: "flex flex-col" }, we = { class: "font-semibold text-12 text-word-1 leading-tight" }, H = /* @__PURE__ */ l({
|
|
137
137
|
__name: "PkToolShowForm",
|
|
138
138
|
props: {
|
|
139
139
|
part: { type: null },
|
|
@@ -142,36 +142,36 @@ var ue = {
|
|
|
142
142
|
emits: ["select"],
|
|
143
143
|
setup(e, { emit: n }) {
|
|
144
144
|
let i = e, a = n, { t: o, d: c } = re({ useScope: "global" }), l = g(() => JSON.parse(JSON.stringify(i.part))), f = g(() => l.value.toolName === "showMultipleChoice" || l.value.type === "tool-showMultipleChoice"), _ = g(() => l.value.state === "input-available"), v = g(() => {
|
|
145
|
-
let e =
|
|
146
|
-
return e ?
|
|
147
|
-
}), D = g(() => f.value &&
|
|
145
|
+
let e = H.value;
|
|
146
|
+
return e ? k.value.length === 1 && !e.allowFreeformInput && !i.allowCustomAnswer : !1;
|
|
147
|
+
}), D = g(() => f.value && k.value.length === 1 && !H.value?.multiSelect), O = g(() => !!H.value?.options && !H.value?.multiSelect && (v.value || D.value)), k = g(() => {
|
|
148
148
|
let e = l.value.input;
|
|
149
149
|
return e?.steps?.length ? e.steps : e?.question && e?.options ? [{
|
|
150
150
|
id: "legacy",
|
|
151
151
|
question: e.question,
|
|
152
152
|
options: e.options.map((e) => typeof e == "string" ? { label: e } : e)
|
|
153
153
|
}] : [];
|
|
154
|
-
}),
|
|
154
|
+
}), A = y(0), H = g(() => k.value[A.value]), U = g(() => A.value > 0), W = g(() => A.value < k.value.length - 1), Te = g(() => k.value.length > 1), G = g(() => Te.value ? Math.round((A.value + 1) / k.value.length * 100) : null), K = ne({}), q = y(""), J = g({
|
|
155
155
|
get: () => {
|
|
156
|
-
let e =
|
|
156
|
+
let e = H.value ? K[H.value.id] : void 0;
|
|
157
157
|
if (!(e == null || Array.isArray(e))) return e;
|
|
158
158
|
},
|
|
159
159
|
set: (e) => {
|
|
160
|
-
|
|
160
|
+
H.value && (K[H.value.id] = e ?? null);
|
|
161
161
|
}
|
|
162
|
-
}),
|
|
162
|
+
}), Y = g({
|
|
163
163
|
get: () => {
|
|
164
|
-
let e =
|
|
164
|
+
let e = H.value ? K[H.value.id] : void 0;
|
|
165
165
|
return Array.isArray(e) ? e : [];
|
|
166
166
|
},
|
|
167
167
|
set: (e) => {
|
|
168
|
-
|
|
168
|
+
H.value && (K[H.value.id] = e ?? []);
|
|
169
169
|
}
|
|
170
170
|
});
|
|
171
171
|
s(() => l.value.output, (e) => {
|
|
172
172
|
if (e) {
|
|
173
|
-
if (typeof e == "string")
|
|
174
|
-
else if (e.answers) for (let t of e.answers)
|
|
173
|
+
if (typeof e == "string") k.value.length === 1 && (K[k.value[0].id] = e);
|
|
174
|
+
else if (e.answers) for (let t of e.answers) K[t.id] = t.value;
|
|
175
175
|
}
|
|
176
176
|
}, { immediate: !0 }), s(g(() => {
|
|
177
177
|
let e = l.value.output;
|
|
@@ -183,101 +183,105 @@ var ue = {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
}), (e) => {
|
|
186
|
-
!e || !
|
|
186
|
+
!e || !H.value || (H.value.options ?? []).map((e) => e.label).includes(e) || (q.value = e);
|
|
187
187
|
}, { immediate: !0 });
|
|
188
|
-
let
|
|
188
|
+
let X = g(() => H.value?.options ? H.value.options.map((e) => ({
|
|
189
189
|
label: e.label,
|
|
190
190
|
value: e.label,
|
|
191
191
|
description: e.description
|
|
192
192
|
})) : []);
|
|
193
|
-
function
|
|
194
|
-
|
|
193
|
+
function Z() {
|
|
194
|
+
if (q.value.trim()) {
|
|
195
|
+
$();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
W.value ? A.value++ : Q();
|
|
195
199
|
}
|
|
196
|
-
function
|
|
197
|
-
U.value &&
|
|
200
|
+
function Ee() {
|
|
201
|
+
U.value && A.value--;
|
|
198
202
|
}
|
|
199
|
-
function
|
|
203
|
+
function Q() {
|
|
200
204
|
if (!_.value) return;
|
|
201
|
-
let e =
|
|
205
|
+
let e = k.value.map((e) => ({
|
|
202
206
|
id: e.id,
|
|
203
|
-
value:
|
|
207
|
+
value: K[e.id] ?? null
|
|
204
208
|
}));
|
|
205
|
-
if (D.value && !
|
|
209
|
+
if (D.value && !H.value?.allowFreeformInput) {
|
|
206
210
|
let t = e[0].value;
|
|
207
211
|
a("select", Array.isArray(t) ? t[0] : t);
|
|
208
212
|
} else a("select", { answers: e });
|
|
209
213
|
}
|
|
210
|
-
function
|
|
211
|
-
let e =
|
|
212
|
-
e && (
|
|
214
|
+
function $() {
|
|
215
|
+
let e = q.value.trim();
|
|
216
|
+
e && (K[H.value.id] = e, q.value = "", k.value.length === 1 ? Q() : Z());
|
|
213
217
|
}
|
|
214
|
-
function
|
|
215
|
-
_.value && (v.value || D.value) &&
|
|
218
|
+
function De() {
|
|
219
|
+
_.value && (v.value || D.value) && Q();
|
|
216
220
|
}
|
|
217
|
-
function
|
|
218
|
-
let t =
|
|
221
|
+
function Oe(e) {
|
|
222
|
+
let t = K[e.id];
|
|
219
223
|
return t == null ? "—" : Array.isArray(t) ? t.join(", ") : e.type === "date" ? c(new Date(t), "short") : t;
|
|
220
224
|
}
|
|
221
225
|
return (e, n) => {
|
|
222
226
|
let i = ae, a = oe, s = ie;
|
|
223
|
-
return
|
|
227
|
+
return k.value.length ? (E(), p("div", ue, [l.value.input?.title || _.value && k.value.length > 1 ? (E(), p("div", de, [l.value.input?.title ? (E(), p("div", {
|
|
224
228
|
key: 0,
|
|
225
|
-
class: r(["px-sm py-10 font-bold", { "border-b border-surface-3": _.value &&
|
|
226
|
-
}, x(l.value.input?.title), 3)) : T("", !0), _.value &&
|
|
227
|
-
current:
|
|
228
|
-
total:
|
|
229
|
-
})), 1), d("span", null, x(
|
|
229
|
+
class: r(["px-sm py-10 font-bold", { "border-b border-surface-3": _.value && k.value.length > 1 }])
|
|
230
|
+
}, x(l.value.input?.title), 3)) : T("", !0), _.value && k.value.length > 1 ? (E(), p("div", fe, [d("div", pe, [d("span", null, x(w(o)("showForm.step", {
|
|
231
|
+
current: A.value + 1,
|
|
232
|
+
total: k.value.length
|
|
233
|
+
})), 1), d("span", null, x(G.value) + "%", 1)]), d("div", j, [d("div", {
|
|
230
234
|
class: "h-full bg-word-4 transition-all duration-300",
|
|
231
|
-
style: ee({ width: `${
|
|
235
|
+
style: ee({ width: `${G.value}%` })
|
|
232
236
|
}, null, 4)])])) : T("", !0)])) : T("", !0), _.value ? (E(), p(b, { key: 1 }, [
|
|
233
|
-
d("div",
|
|
237
|
+
d("div", M, [d("div", N, [d("strong", P, x(H.value.question), 1), H.value.message ? (E(), S(se, {
|
|
234
238
|
key: 0,
|
|
235
|
-
markdown:
|
|
239
|
+
markdown: H.value.message,
|
|
236
240
|
class: "text-word-2 mt-4"
|
|
237
|
-
}, null, 8, ["markdown"])) : T("", !0)]),
|
|
241
|
+
}, null, 8, ["markdown"])) : T("", !0)]), H.value.options && H.value.multiSelect ? (E(), S(le, {
|
|
238
242
|
key: 0,
|
|
239
|
-
modelValue:
|
|
240
|
-
"onUpdate:modelValue": n[0] ||= (e) =>
|
|
241
|
-
name:
|
|
242
|
-
options:
|
|
243
|
+
modelValue: Y.value,
|
|
244
|
+
"onUpdate:modelValue": n[0] ||= (e) => Y.value = e,
|
|
245
|
+
name: H.value.id,
|
|
246
|
+
options: X.value,
|
|
243
247
|
readonly: !_.value,
|
|
244
248
|
class: "mb-0",
|
|
245
249
|
vertical: ""
|
|
246
|
-
}, C({ _: 2 }, [u(
|
|
250
|
+
}, C({ _: 2 }, [u(X.value, (e) => ({
|
|
247
251
|
name: e.value,
|
|
248
|
-
fn: m(() => [d("div",
|
|
252
|
+
fn: m(() => [d("div", F, [d("div", I, x(e.label), 1), e.description ? (E(), p("div", L, x(e.description), 1)) : T("", !0)])])
|
|
249
253
|
}))]), 1032, [
|
|
250
254
|
"modelValue",
|
|
251
255
|
"name",
|
|
252
256
|
"options",
|
|
253
257
|
"readonly"
|
|
254
|
-
])) :
|
|
258
|
+
])) : H.value.options && !H.value.multiSelect ? (E(), S(ce, {
|
|
255
259
|
key: 1,
|
|
256
|
-
modelValue:
|
|
257
|
-
"onUpdate:modelValue": [n[1] ||= (e) =>
|
|
258
|
-
name:
|
|
259
|
-
options:
|
|
260
|
+
modelValue: J.value,
|
|
261
|
+
"onUpdate:modelValue": [n[1] ||= (e) => J.value = e, De],
|
|
262
|
+
name: H.value.id,
|
|
263
|
+
options: X.value,
|
|
260
264
|
readonly: !_.value,
|
|
261
265
|
vertical: "",
|
|
262
266
|
class: "mb-0"
|
|
263
|
-
}, C({ _: 2 }, [u(
|
|
267
|
+
}, C({ _: 2 }, [u(X.value, (e) => ({
|
|
264
268
|
name: e.value,
|
|
265
|
-
fn: m(() => [d("div",
|
|
269
|
+
fn: m(() => [d("div", R, [d("div", z, x(e.label), 1), e.description ? (E(), p("div", B, x(e.description), 1)) : T("", !0)])])
|
|
266
270
|
}))]), 1032, [
|
|
267
271
|
"modelValue",
|
|
268
272
|
"name",
|
|
269
273
|
"options",
|
|
270
274
|
"readonly"
|
|
271
|
-
])) :
|
|
272
|
-
modelValue:
|
|
273
|
-
"onUpdate:modelValue": n[2] ||= (e) =>
|
|
274
|
-
placeholder:
|
|
275
|
-
type:
|
|
275
|
+
])) : H.value.options ? T("", !0) : (E(), p("div", me, [t(i, {
|
|
276
|
+
modelValue: J.value,
|
|
277
|
+
"onUpdate:modelValue": n[2] ||= (e) => J.value = e,
|
|
278
|
+
placeholder: H.value.placeholder || w(o)("placeholder.insert"),
|
|
279
|
+
type: H.value.type,
|
|
276
280
|
class: "w-full",
|
|
277
|
-
name:
|
|
281
|
+
name: H.value.id,
|
|
278
282
|
modifiers: "compact rounded",
|
|
279
283
|
disabled: !_.value,
|
|
280
|
-
onKeyup: te(
|
|
284
|
+
onKeyup: te(Z, ["enter"])
|
|
281
285
|
}, null, 8, [
|
|
282
286
|
"modelValue",
|
|
283
287
|
"placeholder",
|
|
@@ -285,51 +289,51 @@ var ue = {
|
|
|
285
289
|
"name",
|
|
286
290
|
"disabled"
|
|
287
291
|
])]))]),
|
|
288
|
-
|
|
292
|
+
H.value.allowFreeformInput && H.value.options ? (E(), p("div", V, [d("form", {
|
|
289
293
|
class: "flex gap-xs pt-sm",
|
|
290
|
-
onSubmit: h(
|
|
294
|
+
onSubmit: h($, ["prevent"])
|
|
291
295
|
}, [t(i, {
|
|
292
|
-
modelValue:
|
|
293
|
-
"onUpdate:modelValue": n[3] ||= (e) =>
|
|
296
|
+
modelValue: q.value,
|
|
297
|
+
"onUpdate:modelValue": n[3] ||= (e) => q.value = e,
|
|
294
298
|
class: "flex-1",
|
|
295
|
-
name: `${
|
|
299
|
+
name: `${H.value.id}-custom`,
|
|
296
300
|
modifiers: "compact rounded",
|
|
297
301
|
disabled: !_.value,
|
|
298
|
-
placeholder:
|
|
302
|
+
placeholder: H.value.placeholder || w(o)("showForm.otherPlaceholder")
|
|
299
303
|
}, null, 8, [
|
|
300
304
|
"modelValue",
|
|
301
305
|
"name",
|
|
302
306
|
"disabled",
|
|
303
307
|
"placeholder"
|
|
304
308
|
])], 32)])) : T("", !0),
|
|
305
|
-
|
|
309
|
+
O.value ? T("", !0) : (E(), p("div", he, [U.value ? (E(), S(a, {
|
|
306
310
|
key: 0,
|
|
307
311
|
modifiers: "action-quiet",
|
|
308
312
|
icon: "ri:arrow-left-s-line",
|
|
309
313
|
label: w(o)("action.back"),
|
|
310
|
-
onClick: h(
|
|
311
|
-
}, null, 8, ["label"])) : (E(), p("div",
|
|
314
|
+
onClick: h(Ee, ["stop"])
|
|
315
|
+
}, null, 8, ["label"])) : (E(), p("div", ge)), t(a, {
|
|
312
316
|
modifiers: W.value ? "action-quiet" : "action-primary",
|
|
313
317
|
icon: W.value ? "ri:arrow-right-s-line" : "ri:send-plane-2-line",
|
|
314
318
|
"icon-position": "right",
|
|
315
319
|
label: W.value ? w(o)("action.next") : w(o)("action.submit"),
|
|
316
|
-
onClick: h(
|
|
320
|
+
onClick: h(Z, ["stop"])
|
|
317
321
|
}, null, 8, [
|
|
318
322
|
"modifiers",
|
|
319
323
|
"icon",
|
|
320
324
|
"label"
|
|
321
|
-
])]))
|
|
322
|
-
], 64)) : (E(), p(b, { key: 2 }, [d("div",
|
|
325
|
+
])]))
|
|
326
|
+
], 64)) : (E(), p(b, { key: 2 }, [d("div", _e, [d("ul", ve, [(E(!0), p(b, null, u(k.value, (e) => (E(), p("li", {
|
|
323
327
|
key: e.id,
|
|
324
328
|
class: "leading-normal"
|
|
325
|
-
}, [d("div",
|
|
329
|
+
}, [d("div", ye, x(e.question), 1), d("span", be, x(Oe(e)), 1)]))), 128))])]), d("div", xe, [d("div", Se, [t(s, {
|
|
326
330
|
name: "ri:check-line",
|
|
327
331
|
class: "text-12"
|
|
328
|
-
})]), d("div",
|
|
332
|
+
})]), d("div", Ce, [d("span", we, x(w(o)("showForm.completed")), 1)])])], 64))])) : T("", !0);
|
|
329
333
|
};
|
|
330
334
|
}
|
|
331
335
|
});
|
|
332
336
|
//#endregion
|
|
333
|
-
export {
|
|
337
|
+
export { H as default };
|
|
334
338
|
|
|
335
|
-
//# sourceMappingURL=PkToolShowForm-
|
|
339
|
+
//# sourceMappingURL=PkToolShowForm-dFuoqTPs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PkToolShowForm-dFuoqTPs.js","names":[],"sources":["../../../../packages/components/src/PkEditorRadioButtons.vue","../../../../packages/components/src/PkEditorRadioButtons.vue","../../../../packages/components/src/PkEditorCheckboxButtons.vue","../../../../packages/components/src/PkEditorCheckboxButtons.vue","../../../../packages/components/src/chat/PkToolShowForm.vue","../../../../packages/components/src/chat/PkToolShowForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n import { computed } from 'vue'\n\n const props = defineProps<{\n name: string\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n modifiers?: string | string[]\n }>()\n\n const modelValue = defineModel<\n string | number | boolean | symbol | unknown[] | undefined\n >()\n\n const radioModifiers = computed(() => {\n const toReturn = props.modifiers\n ? Array.isArray(props.modifiers)\n ? props.modifiers\n : [props.modifiers]\n : []\n return [...toReturn, 'button']\n })\n</script>\n\n<template>\n <VvRadioGroup v-model=\"modelValue\" :name modifiers=\"buttons\">\n <VvRadio\n v-for=\"(option, key) in options\"\n :key\n :name\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n :modifiers=\"radioModifiers\">\n <slot :name=\"option.value\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvRadio>\n </VvRadioGroup>\n</template>\n","<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n import { computed } from 'vue'\n\n const props = defineProps<{\n name: string\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n modifiers?: string | string[]\n }>()\n\n const modelValue = defineModel<\n string | number | boolean | symbol | unknown[] | undefined\n >()\n\n const radioModifiers = computed(() => {\n const toReturn = props.modifiers\n ? Array.isArray(props.modifiers)\n ? props.modifiers\n : [props.modifiers]\n : []\n return [...toReturn, 'button']\n })\n</script>\n\n<template>\n <VvRadioGroup v-model=\"modelValue\" :name modifiers=\"buttons\">\n <VvRadio\n v-for=\"(option, key) in options\"\n :key\n :name\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n :modifiers=\"radioModifiers\">\n <slot :name=\"option.value\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvRadio>\n </VvRadioGroup>\n</template>\n","<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n\n defineProps<{\n name: string\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n }>()\n\n const modelValue = defineModel<unknown[] | undefined>()\n</script>\n\n<template>\n <VvCheckboxGroup v-model=\"modelValue\" :name modifiers=\"buttons\">\n <VvCheckbox\n v-for=\"(option, key) in options\"\n :key\n :name\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n modifiers=\"button\">\n <slot :name=\"String(option.value)\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvCheckbox>\n </VvCheckboxGroup>\n</template>\n","<script setup lang=\"ts\">\n import type { Option } from '@volverjs/ui-vue'\n\n defineProps<{\n name: string\n required?: boolean\n options: Option[]\n readonly?: boolean\n disabled?: boolean\n }>()\n\n const modelValue = defineModel<unknown[] | undefined>()\n</script>\n\n<template>\n <VvCheckboxGroup v-model=\"modelValue\" :name modifiers=\"buttons\">\n <VvCheckbox\n v-for=\"(option, key) in options\"\n :key\n :name\n :required\n :readonly\n :disabled\n :value=\"option.value\"\n modifiers=\"button\">\n <slot :name=\"String(option.value)\" v-bind=\"{ option }\">\n {{ option.label }}\n </slot>\n </VvCheckbox>\n </VvCheckboxGroup>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkEditorRadioButtons from '../PkEditorRadioButtons.vue'\n import PkEditorCheckboxButtons from '../PkEditorCheckboxButtons.vue'\n\n // --- Types ---\n\n interface FormOption {\n label: string\n description?: string\n }\n\n interface FormStep {\n id: string\n question: string\n message?: string\n type?: 'text' | 'number' | 'date'\n options?: FormOption[]\n multiSelect?: boolean\n allowFreeformInput?: boolean\n placeholder?: string\n }\n\n interface FormAnswer {\n id: string\n value: string | string[] | number | null\n }\n\n interface ToolPartShape {\n state?: 'input-available' | 'output-available'\n toolCallId?: string\n // AI SDK v5 encodes the tool name in `type` (e.g. 'tool-showMultipleChoice').\n // `toolName` is kept for backward compatibility but is normally absent.\n type?: string\n toolName?: string\n input?: {\n title?: string\n steps?: FormStep[]\n // Legacy showMultipleChoice fields\n question?: string\n options?: (string | FormOption)[]\n }\n output?: string | { answers: FormAnswer[] }\n }\n\n type AnswerValue = string | string[] | number | null\n\n // --- Props & Emits ---\n\n const props = defineProps<{\n part: unknown\n allowCustomAnswer?: boolean\n }>()\n\n const emit = defineEmits<{\n select: [answer: string | { answers: FormAnswer[] }]\n }>()\n\n const { t: $t, d: $d } = useI18n({ useScope: 'global' })\n\n // --- Parsed tool part ---\n\n const toolPart = computed(\n () => JSON.parse(JSON.stringify(props.part)) as ToolPartShape,\n )\n\n const isLegacy = computed(\n () =>\n toolPart.value.toolName === 'showMultipleChoice' ||\n toolPart.value.type === 'tool-showMultipleChoice',\n )\n\n const isInteractive = computed(\n () => toolPart.value.state === 'input-available',\n )\n\n /** Single-step with no custom-answer and no freeform → auto-submit on option pick */\n const isAutoSubmitStep = computed(() => {\n const step = currentStep.value\n if (!step) return false\n return (\n steps.value.length === 1 &&\n !step.allowFreeformInput &&\n !props.allowCustomAnswer\n )\n })\n\n /** Legacy tool should auto-submit on option pick regardless of allowCustomAnswer */\n const isLegacyAutoSubmit = computed(\n () =>\n isLegacy.value &&\n steps.value.length === 1 &&\n !currentStep.value?.multiSelect,\n )\n\n /**\n * True only for the steps that send themselves as soon as an option is\n * picked: the single case where the footer button would be redundant.\n * Every other shape needs it, single-step forms included: with\n * `allowFreeformInput` (or `allowCustomAnswer`) the radio pick no longer\n * auto-submits, so without the footer the user was left with a chosen\n * option and no way to send it.\n */\n const isSelfSubmitting = computed(\n () =>\n Boolean(currentStep.value?.options) &&\n !currentStep.value?.multiSelect &&\n (isAutoSubmitStep.value || isLegacyAutoSubmit.value),\n )\n\n // --- Steps ---\n\n const steps = computed<FormStep[]>(() => {\n const input = toolPart.value.input\n if (input?.steps?.length) {\n return input.steps\n }\n // Legacy showMultipleChoice → normalize into a single step\n if (input?.question && input?.options) {\n return [\n {\n id: 'legacy',\n question: input.question,\n options: input.options.map((o) =>\n typeof o === 'string' ? { label: o } : o,\n ),\n },\n ]\n }\n return []\n })\n\n // --- Navigation state ---\n\n const currentStepIndex = ref(0)\n const currentStep = computed(() => steps.value[currentStepIndex.value])\n const canGoBack = computed(() => currentStepIndex.value > 0)\n const canGoNext = computed(\n () => currentStepIndex.value < steps.value.length - 1,\n )\n const isMultiStep = computed(() => steps.value.length > 1)\n\n const progress = computed(() => {\n if (!isMultiStep.value) return null\n return Math.round(\n ((currentStepIndex.value + 1) / steps.value.length) * 100,\n )\n })\n\n // --- Answers ---\n\n const answers = reactive<Record<string, AnswerValue>>({})\n const customAnswer = ref('')\n\n // Single scalar answer shared by radio buttons and freeform text/number/date inputs.\n const scalarValue = computed<string | number | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStep.value.id]\n : undefined\n if (val == null || Array.isArray(val)) return undefined\n return val\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStep.value.id] = val ?? null\n }\n },\n })\n\n const activeCheckboxValue = computed<unknown[] | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStep.value.id]\n : undefined\n return Array.isArray(val) ? val : []\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStep.value.id] = (val as string[]) ?? []\n }\n },\n })\n\n // --- Initialize answers from existing output ---\n\n watch(\n () => toolPart.value.output,\n (output) => {\n if (!output) return\n if (typeof output === 'string') {\n if (steps.value.length === 1) {\n answers[steps.value[0].id] = output\n }\n } else if (output.answers) {\n for (const a of output.answers) {\n answers[a.id] = a.value\n }\n }\n },\n { immediate: true },\n )\n\n // --- Restore custom answer from output when it doesn't match any option ---\n\n const normalizedOutput = computed(() => {\n const out = toolPart.value.output\n if (!out) return undefined\n if (typeof out === 'string') return out\n if (out.answers?.length === 1) {\n const val = out.answers[0].value\n return Array.isArray(val) ? val.join(', ') : String(val)\n }\n return undefined\n })\n\n watch(\n normalizedOutput,\n (value) => {\n if (!value || !currentStep.value) return\n const optionLabels = (currentStep.value.options ?? []).map(\n (o) => o.label,\n )\n if (!optionLabels.includes(value)) {\n customAnswer.value = value\n }\n },\n { immediate: true },\n )\n\n // --- Option mapping for radio/checkbox components ---\n\n const radioOptions = computed(() => {\n if (!currentStep.value?.options) return []\n return currentStep.value.options.map((option) => ({\n label: option.label,\n value: option.label,\n description: option.description,\n }))\n })\n\n // --- Navigation & Submission ---\n\n function goNext() {\n // A typed freeform answer wins over the picked option: it is the more\n // recent intent, and the footer button is now the visible way to send\n // it (the freeform field only submits on Enter).\n if (customAnswer.value.trim()) {\n submitCustomAnswer()\n return\n }\n if (canGoNext.value) {\n currentStepIndex.value++\n } else {\n submitForm()\n }\n }\n\n function goBack() {\n if (canGoBack.value) {\n currentStepIndex.value--\n }\n }\n\n function submitForm() {\n if (!isInteractive.value) return\n\n const finalAnswers: FormAnswer[] = steps.value.map((step) => ({\n id: step.id,\n value: answers[step.id] ?? null,\n }))\n\n // Legacy backward compatibility: single-step, single-select → return plain string\n if (\n isLegacyAutoSubmit.value &&\n !currentStep.value?.allowFreeformInput\n ) {\n const val = finalAnswers[0].value\n emit('select', Array.isArray(val) ? val[0] : (val as string))\n } else {\n emit('select', { answers: finalAnswers })\n }\n }\n\n function submitCustomAnswer() {\n const value = customAnswer.value.trim()\n if (!value) return\n\n answers[currentStep.value.id] = value\n // Cleared before navigating: `goNext()` reads it back to route a typed\n // answer here, so leaving it set would recurse.\n customAnswer.value = ''\n if (steps.value.length === 1) {\n submitForm()\n } else {\n goNext()\n }\n }\n\n function onRadioChange() {\n if (!isInteractive.value) return\n if (isAutoSubmitStep.value || isLegacyAutoSubmit.value) {\n submitForm()\n }\n }\n\n // --- Summary helpers ---\n\n function getSummaryValue(step: FormStep) {\n const val = answers[step.id]\n if (val == null) return '—'\n if (Array.isArray(val)) return val.join(', ')\n if (step.type === 'date') return $d(new Date(val), 'short')\n return val\n }\n</script>\n\n<template>\n <div\n v-if=\"steps.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden flex flex-col transition-all bg-surface\">\n <!-- Header -->\n <div\n v-if=\"toolPart.input?.title || (isInteractive && steps.length > 1)\"\n class=\"text-12 border-b border-surface-3 text-word-3 bg-surface-1\">\n <div\n v-if=\"toolPart.input?.title\"\n class=\"px-sm py-10 font-bold\"\n :class=\"{\n 'border-b border-surface-3':\n isInteractive && steps.length > 1,\n }\">\n {{ toolPart.input?.title }}\n </div>\n <!-- Progress for multi-step -->\n <div\n v-if=\"isInteractive && steps.length > 1\"\n class=\"flex flex-col gap-4 p-sm\">\n <div class=\"flex justify-between text-10 text-word-3\">\n <span>\n {{\n $t('showForm.step', {\n current: currentStepIndex + 1,\n total: steps.length,\n })\n }}\n </span>\n <span>{{ progress }}%</span>\n </div>\n <div class=\"h-4 bg-surface-3 rounded-full overflow-hidden\">\n <div\n class=\"h-full bg-word-4 transition-all duration-300\"\n :style=\"{ width: `${progress}%` }\"></div>\n </div>\n </div>\n </div>\n\n <!-- Input Available State -->\n <template v-if=\"isInteractive\">\n <!-- Current Step Content -->\n <div class=\"px-sm py-16 flex flex-col gap-12\">\n <div class=\"leading-normal text-12\">\n <strong class=\"font-semibold\">\n {{ currentStep.question }}\n </strong>\n <PkStreamingMarkdown\n v-if=\"currentStep.message\"\n :markdown=\"currentStep.message\"\n class=\"text-word-2 mt-4\" />\n </div>\n\n <!-- Checkbox list for multi-select -->\n <PkEditorCheckboxButtons\n v-if=\"currentStep.options && currentStep.multiSelect\"\n v-model=\"activeCheckboxValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n class=\"mb-0\"\n vertical>\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorCheckboxButtons>\n\n <!-- Radio buttons for single-select -->\n <PkEditorRadioButtons\n v-else-if=\"currentStep.options && !currentStep.multiSelect\"\n v-model=\"scalarValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n vertical\n class=\"mb-0\"\n @update:model-value=\"onRadioChange\">\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorRadioButtons>\n\n <!-- Pure Freeform Input (text, number, date) -->\n <div\n v-else-if=\"!currentStep.options\"\n class=\"flex flex-col gap-6 w-full\">\n <VvInputText\n v-model=\"scalarValue\"\n :placeholder=\"\n currentStep.placeholder || $t('placeholder.insert')\n \"\n :type=\"currentStep.type\"\n class=\"w-full\"\n :name=\"currentStep.id\"\n modifiers=\"compact rounded\"\n :disabled=\"!isInteractive\"\n @keyup.enter=\"goNext\" />\n </div>\n </div>\n\n <!-- Custom footer freeform input (shown alongside options when allowFreeformInput) -->\n <div\n v-if=\"currentStep.allowFreeformInput && currentStep.options\"\n class=\"px-sm pb-sm border-t border-surface-3\">\n <form\n class=\"flex gap-xs pt-sm\"\n @submit.prevent=\"submitCustomAnswer\">\n <VvInputText\n v-model=\"customAnswer\"\n class=\"flex-1\"\n :name=\"`${currentStep.id}-custom`\"\n modifiers=\"compact rounded\"\n :disabled=\"!isInteractive\"\n :placeholder=\"\n currentStep.placeholder ||\n $t('showForm.otherPlaceholder')\n \" />\n </form>\n </div>\n\n <!-- Footer navigation: shown for every step that does not submit\n itself on option pick (see `isSelfSubmitting`). -->\n <div\n v-if=\"!isSelfSubmitting\"\n class=\"px-sm py-10 bg-surface border-t border-surface-3 flex justify-between gap-12\">\n <VvButton\n v-if=\"canGoBack\"\n modifiers=\"action-quiet\"\n icon=\"ri:arrow-left-s-line\"\n :label=\"$t('action.back')\"\n @click.stop=\"goBack\" />\n <div v-else></div>\n\n <VvButton\n :modifiers=\"canGoNext ? 'action-quiet' : 'action-primary'\"\n :icon=\"\n canGoNext\n ? 'ri:arrow-right-s-line'\n : 'ri:send-plane-2-line'\n \"\n icon-position=\"right\"\n :label=\"canGoNext ? $t('action.next') : $t('action.submit')\"\n @click.stop=\"goNext\" />\n </div>\n </template>\n\n <!-- Output Available State (Multi-step Summary) -->\n <template v-else>\n <div class=\"px-sm py-12 bg-surface space-y-10\">\n <ul class=\"flex flex-col gap-8\">\n <li\n v-for=\"step in steps\"\n :key=\"step.id\"\n class=\"leading-normal\">\n <div class=\"text-12 text-word-3\">\n {{ step.question }}\n </div>\n <span class=\"text-12 text-word-2 font-semibold\">\n {{ getSummaryValue(step) }}\n </span>\n </li>\n </ul>\n </div>\n <div\n class=\"px-sm py-12 bg-surface border-t border-surface-3 flex items-center gap-10\">\n <div\n class=\"w-20 h-20 rounded-full border border-success text-success flex items-center justify-center\">\n <VvIcon name=\"ri:check-line\" class=\"text-12\" />\n </div>\n <div class=\"flex flex-col\">\n <span\n class=\"font-semibold text-12 text-word-1 leading-tight\">\n {{ $t('showForm.completed') }}\n </span>\n </div>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\n import { computed, ref, reactive, watch } from 'vue'\n import { useI18n } from 'vue-i18n'\n import PkStreamingMarkdown from './PkStreamingMarkdown.vue'\n import PkEditorRadioButtons from '../PkEditorRadioButtons.vue'\n import PkEditorCheckboxButtons from '../PkEditorCheckboxButtons.vue'\n\n // --- Types ---\n\n interface FormOption {\n label: string\n description?: string\n }\n\n interface FormStep {\n id: string\n question: string\n message?: string\n type?: 'text' | 'number' | 'date'\n options?: FormOption[]\n multiSelect?: boolean\n allowFreeformInput?: boolean\n placeholder?: string\n }\n\n interface FormAnswer {\n id: string\n value: string | string[] | number | null\n }\n\n interface ToolPartShape {\n state?: 'input-available' | 'output-available'\n toolCallId?: string\n // AI SDK v5 encodes the tool name in `type` (e.g. 'tool-showMultipleChoice').\n // `toolName` is kept for backward compatibility but is normally absent.\n type?: string\n toolName?: string\n input?: {\n title?: string\n steps?: FormStep[]\n // Legacy showMultipleChoice fields\n question?: string\n options?: (string | FormOption)[]\n }\n output?: string | { answers: FormAnswer[] }\n }\n\n type AnswerValue = string | string[] | number | null\n\n // --- Props & Emits ---\n\n const props = defineProps<{\n part: unknown\n allowCustomAnswer?: boolean\n }>()\n\n const emit = defineEmits<{\n select: [answer: string | { answers: FormAnswer[] }]\n }>()\n\n const { t: $t, d: $d } = useI18n({ useScope: 'global' })\n\n // --- Parsed tool part ---\n\n const toolPart = computed(\n () => JSON.parse(JSON.stringify(props.part)) as ToolPartShape,\n )\n\n const isLegacy = computed(\n () =>\n toolPart.value.toolName === 'showMultipleChoice' ||\n toolPart.value.type === 'tool-showMultipleChoice',\n )\n\n const isInteractive = computed(\n () => toolPart.value.state === 'input-available',\n )\n\n /** Single-step with no custom-answer and no freeform → auto-submit on option pick */\n const isAutoSubmitStep = computed(() => {\n const step = currentStep.value\n if (!step) return false\n return (\n steps.value.length === 1 &&\n !step.allowFreeformInput &&\n !props.allowCustomAnswer\n )\n })\n\n /** Legacy tool should auto-submit on option pick regardless of allowCustomAnswer */\n const isLegacyAutoSubmit = computed(\n () =>\n isLegacy.value &&\n steps.value.length === 1 &&\n !currentStep.value?.multiSelect,\n )\n\n /**\n * True only for the steps that send themselves as soon as an option is\n * picked: the single case where the footer button would be redundant.\n * Every other shape needs it, single-step forms included: with\n * `allowFreeformInput` (or `allowCustomAnswer`) the radio pick no longer\n * auto-submits, so without the footer the user was left with a chosen\n * option and no way to send it.\n */\n const isSelfSubmitting = computed(\n () =>\n Boolean(currentStep.value?.options) &&\n !currentStep.value?.multiSelect &&\n (isAutoSubmitStep.value || isLegacyAutoSubmit.value),\n )\n\n // --- Steps ---\n\n const steps = computed<FormStep[]>(() => {\n const input = toolPart.value.input\n if (input?.steps?.length) {\n return input.steps\n }\n // Legacy showMultipleChoice → normalize into a single step\n if (input?.question && input?.options) {\n return [\n {\n id: 'legacy',\n question: input.question,\n options: input.options.map((o) =>\n typeof o === 'string' ? { label: o } : o,\n ),\n },\n ]\n }\n return []\n })\n\n // --- Navigation state ---\n\n const currentStepIndex = ref(0)\n const currentStep = computed(() => steps.value[currentStepIndex.value])\n const canGoBack = computed(() => currentStepIndex.value > 0)\n const canGoNext = computed(\n () => currentStepIndex.value < steps.value.length - 1,\n )\n const isMultiStep = computed(() => steps.value.length > 1)\n\n const progress = computed(() => {\n if (!isMultiStep.value) return null\n return Math.round(\n ((currentStepIndex.value + 1) / steps.value.length) * 100,\n )\n })\n\n // --- Answers ---\n\n const answers = reactive<Record<string, AnswerValue>>({})\n const customAnswer = ref('')\n\n // Single scalar answer shared by radio buttons and freeform text/number/date inputs.\n const scalarValue = computed<string | number | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStep.value.id]\n : undefined\n if (val == null || Array.isArray(val)) return undefined\n return val\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStep.value.id] = val ?? null\n }\n },\n })\n\n const activeCheckboxValue = computed<unknown[] | undefined>({\n get: () => {\n const val = currentStep.value\n ? answers[currentStep.value.id]\n : undefined\n return Array.isArray(val) ? val : []\n },\n set: (val) => {\n if (currentStep.value) {\n answers[currentStep.value.id] = (val as string[]) ?? []\n }\n },\n })\n\n // --- Initialize answers from existing output ---\n\n watch(\n () => toolPart.value.output,\n (output) => {\n if (!output) return\n if (typeof output === 'string') {\n if (steps.value.length === 1) {\n answers[steps.value[0].id] = output\n }\n } else if (output.answers) {\n for (const a of output.answers) {\n answers[a.id] = a.value\n }\n }\n },\n { immediate: true },\n )\n\n // --- Restore custom answer from output when it doesn't match any option ---\n\n const normalizedOutput = computed(() => {\n const out = toolPart.value.output\n if (!out) return undefined\n if (typeof out === 'string') return out\n if (out.answers?.length === 1) {\n const val = out.answers[0].value\n return Array.isArray(val) ? val.join(', ') : String(val)\n }\n return undefined\n })\n\n watch(\n normalizedOutput,\n (value) => {\n if (!value || !currentStep.value) return\n const optionLabels = (currentStep.value.options ?? []).map(\n (o) => o.label,\n )\n if (!optionLabels.includes(value)) {\n customAnswer.value = value\n }\n },\n { immediate: true },\n )\n\n // --- Option mapping for radio/checkbox components ---\n\n const radioOptions = computed(() => {\n if (!currentStep.value?.options) return []\n return currentStep.value.options.map((option) => ({\n label: option.label,\n value: option.label,\n description: option.description,\n }))\n })\n\n // --- Navigation & Submission ---\n\n function goNext() {\n // A typed freeform answer wins over the picked option: it is the more\n // recent intent, and the footer button is now the visible way to send\n // it (the freeform field only submits on Enter).\n if (customAnswer.value.trim()) {\n submitCustomAnswer()\n return\n }\n if (canGoNext.value) {\n currentStepIndex.value++\n } else {\n submitForm()\n }\n }\n\n function goBack() {\n if (canGoBack.value) {\n currentStepIndex.value--\n }\n }\n\n function submitForm() {\n if (!isInteractive.value) return\n\n const finalAnswers: FormAnswer[] = steps.value.map((step) => ({\n id: step.id,\n value: answers[step.id] ?? null,\n }))\n\n // Legacy backward compatibility: single-step, single-select → return plain string\n if (\n isLegacyAutoSubmit.value &&\n !currentStep.value?.allowFreeformInput\n ) {\n const val = finalAnswers[0].value\n emit('select', Array.isArray(val) ? val[0] : (val as string))\n } else {\n emit('select', { answers: finalAnswers })\n }\n }\n\n function submitCustomAnswer() {\n const value = customAnswer.value.trim()\n if (!value) return\n\n answers[currentStep.value.id] = value\n // Cleared before navigating: `goNext()` reads it back to route a typed\n // answer here, so leaving it set would recurse.\n customAnswer.value = ''\n if (steps.value.length === 1) {\n submitForm()\n } else {\n goNext()\n }\n }\n\n function onRadioChange() {\n if (!isInteractive.value) return\n if (isAutoSubmitStep.value || isLegacyAutoSubmit.value) {\n submitForm()\n }\n }\n\n // --- Summary helpers ---\n\n function getSummaryValue(step: FormStep) {\n const val = answers[step.id]\n if (val == null) return '—'\n if (Array.isArray(val)) return val.join(', ')\n if (step.type === 'date') return $d(new Date(val), 'short')\n return val\n }\n</script>\n\n<template>\n <div\n v-if=\"steps.length\"\n class=\"border border-surface-3 rounded-xl w-full overflow-hidden flex flex-col transition-all bg-surface\">\n <!-- Header -->\n <div\n v-if=\"toolPart.input?.title || (isInteractive && steps.length > 1)\"\n class=\"text-12 border-b border-surface-3 text-word-3 bg-surface-1\">\n <div\n v-if=\"toolPart.input?.title\"\n class=\"px-sm py-10 font-bold\"\n :class=\"{\n 'border-b border-surface-3':\n isInteractive && steps.length > 1,\n }\">\n {{ toolPart.input?.title }}\n </div>\n <!-- Progress for multi-step -->\n <div\n v-if=\"isInteractive && steps.length > 1\"\n class=\"flex flex-col gap-4 p-sm\">\n <div class=\"flex justify-between text-10 text-word-3\">\n <span>\n {{\n $t('showForm.step', {\n current: currentStepIndex + 1,\n total: steps.length,\n })\n }}\n </span>\n <span>{{ progress }}%</span>\n </div>\n <div class=\"h-4 bg-surface-3 rounded-full overflow-hidden\">\n <div\n class=\"h-full bg-word-4 transition-all duration-300\"\n :style=\"{ width: `${progress}%` }\"></div>\n </div>\n </div>\n </div>\n\n <!-- Input Available State -->\n <template v-if=\"isInteractive\">\n <!-- Current Step Content -->\n <div class=\"px-sm py-16 flex flex-col gap-12\">\n <div class=\"leading-normal text-12\">\n <strong class=\"font-semibold\">\n {{ currentStep.question }}\n </strong>\n <PkStreamingMarkdown\n v-if=\"currentStep.message\"\n :markdown=\"currentStep.message\"\n class=\"text-word-2 mt-4\" />\n </div>\n\n <!-- Checkbox list for multi-select -->\n <PkEditorCheckboxButtons\n v-if=\"currentStep.options && currentStep.multiSelect\"\n v-model=\"activeCheckboxValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n class=\"mb-0\"\n vertical>\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorCheckboxButtons>\n\n <!-- Radio buttons for single-select -->\n <PkEditorRadioButtons\n v-else-if=\"currentStep.options && !currentStep.multiSelect\"\n v-model=\"scalarValue\"\n :name=\"currentStep.id\"\n :options=\"radioOptions\"\n :readonly=\"!isInteractive\"\n vertical\n class=\"mb-0\"\n @update:model-value=\"onRadioChange\">\n <template\n v-for=\"option in radioOptions\"\n :key=\"option.value\"\n #[option.value]>\n <!-- Shared option layout: label + optional description -->\n <div class=\"text-12\">\n <div class=\"font-semibold\">{{ option.label }}</div>\n <div v-if=\"option.description\" class=\"text-word-3\">\n {{ option.description }}\n </div>\n </div>\n </template>\n </PkEditorRadioButtons>\n\n <!-- Pure Freeform Input (text, number, date) -->\n <div\n v-else-if=\"!currentStep.options\"\n class=\"flex flex-col gap-6 w-full\">\n <VvInputText\n v-model=\"scalarValue\"\n :placeholder=\"\n currentStep.placeholder || $t('placeholder.insert')\n \"\n :type=\"currentStep.type\"\n class=\"w-full\"\n :name=\"currentStep.id\"\n modifiers=\"compact rounded\"\n :disabled=\"!isInteractive\"\n @keyup.enter=\"goNext\" />\n </div>\n </div>\n\n <!-- Custom footer freeform input (shown alongside options when allowFreeformInput) -->\n <div\n v-if=\"currentStep.allowFreeformInput && currentStep.options\"\n class=\"px-sm pb-sm border-t border-surface-3\">\n <form\n class=\"flex gap-xs pt-sm\"\n @submit.prevent=\"submitCustomAnswer\">\n <VvInputText\n v-model=\"customAnswer\"\n class=\"flex-1\"\n :name=\"`${currentStep.id}-custom`\"\n modifiers=\"compact rounded\"\n :disabled=\"!isInteractive\"\n :placeholder=\"\n currentStep.placeholder ||\n $t('showForm.otherPlaceholder')\n \" />\n </form>\n </div>\n\n <!-- Footer navigation: shown for every step that does not submit\n itself on option pick (see `isSelfSubmitting`). -->\n <div\n v-if=\"!isSelfSubmitting\"\n class=\"px-sm py-10 bg-surface border-t border-surface-3 flex justify-between gap-12\">\n <VvButton\n v-if=\"canGoBack\"\n modifiers=\"action-quiet\"\n icon=\"ri:arrow-left-s-line\"\n :label=\"$t('action.back')\"\n @click.stop=\"goBack\" />\n <div v-else></div>\n\n <VvButton\n :modifiers=\"canGoNext ? 'action-quiet' : 'action-primary'\"\n :icon=\"\n canGoNext\n ? 'ri:arrow-right-s-line'\n : 'ri:send-plane-2-line'\n \"\n icon-position=\"right\"\n :label=\"canGoNext ? $t('action.next') : $t('action.submit')\"\n @click.stop=\"goNext\" />\n </div>\n </template>\n\n <!-- Output Available State (Multi-step Summary) -->\n <template v-else>\n <div class=\"px-sm py-12 bg-surface space-y-10\">\n <ul class=\"flex flex-col gap-8\">\n <li\n v-for=\"step in steps\"\n :key=\"step.id\"\n class=\"leading-normal\">\n <div class=\"text-12 text-word-3\">\n {{ step.question }}\n </div>\n <span class=\"text-12 text-word-2 font-semibold\">\n {{ getSummaryValue(step) }}\n </span>\n </li>\n </ul>\n </div>\n <div\n class=\"px-sm py-12 bg-surface border-t border-surface-3 flex items-center gap-10\">\n <div\n class=\"w-20 h-20 rounded-full border border-success text-success flex items-center justify-center\">\n <VvIcon name=\"ri:check-line\" class=\"text-12\" />\n </div>\n <div class=\"flex flex-col\">\n <span\n class=\"font-semibold text-12 text-word-1 leading-tight\">\n {{ $t('showForm.completed') }}\n </span>\n </div>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|