@vertesia/ui 1.6.0-dev.20261004.085740Z → 1.6.0-dev.20261009.095813Z

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.
Files changed (235) hide show
  1. package/README.md +24 -0
  2. package/lib/boot/vite.d.ts +3 -0
  3. package/lib/boot/vite.d.ts.map +1 -1
  4. package/lib/boot/vite.js +12 -0
  5. package/lib/boot/vite.js.map +1 -1
  6. package/lib/core/components/shadcn/button.d.ts +1 -0
  7. package/lib/core/components/shadcn/button.d.ts.map +1 -1
  8. package/lib/core/components/shadcn/button.js +2 -2
  9. package/lib/core/components/shadcn/button.js.map +1 -1
  10. package/lib/core/components/shadcn/selectBox.d.ts.map +1 -1
  11. package/lib/core/components/shadcn/selectBox.js +2 -2
  12. package/lib/core/components/shadcn/selectBox.js.map +1 -1
  13. package/lib/core/components/shadcn/tabs.d.ts +2 -1
  14. package/lib/core/components/shadcn/tabs.d.ts.map +1 -1
  15. package/lib/core/components/shadcn/tabs.js +6 -4
  16. package/lib/core/components/shadcn/tabs.js.map +1 -1
  17. package/lib/env/index.d.ts +9 -1
  18. package/lib/env/index.d.ts.map +1 -1
  19. package/lib/env/index.js +1 -1
  20. package/lib/env/index.js.map +1 -1
  21. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts +1 -1
  22. package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
  23. package/lib/features/agent/chat/AgentRequestInputOverlay.js +16 -10
  24. package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
  25. package/lib/features/agent/chat/AskUserWidget.d.ts +7 -1
  26. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  27. package/lib/features/agent/chat/AskUserWidget.js +17 -16
  28. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  29. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  30. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +2 -253
  31. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  32. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts +1 -3
  33. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts.map +1 -1
  34. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js +2 -8
  35. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js.map +1 -1
  36. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.d.ts +4 -0
  37. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.d.ts.map +1 -0
  38. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.js +248 -0
  39. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.js.map +1 -0
  40. package/lib/features/media-viewer/ImagePanel.d.ts +1 -1
  41. package/lib/features/media-viewer/ImagePanel.d.ts.map +1 -1
  42. package/lib/features/media-viewer/ImagePanel.js +40 -10
  43. package/lib/features/media-viewer/ImagePanel.js.map +1 -1
  44. package/lib/features/store/types/IntakePolicyEditor.d.ts.map +1 -1
  45. package/lib/features/store/types/IntakePolicyEditor.js +1 -1
  46. package/lib/features/store/types/IntakePolicyEditor.js.map +1 -1
  47. package/lib/i18n/locales/ar.json +15 -0
  48. package/lib/i18n/locales/de.json +15 -0
  49. package/lib/i18n/locales/en.json +15 -0
  50. package/lib/i18n/locales/es.json +15 -0
  51. package/lib/i18n/locales/fr.json +15 -0
  52. package/lib/i18n/locales/it.json +15 -0
  53. package/lib/i18n/locales/ja.json +15 -0
  54. package/lib/i18n/locales/ko.json +15 -0
  55. package/lib/i18n/locales/pt.json +15 -0
  56. package/lib/i18n/locales/ru.json +15 -0
  57. package/lib/i18n/locales/tr.json +15 -0
  58. package/lib/i18n/locales/zh-TW.json +15 -0
  59. package/lib/i18n/locales/zh.json +15 -0
  60. package/lib/session/auth/composable.d.ts +1 -5
  61. package/lib/session/auth/composable.d.ts.map +1 -1
  62. package/lib/session/auth/composable.js +1 -10
  63. package/lib/session/auth/composable.js.map +1 -1
  64. package/lib/session/auth/firebase.d.ts.map +1 -1
  65. package/lib/session/auth/firebase.js +8 -5
  66. package/lib/session/auth/firebase.js.map +1 -1
  67. package/lib/session/auth/oauth.d.ts.map +1 -1
  68. package/lib/session/auth/oauth.js +43 -17
  69. package/lib/session/auth/oauth.js.map +1 -1
  70. package/lib/session/auth/vertesiaHosts.d.ts +0 -40
  71. package/lib/session/auth/vertesiaHosts.d.ts.map +1 -1
  72. package/lib/session/auth/vertesiaHosts.js +2 -45
  73. package/lib/session/auth/vertesiaHosts.js.map +1 -1
  74. package/lib/session/index.d.ts +1 -1
  75. package/lib/session/index.d.ts.map +1 -1
  76. package/lib/session/index.js +1 -1
  77. package/lib/session/index.js.map +1 -1
  78. package/lib/session/scopeSelection.d.ts +4 -0
  79. package/lib/session/scopeSelection.d.ts.map +1 -1
  80. package/lib/session/scopeSelection.js +11 -0
  81. package/lib/session/scopeSelection.js.map +1 -1
  82. package/lib/shell/AuthScreenPreview.d.ts.map +1 -1
  83. package/lib/shell/AuthScreenPreview.js +1 -0
  84. package/lib/shell/AuthScreenPreview.js.map +1 -1
  85. package/lib/shell/login/SignInFlow.d.ts +3 -1
  86. package/lib/shell/login/SignInFlow.d.ts.map +1 -1
  87. package/lib/shell/login/SignInFlow.js +13 -4
  88. package/lib/shell/login/SignInFlow.js.map +1 -1
  89. package/lib/shell/login/SignInIcons.d.ts.map +1 -1
  90. package/lib/shell/login/SignInIcons.js +2 -1
  91. package/lib/shell/login/SignInIcons.js.map +1 -1
  92. package/lib/shell/login/SignInPasswordStep.d.ts +9 -0
  93. package/lib/shell/login/SignInPasswordStep.d.ts.map +1 -0
  94. package/lib/shell/login/SignInPasswordStep.js +58 -0
  95. package/lib/shell/login/SignInPasswordStep.js.map +1 -0
  96. package/lib/shell/login/SignInPrimitives.d.ts +13 -0
  97. package/lib/shell/login/SignInPrimitives.d.ts.map +1 -1
  98. package/lib/shell/login/SignInPrimitives.js +11 -1
  99. package/lib/shell/login/SignInPrimitives.js.map +1 -1
  100. package/lib/shell/login/SignInProvidersStep.d.ts +3 -1
  101. package/lib/shell/login/SignInProvidersStep.d.ts.map +1 -1
  102. package/lib/shell/login/SignInProvidersStep.js +4 -2
  103. package/lib/shell/login/SignInProvidersStep.js.map +1 -1
  104. package/lib/shell/login/SignInReturningStep.d.ts +3 -1
  105. package/lib/shell/login/SignInReturningStep.d.ts.map +1 -1
  106. package/lib/shell/login/SignInReturningStep.js +18 -8
  107. package/lib/shell/login/SignInReturningStep.js.map +1 -1
  108. package/lib/shell/login/SignInTenantStep.d.ts +3 -1
  109. package/lib/shell/login/SignInTenantStep.d.ts.map +1 -1
  110. package/lib/shell/login/SignInTenantStep.js +2 -2
  111. package/lib/shell/login/SignInTenantStep.js.map +1 -1
  112. package/lib/shell/login/SignInWithProviderButton.d.ts +5 -3
  113. package/lib/shell/login/SignInWithProviderButton.d.ts.map +1 -1
  114. package/lib/shell/login/SignInWithProviderButton.js +8 -7
  115. package/lib/shell/login/SignInWithProviderButton.js.map +1 -1
  116. package/lib/shell/login/SigninScreen.d.ts.map +1 -1
  117. package/lib/shell/login/SigninScreen.js +9 -3
  118. package/lib/shell/login/SigninScreen.js.map +1 -1
  119. package/lib/shell/login/signInUtils.d.ts +24 -4
  120. package/lib/shell/login/signInUtils.d.ts.map +1 -1
  121. package/lib/shell/login/signInUtils.js +53 -1
  122. package/lib/shell/login/signInUtils.js.map +1 -1
  123. package/lib/vertesia-ui-boot.js.map +1 -1
  124. package/lib/vertesia-ui-core.js +42 -42
  125. package/lib/vertesia-ui-core.js.map +1 -1
  126. package/lib/vertesia-ui-env.js +1 -1
  127. package/lib/vertesia-ui-env.js.map +1 -1
  128. package/lib/vertesia-ui-features.js +574 -589
  129. package/lib/vertesia-ui-features.js.map +1 -1
  130. package/lib/vertesia-ui-i18n.1NWiB6fw.chunk.js +5 -0
  131. package/lib/vertesia-ui-i18n.1NWiB6fw.chunk.js.map +1 -0
  132. package/lib/vertesia-ui-i18n.6Ozlf6Ks.chunk.js +5 -0
  133. package/lib/vertesia-ui-i18n.6Ozlf6Ks.chunk.js.map +1 -0
  134. package/lib/vertesia-ui-i18n.AttJpg8U.chunk.js +5 -0
  135. package/lib/vertesia-ui-i18n.AttJpg8U.chunk.js.map +1 -0
  136. package/lib/vertesia-ui-i18n.BI_xdp49.chunk.js +5 -0
  137. package/lib/vertesia-ui-i18n.BI_xdp49.chunk.js.map +1 -0
  138. package/lib/vertesia-ui-i18n.C-2EEX6w.chunk.js +5 -0
  139. package/lib/vertesia-ui-i18n.C-2EEX6w.chunk.js.map +1 -0
  140. package/lib/vertesia-ui-i18n.CAmo54FO.chunk.js +5 -0
  141. package/lib/vertesia-ui-i18n.CAmo54FO.chunk.js.map +1 -0
  142. package/lib/vertesia-ui-i18n.CbBzmHNP.chunk.js +5 -0
  143. package/lib/vertesia-ui-i18n.CbBzmHNP.chunk.js.map +1 -0
  144. package/lib/vertesia-ui-i18n.DZgTzkIg.chunk.js +5 -0
  145. package/lib/vertesia-ui-i18n.DZgTzkIg.chunk.js.map +1 -0
  146. package/lib/vertesia-ui-i18n.DZsITxq5.chunk.js +5 -0
  147. package/lib/vertesia-ui-i18n.DZsITxq5.chunk.js.map +1 -0
  148. package/lib/vertesia-ui-i18n.Dbf2GHth.chunk.js +5 -0
  149. package/lib/vertesia-ui-i18n.Dbf2GHth.chunk.js.map +1 -0
  150. package/lib/vertesia-ui-i18n.js +2 -2
  151. package/lib/vertesia-ui-i18n.js.map +1 -1
  152. package/lib/vertesia-ui-i18n.lleWnH5n.chunk.js +5 -0
  153. package/lib/vertesia-ui-i18n.lleWnH5n.chunk.js.map +1 -0
  154. package/lib/vertesia-ui-i18n.wG_XSkbr.chunk.js +5 -0
  155. package/lib/vertesia-ui-i18n.wG_XSkbr.chunk.js.map +1 -0
  156. package/lib/vertesia-ui-layout.js +15 -15
  157. package/lib/vertesia-ui-layout.js.map +1 -1
  158. package/lib/vertesia-ui-rich-text.js +40 -40
  159. package/lib/vertesia-ui-rich-text.js.map +1 -1
  160. package/lib/vertesia-ui-router.js.map +1 -1
  161. package/lib/vertesia-ui-session.js +1 -1
  162. package/lib/vertesia-ui-session.js.map +1 -1
  163. package/lib/vertesia-ui-shell.js +54 -47
  164. package/lib/vertesia-ui-shell.js.map +1 -1
  165. package/lib/vertesia-ui-widgets.js +47 -47
  166. package/lib/vertesia-ui-widgets.js.map +1 -1
  167. package/package.json +20 -20
  168. package/src/boot/vite.ts +15 -0
  169. package/src/core/components/shadcn/button.tsx +3 -2
  170. package/src/core/components/shadcn/selectBox.tsx +12 -4
  171. package/src/core/components/shadcn/tabs.tsx +9 -3
  172. package/src/css/index.css +1 -1
  173. package/src/css/typography.css +111 -88
  174. package/src/env/index.ts +10 -3
  175. package/src/features/agent/chat/AgentRequestInputOverlay.tsx +73 -34
  176. package/src/features/agent/chat/AskUserWidget.tsx +225 -206
  177. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +2 -253
  178. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx +2 -8
  179. package/src/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.ts +248 -0
  180. package/src/features/media-viewer/ImagePanel.tsx +51 -10
  181. package/src/features/store/types/IntakePolicyEditor.tsx +2 -1
  182. package/src/i18n/locales/ar.json +15 -0
  183. package/src/i18n/locales/de.json +15 -0
  184. package/src/i18n/locales/en.json +15 -0
  185. package/src/i18n/locales/es.json +15 -0
  186. package/src/i18n/locales/fr.json +15 -0
  187. package/src/i18n/locales/it.json +15 -0
  188. package/src/i18n/locales/ja.json +15 -0
  189. package/src/i18n/locales/ko.json +15 -0
  190. package/src/i18n/locales/pt.json +15 -0
  191. package/src/i18n/locales/ru.json +15 -0
  192. package/src/i18n/locales/tr.json +15 -0
  193. package/src/i18n/locales/zh-TW.json +15 -0
  194. package/src/i18n/locales/zh.json +15 -0
  195. package/src/session/auth/composable.ts +1 -11
  196. package/src/session/auth/firebase.ts +9 -6
  197. package/src/session/auth/oauth.ts +47 -16
  198. package/src/session/auth/vertesiaHosts.ts +2 -47
  199. package/src/session/index.ts +1 -1
  200. package/src/session/scopeSelection.ts +13 -0
  201. package/src/shell/AuthScreenPreview.tsx +1 -0
  202. package/src/shell/login/SignInFlow.tsx +23 -2
  203. package/src/shell/login/SignInIcons.tsx +2 -1
  204. package/src/shell/login/SignInPasswordStep.tsx +103 -0
  205. package/src/shell/login/SignInPrimitives.tsx +53 -1
  206. package/src/shell/login/SignInProvidersStep.tsx +8 -4
  207. package/src/shell/login/SignInReturningStep.tsx +20 -8
  208. package/src/shell/login/SignInTenantStep.tsx +6 -2
  209. package/src/shell/login/SignInWithProviderButton.tsx +11 -7
  210. package/src/shell/login/SigninScreen.tsx +8 -3
  211. package/src/shell/login/signInUtils.ts +68 -8
  212. package/lib/vertesia-ui-i18n.BCPusS2_.chunk.js +0 -5
  213. package/lib/vertesia-ui-i18n.BCPusS2_.chunk.js.map +0 -1
  214. package/lib/vertesia-ui-i18n.BHWlHchf.chunk.js +0 -5
  215. package/lib/vertesia-ui-i18n.BHWlHchf.chunk.js.map +0 -1
  216. package/lib/vertesia-ui-i18n.BkNx8SZu.chunk.js +0 -5
  217. package/lib/vertesia-ui-i18n.BkNx8SZu.chunk.js.map +0 -1
  218. package/lib/vertesia-ui-i18n.C1LasLrO.chunk.js +0 -5
  219. package/lib/vertesia-ui-i18n.C1LasLrO.chunk.js.map +0 -1
  220. package/lib/vertesia-ui-i18n.D8PQ5yKU.chunk.js +0 -5
  221. package/lib/vertesia-ui-i18n.D8PQ5yKU.chunk.js.map +0 -1
  222. package/lib/vertesia-ui-i18n.DUtVV1Bq.chunk.js +0 -5
  223. package/lib/vertesia-ui-i18n.DUtVV1Bq.chunk.js.map +0 -1
  224. package/lib/vertesia-ui-i18n.DgA36_k5.chunk.js +0 -5
  225. package/lib/vertesia-ui-i18n.DgA36_k5.chunk.js.map +0 -1
  226. package/lib/vertesia-ui-i18n.DmG-fjjZ.chunk.js +0 -5
  227. package/lib/vertesia-ui-i18n.DmG-fjjZ.chunk.js.map +0 -1
  228. package/lib/vertesia-ui-i18n.DreWvQIk.chunk.js +0 -5
  229. package/lib/vertesia-ui-i18n.DreWvQIk.chunk.js.map +0 -1
  230. package/lib/vertesia-ui-i18n.Pp8Tj48d.chunk.js +0 -5
  231. package/lib/vertesia-ui-i18n.Pp8Tj48d.chunk.js.map +0 -1
  232. package/lib/vertesia-ui-i18n.gais--x5.chunk.js +0 -5
  233. package/lib/vertesia-ui-i18n.gais--x5.chunk.js.map +0 -1
  234. package/lib/vertesia-ui-i18n.xkqaKpY_.chunk.js +0 -5
  235. package/lib/vertesia-ui-i18n.xkqaKpY_.chunk.js.map +0 -1
@@ -64,6 +64,12 @@ export interface AskUserWidgetProps {
64
64
  compact?: boolean;
65
65
  /** Render as a resolved transcript prompt, without pending controls */
66
66
  answered?: boolean;
67
+ /** Optional action beside the question, inside the existing card header. */
68
+ headerAction?: React.ReactNode;
69
+ /** ID of the response controls for an accessible disclosure action. */
70
+ responseControlsId?: string;
71
+ /** Hide response controls while keeping drafts and selections mounted. */
72
+ responseControlsHidden?: boolean;
67
73
 
68
74
  // Styling props for full customization
69
75
  /** Additional className for the outer container */
@@ -146,6 +152,9 @@ export function AskUserWidget({
146
152
  hideBorder = false,
147
153
  compact = false,
148
154
  answered = false,
155
+ headerAction,
156
+ responseControlsId,
157
+ responseControlsHidden = false,
149
158
  // Styling props
150
159
  className,
151
160
  cardClassName,
@@ -160,7 +169,6 @@ export function AskUserWidget({
160
169
  submitButtonClassName,
161
170
  }: AskUserWidgetProps) {
162
171
  const { t } = useUITranslation();
163
- const resolvedPlaceholder = placeholder ?? t('agent.typeYourResponse');
164
172
  const resolvedSubmitLabel = submitLabel ?? t('agent.send');
165
173
  const [inputValue, setInputValue] = React.useState('');
166
174
  const [selectedOptions, setSelectedOptions] = React.useState<Set<string>>(new Set());
@@ -170,6 +178,9 @@ export function AskUserWidget({
170
178
  const DefaultIcon = VARIANT_ICONS[variant];
171
179
  const validOptions = isAskUserOptions(options);
172
180
  const safeOptions = validOptions ? options : [];
181
+ // Next to predefined options the input is an alternative to picking one, not the whole prompt.
182
+ const resolvedPlaceholder =
183
+ placeholder ?? (safeOptions.length > 0 ? t('agent.typeDifferentResponse') : t('agent.typeYourResponse'));
173
184
  const invalidOptionsReported = React.useRef(false);
174
185
 
175
186
  React.useEffect(() => {
@@ -260,124 +271,127 @@ export function AskUserWidget({
260
271
  )}
261
272
  </div>
262
273
  </div>
274
+ {headerAction}
263
275
  </div>
264
276
  </div>
265
277
 
266
- {safeOptions.length > 0 && (
267
- <div className={cn('flex flex-col gap-1.5 px-3 pb-3 pt-0', optionsClassName)}>
268
- {multiSelect ? (
269
- <>
270
- {safeOptions.map((option) => {
271
- const selected = selectedOptions.has(option.id);
272
- return (
273
- <label
274
- key={option.id}
275
- className={cn(
276
- 'flex cursor-pointer items-start gap-2.5 rounded-md border px-3 py-2 text-start transition-colors',
277
- selected
278
- ? 'border-info/60 bg-info/10'
279
- : 'border-border bg-background/70 hover:bg-mixer-muted/15',
280
- isLoading && 'cursor-not-allowed opacity-50',
281
- buttonClassName,
282
- )}
283
- >
284
- <input
285
- type="checkbox"
286
- checked={selected}
287
- onChange={() => toggleOption(option.id)}
288
- disabled={isLoading}
289
- className="mt-1 size-4 rounded border-border bg-background text-info focus:ring-info"
290
- />
291
- <span className="min-w-0 flex-1">
292
- <span className="flex items-center gap-2 text-sm font-medium leading-5 text-foreground">
293
- {option.icon}
294
- <span className="break-words">{option.label}</span>
295
- </span>
296
- {option.description && (
297
- <span className="mt-0.5 block break-words text-xs leading-5 text-muted">
298
- {option.description}
299
- </span>
278
+ <div id={responseControlsId} hidden={responseControlsHidden}>
279
+ {safeOptions.length > 0 && (
280
+ <div className={cn('flex flex-col gap-1.5 px-3 pb-3 pt-0', optionsClassName)}>
281
+ {multiSelect ? (
282
+ <>
283
+ {safeOptions.map((option) => {
284
+ const selected = selectedOptions.has(option.id);
285
+ return (
286
+ <label
287
+ key={option.id}
288
+ className={cn(
289
+ 'flex cursor-pointer items-start gap-2.5 rounded-md border px-3 py-2 text-start transition-colors',
290
+ selected
291
+ ? 'border-info/60 bg-info/10'
292
+ : 'border-border bg-background/70 hover:bg-mixer-muted/15',
293
+ isLoading && 'cursor-not-allowed opacity-50',
294
+ buttonClassName,
300
295
  )}
301
- </span>
302
- </label>
303
- );
304
- })}
305
- <div className="pt-1">
306
- <Button
307
- size="sm"
308
- onClick={handleMultiSubmit}
309
- disabled={isLoading || selectedOptions.size === 0}
310
- className="inline-flex items-center gap-2"
296
+ >
297
+ <input
298
+ type="checkbox"
299
+ checked={selected}
300
+ onChange={() => toggleOption(option.id)}
301
+ disabled={isLoading}
302
+ className="mt-1 size-4 rounded border-border bg-background text-info focus:ring-info"
303
+ />
304
+ <span className="min-w-0 flex-1">
305
+ <span className="flex items-center gap-2 text-sm font-medium leading-5 text-foreground">
306
+ {option.icon}
307
+ <span className="break-words">{option.label}</span>
308
+ </span>
309
+ {option.description && (
310
+ <span className="mt-0.5 block break-words text-xs leading-5 text-muted">
311
+ {option.description}
312
+ </span>
313
+ )}
314
+ </span>
315
+ </label>
316
+ );
317
+ })}
318
+ <div className="pt-1">
319
+ <Button
320
+ size="sm"
321
+ onClick={handleMultiSubmit}
322
+ disabled={isLoading || selectedOptions.size === 0}
323
+ className="inline-flex items-center gap-2"
324
+ >
325
+ <Send className="size-4" />
326
+ {selectedOptions.size > 0
327
+ ? t('agent.submitSelectionCount', { count: selectedOptions.size })
328
+ : t('agent.submitSelection')}
329
+ </Button>
330
+ </div>
331
+ </>
332
+ ) : (
333
+ safeOptions.map((option) => (
334
+ <button
335
+ type="button"
336
+ key={option.id}
337
+ onClick={() => onSelect?.(option.id)}
338
+ disabled={isLoading}
339
+ className={cn(
340
+ 'flex w-full cursor-pointer items-start gap-2.5 rounded-md border border-border bg-background/70 px-3 py-2 text-start transition-colors',
341
+ 'hover:bg-mixer-muted/15 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
342
+ isLoading && 'cursor-not-allowed opacity-50',
343
+ buttonClassName,
344
+ )}
311
345
  >
312
- <Send className="size-4" />
313
- {selectedOptions.size > 0
314
- ? t('agent.submitSelectionCount', { count: selectedOptions.size })
315
- : t('agent.submitSelection')}
316
- </Button>
317
- </div>
318
- </>
319
- ) : (
320
- safeOptions.map((option) => (
321
- <button
322
- type="button"
323
- key={option.id}
324
- onClick={() => onSelect?.(option.id)}
346
+ <span
347
+ className="mt-1 size-4 flex-shrink-0 rounded-full border border-border"
348
+ aria-hidden="true"
349
+ />
350
+ <span className="min-w-0 flex-1">
351
+ <span className="flex items-center gap-2 text-sm font-medium leading-5 text-foreground">
352
+ {option.icon}
353
+ <span className="break-words">{option.label}</span>
354
+ </span>
355
+ {option.description && (
356
+ <span className="mt-0.5 block break-words text-xs leading-5 text-muted">
357
+ {option.description}
358
+ </span>
359
+ )}
360
+ </span>
361
+ </button>
362
+ ))
363
+ )}
364
+ </div>
365
+ )}
366
+
367
+ {allowFreeResponse && (
368
+ <div className={cn('px-3 pb-3 pt-0', inputContainerClassName)}>
369
+ <div className="flex gap-2">
370
+ <input
371
+ ref={inputRef}
372
+ type="text"
373
+ value={inputValue}
374
+ onChange={(e) => setInputValue(e.target.value)}
375
+ onKeyDown={handleKeyDown}
376
+ placeholder={resolvedPlaceholder}
325
377
  disabled={isLoading}
326
378
  className={cn(
327
- 'flex w-full cursor-pointer items-start gap-2.5 rounded-md border border-border bg-background/70 px-3 py-2 text-start transition-colors',
328
- 'hover:bg-mixer-muted/15 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
329
- isLoading && 'cursor-not-allowed opacity-50',
330
- buttonClassName,
379
+ 'min-w-0 flex-1 rounded-md border border-border bg-background/70 px-3 py-2 text-sm text-foreground focus:border-transparent focus:ring-2 focus:ring-ring',
380
+ inputClassName,
331
381
  )}
382
+ />
383
+ <Button
384
+ size="sm"
385
+ onClick={handleSubmit}
386
+ disabled={isLoading || !inputValue.trim()}
387
+ className={submitButtonClassName}
332
388
  >
333
- <span
334
- className="mt-1 size-4 flex-shrink-0 rounded-full border border-border"
335
- aria-hidden="true"
336
- />
337
- <span className="min-w-0 flex-1">
338
- <span className="flex items-center gap-2 text-sm font-medium leading-5 text-foreground">
339
- {option.icon}
340
- <span className="break-words">{option.label}</span>
341
- </span>
342
- {option.description && (
343
- <span className="mt-0.5 block break-words text-xs leading-5 text-muted">
344
- {option.description}
345
- </span>
346
- )}
347
- </span>
348
- </button>
349
- ))
350
- )}
351
- </div>
352
- )}
353
-
354
- {allowFreeResponse && (
355
- <div className={cn('px-3 pb-3 pt-0', inputContainerClassName)}>
356
- <div className="flex gap-2">
357
- <input
358
- ref={inputRef}
359
- type="text"
360
- value={inputValue}
361
- onChange={(e) => setInputValue(e.target.value)}
362
- onKeyDown={handleKeyDown}
363
- placeholder={resolvedPlaceholder}
364
- disabled={isLoading}
365
- className={cn(
366
- 'min-w-0 flex-1 rounded-md border border-border bg-background/70 px-3 py-2 text-sm text-foreground focus:border-transparent focus:ring-2 focus:ring-ring',
367
- inputClassName,
368
- )}
369
- />
370
- <Button
371
- size="sm"
372
- onClick={handleSubmit}
373
- disabled={isLoading || !inputValue.trim()}
374
- className={submitButtonClassName}
375
- >
376
- {isLoading ? '...' : resolvedSubmitLabel}
377
- </Button>
389
+ {isLoading ? '...' : resolvedSubmitLabel}
390
+ </Button>
391
+ </div>
378
392
  </div>
379
- </div>
380
- )}
393
+ )}
394
+ </div>
381
395
  </div>
382
396
  </div>
383
397
  );
@@ -408,124 +422,129 @@ export function AskUserWidget({
408
422
  )}
409
423
  </div>
410
424
  </div>
425
+ {headerAction}
411
426
  </div>
412
427
  </div>
413
428
 
414
- {/* Options */}
415
- {safeOptions.length > 0 && (
416
- <div className={`px-4 pb-3 pt-1 ${optionsClassName || ''}`}>
417
- {multiSelect ? (
418
- /* Multi-select mode with checkboxes */
419
- <div className="space-y-2">
420
- {safeOptions.map((option) => (
421
- <label
422
- key={option.id}
423
- className={`flex items-start gap-3 px-4 py-3 rounded-lg cursor-pointer transition-colors
429
+ <div id={responseControlsId} hidden={responseControlsHidden}>
430
+ {/* Options */}
431
+ {safeOptions.length > 0 && (
432
+ <div className={`px-4 pb-3 pt-1 ${optionsClassName || ''}`}>
433
+ {multiSelect ? (
434
+ /* Multi-select mode with checkboxes */
435
+ <div className="space-y-2">
436
+ {safeOptions.map((option) => (
437
+ <label
438
+ key={option.id}
439
+ className={`flex items-start gap-3 px-4 py-3 rounded-lg cursor-pointer transition-colors
424
440
  ${
425
441
  selectedOptions.has(option.id)
426
442
  ? 'bg-info border border-info'
427
443
  : 'bg-card text-card-foreground border border-border hover:bg-muted'
428
444
  }
429
445
  ${isLoading ? 'opacity-50 cursor-not-allowed' : ''}`}
430
- >
431
- <input
432
- type="checkbox"
433
- checked={selectedOptions.has(option.id)}
434
- onChange={() => toggleOption(option.id)}
435
- disabled={isLoading}
436
- className="mt-0.5 w-4 h-4 rounded border-border text-info focus:ring-info bg-background"
437
- />
438
- <div className="flex-1 overflow-hidden">
439
- <div className="flex items-center gap-2 text-sm font-medium text-foreground break-words">
440
- {option.icon}
441
- <span className="break-words">{option.label}</span>
442
- </div>
443
- {option.description && (
444
- <div className="mt-1 text-sm text-muted break-words whitespace-pre-wrap">
445
- {option.description}
446
+ >
447
+ <input
448
+ type="checkbox"
449
+ checked={selectedOptions.has(option.id)}
450
+ onChange={() => toggleOption(option.id)}
451
+ disabled={isLoading}
452
+ className="mt-0.5 w-4 h-4 rounded border-border text-info focus:ring-info bg-background"
453
+ />
454
+ <div className="flex-1 overflow-hidden">
455
+ <div className="flex items-center gap-2 text-sm font-medium text-foreground break-words">
456
+ {option.icon}
457
+ <span className="break-words">{option.label}</span>
446
458
  </div>
447
- )}
448
- </div>
449
- </label>
450
- ))}
451
- <div className="pt-2">
452
- <Button
453
- size="sm"
454
- onClick={handleMultiSubmit}
455
- disabled={isLoading || selectedOptions.size === 0}
456
- className="flex items-center gap-2"
457
- >
458
- <Send className="size-4" />
459
- {selectedOptions.size > 0
460
- ? t('agent.submitSelectionCount', { count: selectedOptions.size })
461
- : t('agent.submitSelection')}
462
- </Button>
459
+ {option.description && (
460
+ <div className="mt-1 text-sm text-muted break-words whitespace-pre-wrap">
461
+ {option.description}
462
+ </div>
463
+ )}
464
+ </div>
465
+ </label>
466
+ ))}
467
+ <div className="pt-2">
468
+ <Button
469
+ size="sm"
470
+ onClick={handleMultiSubmit}
471
+ disabled={isLoading || selectedOptions.size === 0}
472
+ className="flex items-center gap-2"
473
+ >
474
+ <Send className="size-4" />
475
+ {selectedOptions.size > 0
476
+ ? t('agent.submitSelectionCount', { count: selectedOptions.size })
477
+ : t('agent.submitSelection')}
478
+ </Button>
479
+ </div>
463
480
  </div>
464
- </div>
465
- ) : (
466
- /* Single-select mode - always use full-width card layout for clarity */
467
- <div className="flex flex-col gap-2 w-full">
468
- {safeOptions.map((option) => (
469
- <Button
470
- variant="unstyled"
471
- key={option.id}
472
- onClick={() => onSelect?.(option.id)}
473
- disabled={isLoading}
474
- className={`w-full h-auto whitespace-normal text-start px-4 py-3 rounded-lg border border-border
481
+ ) : (
482
+ /* Single-select mode - always use full-width card layout for clarity */
483
+ <div className="flex flex-col gap-2 w-full">
484
+ {safeOptions.map((option) => (
485
+ <Button
486
+ variant="unstyled"
487
+ key={option.id}
488
+ onClick={() => onSelect?.(option.id)}
489
+ disabled={isLoading}
490
+ className={`w-full h-auto whitespace-normal text-start px-4 py-3 rounded-lg border border-border
475
491
  bg-card text-card-foreground
476
492
  hover:bg-muted hover:border-border
477
493
  focus:outline-none focus:ring-2 focus:ring-info focus:ring-offset-1
478
494
  transition-colors
479
495
  ${isLoading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}
480
496
  ${buttonClassName || ''}`}
481
- >
482
- <div className="flex items-start gap-3">
483
- {option.icon && (
484
- <span className="flex-shrink-0 mt-0.5 text-muted">{option.icon}</span>
485
- )}
486
- <div className="flex-1 overflow-hidden">
487
- <div className="font-medium text-sm text-foreground break-words text-center">
488
- {option.label}
489
- </div>
490
- {option.description && (
491
- <div className="mt-1 text-sm text-muted break-words whitespace-pre-wrap">
492
- {option.description}
493
- </div>
497
+ >
498
+ <div className="flex items-start gap-3">
499
+ {option.icon && (
500
+ <span className="flex-shrink-0 mt-0.5 text-muted">
501
+ {option.icon}
502
+ </span>
494
503
  )}
504
+ <div className="flex-1 overflow-hidden">
505
+ <div className="font-medium text-sm text-foreground break-words text-center">
506
+ {option.label}
507
+ </div>
508
+ {option.description && (
509
+ <div className="mt-1 text-sm text-muted break-words whitespace-pre-wrap">
510
+ {option.description}
511
+ </div>
512
+ )}
513
+ </div>
495
514
  </div>
496
- </div>
497
- </Button>
498
- ))}
499
- </div>
500
- )}
501
- </div>
502
- )}
515
+ </Button>
516
+ ))}
517
+ </div>
518
+ )}
519
+ </div>
520
+ )}
503
521
 
504
- {/* Free-form input */}
505
- {allowFreeResponse && (
506
- <div className={`px-4 pb-3 pt-1 ${inputContainerClassName || ''}`}>
507
- <div className="flex gap-2">
508
- <input
509
- ref={inputRef}
510
- type="text"
511
- value={inputValue}
512
- onChange={(e) => setInputValue(e.target.value)}
513
- onKeyDown={handleKeyDown}
514
- placeholder={resolvedPlaceholder}
515
- disabled={isLoading}
516
- className={`flex-1 px-3 py-2 text-sm border border-border rounded-md bg-card text-card-foreground focus:ring-2 focus:ring-info focus:border-transparent ${inputClassName || ''}`}
517
- />
518
- <Button
519
- size="sm"
520
- onClick={handleSubmit}
521
- disabled={isLoading || !inputValue.trim()}
522
- className={submitButtonClassName}
523
- >
524
- {isLoading ? '...' : resolvedSubmitLabel}
525
- </Button>
522
+ {/* Free-form input */}
523
+ {allowFreeResponse && (
524
+ <div className={`px-4 pb-3 pt-1 ${inputContainerClassName || ''}`}>
525
+ <div className="flex gap-2">
526
+ <input
527
+ ref={inputRef}
528
+ type="text"
529
+ value={inputValue}
530
+ onChange={(e) => setInputValue(e.target.value)}
531
+ onKeyDown={handleKeyDown}
532
+ placeholder={resolvedPlaceholder}
533
+ disabled={isLoading}
534
+ className={`flex-1 px-3 py-2 text-sm border border-border rounded-md bg-card text-card-foreground focus:ring-2 focus:ring-info focus:border-transparent ${inputClassName || ''}`}
535
+ />
536
+ <Button
537
+ size="sm"
538
+ onClick={handleSubmit}
539
+ disabled={isLoading || !inputValue.trim()}
540
+ className={submitButtonClassName}
541
+ >
542
+ {isLoading ? '...' : resolvedSubmitLabel}
543
+ </Button>
544
+ </div>
526
545
  </div>
527
- </div>
528
- )}
546
+ )}
547
+ </div>
529
548
  </div>
530
549
  </div>
531
550
  );