@vertesia/ui 1.6.0-dev.20261008.064737Z → 1.6.0-dev.20261014.015055Z

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 (204) hide show
  1. package/README.md +24 -0
  2. package/lib/boot/index.d.ts +12 -1
  3. package/lib/boot/index.d.ts.map +1 -1
  4. package/lib/boot/index.js +65 -10
  5. package/lib/boot/index.js.map +1 -1
  6. package/lib/boot/text.d.ts +13 -0
  7. package/lib/boot/text.d.ts.map +1 -0
  8. package/lib/boot/text.js +68 -0
  9. package/lib/boot/text.js.map +1 -0
  10. package/lib/core/components/shadcn/button.d.ts +1 -0
  11. package/lib/core/components/shadcn/button.d.ts.map +1 -1
  12. package/lib/core/components/shadcn/button.js +2 -2
  13. package/lib/core/components/shadcn/button.js.map +1 -1
  14. package/lib/core/components/shadcn/selectBox.d.ts.map +1 -1
  15. package/lib/core/components/shadcn/selectBox.js +2 -2
  16. package/lib/core/components/shadcn/selectBox.js.map +1 -1
  17. package/lib/env/index.d.ts +1 -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/PayloadBuilder.d.ts +6 -0
  22. package/lib/features/agent/PayloadBuilder.d.ts.map +1 -1
  23. package/lib/features/agent/chat/AskUserWidget.d.ts.map +1 -1
  24. package/lib/features/agent/chat/AskUserWidget.js +2 -1
  25. package/lib/features/agent/chat/AskUserWidget.js.map +1 -1
  26. package/lib/features/agent/chat/ModernAgentConversation.js +6 -4
  27. package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
  28. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
  29. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +2 -253
  30. package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
  31. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts +1 -3
  32. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.d.ts.map +1 -1
  33. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js +2 -8
  34. package/lib/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.js.map +1 -1
  35. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.d.ts.map +1 -1
  36. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js +58 -0
  37. package/lib/features/agent/chat/ModernAgentOutput/SummaryConversation.js.map +1 -1
  38. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.d.ts +4 -0
  39. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.d.ts.map +1 -0
  40. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.js +248 -0
  41. package/lib/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.js.map +1 -0
  42. package/lib/features/agent/chat/SkillWidgetProvider.d.ts.map +1 -1
  43. package/lib/features/agent/chat/SkillWidgetProvider.js +3 -2
  44. package/lib/features/agent/chat/SkillWidgetProvider.js.map +1 -1
  45. package/lib/i18n/LanguageProvider.d.ts +9 -1
  46. package/lib/i18n/LanguageProvider.d.ts.map +1 -1
  47. package/lib/i18n/LanguageProvider.js +7 -4
  48. package/lib/i18n/LanguageProvider.js.map +1 -1
  49. package/lib/i18n/locales/ar.json +6 -1
  50. package/lib/i18n/locales/de.json +6 -1
  51. package/lib/i18n/locales/en.json +6 -1
  52. package/lib/i18n/locales/es.json +6 -1
  53. package/lib/i18n/locales/fr.json +6 -1
  54. package/lib/i18n/locales/it.json +6 -1
  55. package/lib/i18n/locales/ja.json +6 -1
  56. package/lib/i18n/locales/ko.json +6 -1
  57. package/lib/i18n/locales/pt.json +6 -1
  58. package/lib/i18n/locales/ru.json +6 -1
  59. package/lib/i18n/locales/tr.json +6 -1
  60. package/lib/i18n/locales/zh-TW.json +6 -1
  61. package/lib/i18n/locales/zh.json +6 -1
  62. package/lib/i18n/rtl.d.ts +4 -2
  63. package/lib/i18n/rtl.d.ts.map +1 -1
  64. package/lib/i18n/rtl.js +4 -2
  65. package/lib/i18n/rtl.js.map +1 -1
  66. package/lib/session/UserSessionProvider.d.ts.map +1 -1
  67. package/lib/session/UserSessionProvider.js +10 -4
  68. package/lib/session/UserSessionProvider.js.map +1 -1
  69. package/lib/session/auth/composable.d.ts +1 -5
  70. package/lib/session/auth/composable.d.ts.map +1 -1
  71. package/lib/session/auth/composable.js +1 -10
  72. package/lib/session/auth/composable.js.map +1 -1
  73. package/lib/session/auth/firebase.d.ts.map +1 -1
  74. package/lib/session/auth/firebase.js +8 -5
  75. package/lib/session/auth/firebase.js.map +1 -1
  76. package/lib/session/auth/oauth.d.ts.map +1 -1
  77. package/lib/session/auth/oauth.js +53 -25
  78. package/lib/session/auth/oauth.js.map +1 -1
  79. package/lib/session/auth/vertesiaHosts.d.ts +0 -40
  80. package/lib/session/auth/vertesiaHosts.d.ts.map +1 -1
  81. package/lib/session/auth/vertesiaHosts.js +2 -45
  82. package/lib/session/auth/vertesiaHosts.js.map +1 -1
  83. package/lib/session/index.d.ts +1 -1
  84. package/lib/session/index.d.ts.map +1 -1
  85. package/lib/session/index.js +1 -1
  86. package/lib/session/index.js.map +1 -1
  87. package/lib/session/scopeSelection.d.ts +4 -0
  88. package/lib/session/scopeSelection.d.ts.map +1 -1
  89. package/lib/session/scopeSelection.js +11 -0
  90. package/lib/session/scopeSelection.js.map +1 -1
  91. package/lib/shell/VertesiaShell.d.ts +7 -2
  92. package/lib/shell/VertesiaShell.d.ts.map +1 -1
  93. package/lib/shell/VertesiaShell.js +2 -2
  94. package/lib/shell/VertesiaShell.js.map +1 -1
  95. package/lib/shell/login/InviteAcceptModal.d.ts.map +1 -1
  96. package/lib/shell/login/InviteAcceptModal.js +83 -42
  97. package/lib/shell/login/InviteAcceptModal.js.map +1 -1
  98. package/lib/vertesia-ui-boot.js +52 -15
  99. package/lib/vertesia-ui-boot.js.map +1 -1
  100. package/lib/vertesia-ui-core.js +44 -44
  101. package/lib/vertesia-ui-core.js.map +1 -1
  102. package/lib/vertesia-ui-env.js +1 -1
  103. package/lib/vertesia-ui-env.js.map +1 -1
  104. package/lib/vertesia-ui-features.js +570 -585
  105. package/lib/vertesia-ui-features.js.map +1 -1
  106. package/lib/vertesia-ui-i18n.B1Nd8-dE.chunk.js +5 -0
  107. package/lib/vertesia-ui-i18n.B1Nd8-dE.chunk.js.map +1 -0
  108. package/lib/vertesia-ui-i18n.B2Z7mx1x.chunk.js +5 -0
  109. package/lib/vertesia-ui-i18n.B2Z7mx1x.chunk.js.map +1 -0
  110. package/lib/vertesia-ui-i18n.B756-YtS.chunk.js +5 -0
  111. package/lib/vertesia-ui-i18n.B756-YtS.chunk.js.map +1 -0
  112. package/lib/vertesia-ui-i18n.BICdgAEn.chunk.js +5 -0
  113. package/lib/vertesia-ui-i18n.BICdgAEn.chunk.js.map +1 -0
  114. package/lib/vertesia-ui-i18n.B_yx0c2A.chunk.js +5 -0
  115. package/lib/vertesia-ui-i18n.B_yx0c2A.chunk.js.map +1 -0
  116. package/lib/vertesia-ui-i18n.BwPrGiQn.chunk.js +5 -0
  117. package/lib/vertesia-ui-i18n.BwPrGiQn.chunk.js.map +1 -0
  118. package/lib/vertesia-ui-i18n.CfQbOHm3.chunk.js +5 -0
  119. package/lib/vertesia-ui-i18n.CfQbOHm3.chunk.js.map +1 -0
  120. package/lib/vertesia-ui-i18n.DMISlF16.chunk.js +5 -0
  121. package/lib/vertesia-ui-i18n.DMISlF16.chunk.js.map +1 -0
  122. package/lib/vertesia-ui-i18n.D_WA36Yq.chunk.js +5 -0
  123. package/lib/vertesia-ui-i18n.D_WA36Yq.chunk.js.map +1 -0
  124. package/lib/vertesia-ui-i18n.Dp3KuMs-.chunk.js +5 -0
  125. package/lib/vertesia-ui-i18n.Dp3KuMs-.chunk.js.map +1 -0
  126. package/lib/vertesia-ui-i18n.DsioKoQ3.chunk.js +5 -0
  127. package/lib/vertesia-ui-i18n.DsioKoQ3.chunk.js.map +1 -0
  128. package/lib/vertesia-ui-i18n.QEIqEPlv.chunk.js +5 -0
  129. package/lib/vertesia-ui-i18n.QEIqEPlv.chunk.js.map +1 -0
  130. package/lib/vertesia-ui-i18n.js +2 -2
  131. package/lib/vertesia-ui-i18n.js.map +1 -1
  132. package/lib/vertesia-ui-layout.js +15 -15
  133. package/lib/vertesia-ui-layout.js.map +1 -1
  134. package/lib/vertesia-ui-rich-text.js +44 -44
  135. package/lib/vertesia-ui-rich-text.js.map +1 -1
  136. package/lib/vertesia-ui-session.js +1 -1
  137. package/lib/vertesia-ui-session.js.map +1 -1
  138. package/lib/vertesia-ui-shell.js +51 -51
  139. package/lib/vertesia-ui-shell.js.map +1 -1
  140. package/lib/vertesia-ui-widgets.js +51 -51
  141. package/lib/vertesia-ui-widgets.js.map +1 -1
  142. package/package.json +20 -20
  143. package/src/boot/index.ts +74 -11
  144. package/src/boot/text.ts +80 -0
  145. package/src/core/components/shadcn/button.tsx +3 -2
  146. package/src/core/components/shadcn/selectBox.tsx +12 -4
  147. package/src/css/index.css +1 -1
  148. package/src/css/typography.css +111 -88
  149. package/src/env/index.ts +0 -2
  150. package/src/features/agent/chat/AskUserWidget.tsx +3 -1
  151. package/src/features/agent/chat/ModernAgentConversation.tsx +7 -4
  152. package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +2 -253
  153. package/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx +2 -8
  154. package/src/features/agent/chat/ModernAgentOutput/SummaryConversation.ts +60 -0
  155. package/src/features/agent/chat/ModernAgentOutput/agentMarkdownStyles.ts +248 -0
  156. package/src/features/agent/chat/SkillWidgetProvider.tsx +3 -2
  157. package/src/i18n/LanguageProvider.tsx +16 -4
  158. package/src/i18n/locales/ar.json +6 -1
  159. package/src/i18n/locales/de.json +6 -1
  160. package/src/i18n/locales/en.json +6 -1
  161. package/src/i18n/locales/es.json +6 -1
  162. package/src/i18n/locales/fr.json +6 -1
  163. package/src/i18n/locales/it.json +6 -1
  164. package/src/i18n/locales/ja.json +6 -1
  165. package/src/i18n/locales/ko.json +6 -1
  166. package/src/i18n/locales/pt.json +6 -1
  167. package/src/i18n/locales/ru.json +6 -1
  168. package/src/i18n/locales/tr.json +6 -1
  169. package/src/i18n/locales/zh-TW.json +6 -1
  170. package/src/i18n/locales/zh.json +6 -1
  171. package/src/i18n/rtl.ts +5 -2
  172. package/src/session/UserSessionProvider.tsx +11 -4
  173. package/src/session/auth/composable.ts +1 -11
  174. package/src/session/auth/firebase.ts +9 -6
  175. package/src/session/auth/oauth.ts +65 -25
  176. package/src/session/auth/vertesiaHosts.ts +2 -47
  177. package/src/session/index.ts +1 -1
  178. package/src/session/scopeSelection.ts +13 -0
  179. package/src/shell/VertesiaShell.tsx +8 -2
  180. package/src/shell/login/InviteAcceptModal.tsx +112 -65
  181. package/lib/vertesia-ui-i18n.B0orbJb6.chunk.js +0 -5
  182. package/lib/vertesia-ui-i18n.B0orbJb6.chunk.js.map +0 -1
  183. package/lib/vertesia-ui-i18n.BChwIQVA.chunk.js +0 -5
  184. package/lib/vertesia-ui-i18n.BChwIQVA.chunk.js.map +0 -1
  185. package/lib/vertesia-ui-i18n.BTEcAVQm.chunk.js +0 -5
  186. package/lib/vertesia-ui-i18n.BTEcAVQm.chunk.js.map +0 -1
  187. package/lib/vertesia-ui-i18n.BeJQKbv7.chunk.js +0 -5
  188. package/lib/vertesia-ui-i18n.BeJQKbv7.chunk.js.map +0 -1
  189. package/lib/vertesia-ui-i18n.BpGr0-5g.chunk.js +0 -5
  190. package/lib/vertesia-ui-i18n.BpGr0-5g.chunk.js.map +0 -1
  191. package/lib/vertesia-ui-i18n.Bs8x-1wI.chunk.js +0 -5
  192. package/lib/vertesia-ui-i18n.Bs8x-1wI.chunk.js.map +0 -1
  193. package/lib/vertesia-ui-i18n.BymQoOm0.chunk.js +0 -5
  194. package/lib/vertesia-ui-i18n.BymQoOm0.chunk.js.map +0 -1
  195. package/lib/vertesia-ui-i18n.CNT_wabV.chunk.js +0 -5
  196. package/lib/vertesia-ui-i18n.CNT_wabV.chunk.js.map +0 -1
  197. package/lib/vertesia-ui-i18n.Cxh1iVk0.chunk.js +0 -5
  198. package/lib/vertesia-ui-i18n.Cxh1iVk0.chunk.js.map +0 -1
  199. package/lib/vertesia-ui-i18n.DLYCM9fV.chunk.js +0 -5
  200. package/lib/vertesia-ui-i18n.DLYCM9fV.chunk.js.map +0 -1
  201. package/lib/vertesia-ui-i18n.QvAVguDs.chunk.js +0 -5
  202. package/lib/vertesia-ui-i18n.QvAVguDs.chunk.js.map +0 -1
  203. package/lib/vertesia-ui-i18n.smozeSmI.chunk.js +0 -5
  204. package/lib/vertesia-ui-i18n.smozeSmI.chunk.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertesia/ui",
3
- "version": "1.6.0-dev.20261008.064737Z",
3
+ "version": "1.6.0-dev.20261014.015055Z",
4
4
  "description": "Vertesia UI components and and hooks",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -47,11 +47,11 @@
47
47
  "@radix-ui/react-switch": "^1.3.7",
48
48
  "@radix-ui/react-tabs": "^1.1.21",
49
49
  "@radix-ui/react-tooltip": "^1.2.16",
50
- "@vertesia/client": "1.6.0-dev.20261008.064737Z",
51
- "@vertesia/common": "1.6.0-dev.20261008.064737Z",
52
- "@vertesia/fusion-ux": "1.6.0-dev.20261008.064737Z",
53
- "@vertesia/json": "1.6.0-dev.20261008.064737Z",
54
- "@vertesia/rich-text": "1.6.0-dev.20261008.064737Z",
50
+ "@vertesia/client": "1.6.0-dev.20261014.015055Z",
51
+ "@vertesia/common": "1.6.0-dev.20261014.015055Z",
52
+ "@vertesia/fusion-ux": "1.6.0-dev.20261014.015055Z",
53
+ "@vertesia/json": "1.6.0-dev.20261014.015055Z",
54
+ "@vertesia/rich-text": "1.6.0-dev.20261014.015055Z",
55
55
  "ajv": "^8.20.0",
56
56
  "ajv-formats": "^3.0.1",
57
57
  "aria-hidden": "^1.2.6",
@@ -63,15 +63,15 @@
63
63
  "dompurify": "^3.4.16",
64
64
  "fast-xml-parser": "^5.11.2",
65
65
  "firebase": "^12.19.0",
66
- "framer-motion": "^13.4.4",
66
+ "framer-motion": "^13.5.1",
67
67
  "i18next": "^26.4.2",
68
68
  "jwt-decode": "^4.0.0",
69
- "katex": "^0.18.9",
69
+ "katex": "^0.19.0",
70
70
  "lodash-es": "^4.18.1",
71
- "lucide-react": "^1.48.0",
72
- "mermaid": "^12.0.0",
71
+ "lucide-react": "^1.52.0",
72
+ "mermaid": "^12.1.0",
73
73
  "monaco-editor": "^0.57.0",
74
- "motion": "^13.4.4",
74
+ "motion": "^13.5.1",
75
75
  "papaparse": "^5.7.0",
76
76
  "react": "19.3.0",
77
77
  "react-calendar": "^6.0.1",
@@ -83,11 +83,11 @@
83
83
  "react-pdf": "^10.5.0",
84
84
  "react-remove-scroll": "^2.7.2",
85
85
  "react-remove-scroll-bar": "^2.3.8",
86
- "react-resizable-panels": "^4.14.1",
86
+ "react-resizable-panels": "^4.14.2",
87
87
  "react-style-singleton": "^2.2.3",
88
88
  "react-vega": "^8.0.0",
89
89
  "rehype-katex": "^7.0.1",
90
- "remark-definition-list": "^2.0.0",
90
+ "remark-definition-list": "^2.0.1",
91
91
  "remark-directive": "^4.0.0",
92
92
  "remark-gfm": "^4.0.1",
93
93
  "remark-github-blockquote-alert": "^2.1.0",
@@ -105,19 +105,19 @@
105
105
  "@testing-library/user-event": "^14.6.7",
106
106
  "@types/hast": "^3.0.5",
107
107
  "@types/lodash-es": "^4.17.12",
108
- "@types/node": "^24.19.0",
108
+ "@types/node": "^24.19.1",
109
109
  "@types/papaparse": "^5.5.2",
110
110
  "@types/react": "19.3.0",
111
111
  "@types/react-dom": "19.3.0",
112
112
  "@vertesia/tsconfig": "0.1.0",
113
- "axe-core": "^4.13.0",
113
+ "axe-core": "^4.14.0",
114
114
  "culori": "^4.0.2",
115
- "i18next-cli": "^1.74.2",
116
- "jsdom": "^30.1.1",
117
- "rolldown": "1.2.11",
115
+ "i18next-cli": "^1.74.8",
116
+ "jsdom": "^30.1.2",
117
+ "rolldown": "1.2.12",
118
118
  "tailwindcss": "^4.3.3",
119
119
  "typescript": "7.0.2",
120
- "vitest": "5.0.2",
120
+ "vitest": "5.0.3",
121
121
  "vitest-axe": "^0.1.0",
122
122
  "wcag-contrast": "^3.0.0"
123
123
  },
@@ -193,7 +193,7 @@
193
193
  "url": "https://github.com/vertesia/composableai.git",
194
194
  "directory": "packages/ui"
195
195
  },
196
- "gitHead": "8ee3a33e9a7c3dbcd13937e971cbdcc5fad56717",
196
+ "gitHead": "468240ecf939f968e1324116be26386cdf5ada49",
197
197
  "scripts": {
198
198
  "clean:lib": "rimraf ./lib ./tsconfig.tsbuildinfo",
199
199
  "clean": "rimraf ./node_modules ./lib ./tsconfig.tsbuildinfo",
package/src/boot/index.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import { isRTL, LANGUAGE_STORAGE_KEY, SUPPORTED_LANGUAGES, type SupportedLanguage } from '../i18n/rtl.js';
1
2
  import { vertesiaBranding } from './branding.js';
2
3
  import { LOADING_ICON_SIZE, LOADING_INDICATOR_STYLES } from './loading.js';
4
+ import { BOOT_TEXT, type BootText } from './text.js';
3
5
 
4
6
  export interface BootScreenOptions {
5
7
  storageKey?: string;
@@ -8,6 +10,12 @@ export interface BootScreenOptions {
8
10
  slowLoadingLabel?: string;
9
11
  reloadLabel?: string;
10
12
  loadingLabel?: string;
13
+ /**
14
+ * Language of the boot copy. `'auto'` (the default) resolves it the way `LanguageProvider` does:
15
+ * the saved choice, then the browser language, then English. Pin a language when the app's
16
+ * React tree pins one, so the boot screen and the app agree.
17
+ */
18
+ language?: SupportedLanguage | 'auto';
11
19
  autoStart?: boolean;
12
20
  /** Trusted, app-authored HTML inside the loading container. Never pass user content. */
13
21
  html?: string;
@@ -22,7 +30,6 @@ export interface BootScreenHtmlPlugin {
22
30
 
23
31
  const DEFAULT_STORAGE_KEY = 'vite-ui-theme';
24
32
  const DEFAULT_ICON_SRC = vertesiaBranding.loadingIcon.light;
25
- const DEFAULT_LOADING_LABEL = 'Loading';
26
33
 
27
34
  export const BOOT_SCREEN_STYLES = `
28
35
  ${LOADING_INDICATOR_STYLES}
@@ -103,44 +110,95 @@ ${LOADING_INDICATOR_STYLES}
103
110
  }
104
111
  `;
105
112
 
106
- function inlineJson(value: string): string {
113
+ function inlineJson(value: unknown): string {
107
114
  return JSON.stringify(value).replaceAll('<', '\\u003c');
108
115
  }
109
116
 
110
117
  function normalizedOptions(options: BootScreenOptions) {
111
118
  return {
119
+ ...options,
112
120
  storageKey: options.storageKey ?? DEFAULT_STORAGE_KEY,
113
121
  iconSrc: options.iconSrc ?? DEFAULT_ICON_SRC,
114
- loadingLabel: options.loadingLabel ?? DEFAULT_LOADING_LABEL,
122
+ language: options.language ?? 'auto',
115
123
  autoStart: options.autoStart ?? true,
116
- html: options.html,
117
- styles: options.styles,
118
124
  };
119
125
  }
120
126
 
121
- /** Default markup shared by Studio and configurable applications. */
127
+ /** An app-supplied label wins in every language. */
128
+ function bootText(options: BootScreenOptions, language: SupportedLanguage): BootText {
129
+ return {
130
+ loading: options.loadingLabel ?? BOOT_TEXT[language].loading,
131
+ slowLoading: options.slowLoadingLabel ?? BOOT_TEXT[language].slowLoading,
132
+ reload: options.reloadLabel ?? BOOT_TEXT[language].reload,
133
+ };
134
+ }
135
+
136
+ /** The copy of every language the runtime may pick: only the pinned one, or all of them. */
137
+ function bootTexts(options: BootScreenOptions): Partial<Record<SupportedLanguage, BootText>> {
138
+ const languages = options.language && options.language !== 'auto' ? [options.language] : SUPPORTED_LANGUAGES;
139
+ return Object.fromEntries(languages.map((language) => [language, bootText(options, language)]));
140
+ }
141
+
142
+ /**
143
+ * Default markup shared by Studio and configurable applications. It carries the copy of the pinned
144
+ * language (English under `'auto'`); `data-boot-text` and `data-boot-alt` let the runtime swap in
145
+ * the language it resolves at page load. Custom `html` may use the same attributes.
146
+ */
122
147
  export function renderDefaultBootContent(options: BootScreenOptions = {}): string {
123
148
  const escapeMarkup = (value: string) =>
124
149
  value.replace(
125
150
  /[&<>"']/g,
126
151
  (char) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[char] ?? char,
127
152
  );
128
- const { iconSrc, loadingLabel } = normalizedOptions(options);
153
+ const { iconSrc, language } = normalizedOptions(options);
154
+ const text = bootText(options, language === 'auto' ? 'en' : language);
129
155
  const icon = (src: string, className = '') =>
130
- `<img class="vboot-spinner vertesia-loading-icon vertesia-loading-motion ${className}" width="${LOADING_ICON_SIZE}" height="${LOADING_ICON_SIZE}" src="${escapeMarkup(src)}" alt="${escapeMarkup(loadingLabel)}" />`;
156
+ `<img class="vboot-spinner vertesia-loading-icon vertesia-loading-motion ${className}" width="${LOADING_ICON_SIZE}" height="${LOADING_ICON_SIZE}" src="${escapeMarkup(src)}" alt="${escapeMarkup(text.loading)}" data-boot-alt="loading" />`;
131
157
  return `<div class="vboot-overlay" role="status" aria-live="polite">${options.darkIconSrc ? icon(iconSrc, 'vboot-icon-light') + icon(options.darkIconSrc, 'vboot-icon-dark') : icon(iconSrc)}
132
158
  <div id="loading-slow-notice" class="vboot-slow" style="display: none;">
133
- <p>${escapeMarkup(options.slowLoadingLabel ?? 'Still loading — this is taking longer than usual.')}</p>
134
- <div id="loading-slow-reload" style="display: none;"><button type="button" class="vboot-btn" data-boot-reload>${escapeMarkup(options.reloadLabel ?? 'Reload page')}</button></div></div></div>`;
159
+ <p data-boot-text="slowLoading">${escapeMarkup(text.slowLoading)}</p>
160
+ <div id="loading-slow-reload" style="display: none;"><button type="button" class="vboot-btn" data-boot-reload data-boot-text="reload">${escapeMarkup(text.reload)}</button></div></div></div>`;
135
161
  }
136
162
 
137
163
  export function renderBootScreenRuntime(options: BootScreenOptions = {}): string {
138
- const { storageKey, html } = normalizedOptions(options);
164
+ const { storageKey, html, language } = normalizedOptions(options);
139
165
  const defaultHtml = renderDefaultBootContent(options);
140
166
  return `(() => {
141
167
  const storageKey = ${inlineJson(storageKey)};
142
168
 
143
169
  const customHtml = ${html === undefined ? 'undefined' : inlineJson(html)};
170
+ const bootText = ${inlineJson(bootTexts(options))};
171
+ // Mirrors resolveLanguage and LanguageProvider's initial read (i18n/rtl.ts, i18n/LanguageProvider.tsx).
172
+ const hasText = (code) => Object.prototype.hasOwnProperty.call(bootText, code);
173
+ const resolveLanguage = (raw) => {
174
+ if (!raw) return 'en';
175
+ if (hasText(raw)) return raw;
176
+ const base = String(raw).split('-')[0];
177
+ return hasText(base) ? base : 'en';
178
+ };
179
+ const language = (() => {
180
+ const pinned = ${inlineJson(language)};
181
+ if (pinned !== 'auto') return pinned;
182
+ try {
183
+ const stored = window.localStorage.getItem(${inlineJson(LANGUAGE_STORAGE_KEY)});
184
+ if (stored) return resolveLanguage(stored);
185
+ } catch (_error) {
186
+ // Storage can be unavailable in private or restricted browsing contexts.
187
+ }
188
+ return resolveLanguage(window.navigator && window.navigator.language);
189
+ })();
190
+ const direction = ${inlineJson(SUPPORTED_LANGUAGES.filter((code) => isRTL(code)))}.includes(language) ? 'rtl' : 'ltr';
191
+ const localize = (container) => {
192
+ const text = bootText[language];
193
+ container.querySelectorAll('[data-boot-text]').forEach((node) => {
194
+ const value = text[node.getAttribute('data-boot-text')];
195
+ if (value) node.textContent = value;
196
+ });
197
+ container.querySelectorAll('[data-boot-alt]').forEach((node) => {
198
+ const value = text[node.getAttribute('data-boot-alt')];
199
+ if (value) node.setAttribute('alt', value);
200
+ });
201
+ };
144
202
  let slowNoticeTimer;
145
203
  let slowReloadTimer;
146
204
  const previewTheme = (() => {
@@ -196,7 +254,10 @@ export function renderBootScreenRuntime(options: BootScreenOptions = {}): string
196
254
  const loading = document.createElement('div');
197
255
  loading.id = 'loading-indicator';
198
256
  loading.className = bootThemeClass();
257
+ loading.lang = language;
258
+ loading.dir = direction;
199
259
  loading.innerHTML = customHtml ?? ${inlineJson(defaultHtml)};
260
+ localize(loading);
200
261
  loading.querySelectorAll('[data-boot-title]').forEach((heading) => {
201
262
  if (document.title) heading.textContent = document.title;
202
263
  });
@@ -251,6 +312,8 @@ export function renderBootScreenRuntime(options: BootScreenOptions = {}): string
251
312
  }, 30000);
252
313
  };
253
314
  window.__vertesiaBoot = {
315
+ direction,
316
+ language,
254
317
  scheduleSlowLoadingNotice,
255
318
  applyTheme,
256
319
  bootThemeClass,
@@ -0,0 +1,80 @@
1
+ import type { SupportedLanguage } from '../i18n/rtl.js';
2
+
3
+ /**
4
+ * Copy for the pre-React boot overlay. It renders before any bundle loads, so it cannot use the
5
+ * `vertesia.ui` locale files: each string is inlined into the page by the boot runtime.
6
+ */
7
+ export interface BootText {
8
+ /** Accessible name of the loading spinner. */
9
+ loading: string;
10
+ slowLoading: string;
11
+ reload: string;
12
+ }
13
+
14
+ export const BOOT_TEXT: Record<SupportedLanguage, BootText> = {
15
+ ar: {
16
+ loading: 'جارٍ التحميل',
17
+ slowLoading: 'لا يزال التحميل جاريًا — يستغرق الأمر وقتًا أطول من المعتاد.',
18
+ reload: 'إعادة تحميل الصفحة',
19
+ },
20
+ de: {
21
+ loading: 'Wird geladen',
22
+ slowLoading: 'Wird noch geladen – das dauert länger als üblich.',
23
+ reload: 'Seite neu laden',
24
+ },
25
+ en: {
26
+ loading: 'Loading',
27
+ slowLoading: 'Still loading — this is taking longer than usual.',
28
+ reload: 'Reload page',
29
+ },
30
+ es: {
31
+ loading: 'Cargando',
32
+ slowLoading: 'Todavía cargando: está tardando más de lo habitual.',
33
+ reload: 'Recargar la página',
34
+ },
35
+ fr: {
36
+ loading: 'Chargement',
37
+ slowLoading: 'Chargement en cours — cela prend plus de temps que d’habitude.',
38
+ reload: 'Recharger la page',
39
+ },
40
+ it: {
41
+ loading: 'Caricamento',
42
+ slowLoading: 'Caricamento ancora in corso: ci vuole più tempo del solito.',
43
+ reload: 'Ricarica la pagina',
44
+ },
45
+ ja: {
46
+ loading: '読み込み中',
47
+ slowLoading: '読み込み中です。通常より時間がかかっています。',
48
+ reload: 'ページを再読み込み',
49
+ },
50
+ ko: {
51
+ loading: '로드 중',
52
+ slowLoading: '아직 로드 중입니다. 평소보다 시간이 더 걸리고 있습니다.',
53
+ reload: '페이지 새로고침',
54
+ },
55
+ pt: {
56
+ loading: 'Carregando',
57
+ slowLoading: 'Ainda carregando — está demorando mais do que o normal.',
58
+ reload: 'Recarregar a página',
59
+ },
60
+ ru: {
61
+ loading: 'Загрузка',
62
+ slowLoading: 'Загрузка продолжается — это занимает больше времени, чем обычно.',
63
+ reload: 'Перезагрузить страницу',
64
+ },
65
+ tr: {
66
+ loading: 'Yükleniyor',
67
+ slowLoading: 'Hâlâ yükleniyor — bu, normalden uzun sürüyor.',
68
+ reload: 'Sayfayı yeniden yükle',
69
+ },
70
+ zh: {
71
+ loading: '正在加载',
72
+ slowLoading: '仍在加载,所需时间比平时更长。',
73
+ reload: '重新加载页面',
74
+ },
75
+ 'zh-TW': {
76
+ loading: '載入中',
77
+ slowLoading: '仍在載入,所需時間比平常更長。',
78
+ reload: '重新載入頁面',
79
+ },
80
+ };
@@ -188,6 +188,7 @@ interface CopyButtonProps {
188
188
  /** @deprecated use `aria-label` */
189
189
  alt?: string;
190
190
  'aria-label'?: string;
191
+ BtnIcon?: React.ReactNode;
191
192
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'icon';
192
193
  toast?: {
193
194
  toast: (options: { status: 'success' | 'error'; title: string; duration: number }) => void;
@@ -197,7 +198,7 @@ interface CopyButtonProps {
197
198
  }
198
199
 
199
200
  const CopyButton = React.forwardRef<HTMLButtonElement, CopyButtonProps>(
200
- ({ size, content, toast, className, alt, 'aria-label': ariaLabel, ...props }, ref) => {
201
+ ({ size, content, toast, className, alt, BtnIcon, 'aria-label': ariaLabel, ...props }, ref) => {
201
202
  const [isCopied, setIsCopied] = useState(false);
202
203
  const { t } = useUITranslation();
203
204
 
@@ -246,7 +247,7 @@ const CopyButton = React.forwardRef<HTMLButtonElement, CopyButtonProps>(
246
247
  {...props}
247
248
  title={label}
248
249
  >
249
- {isCopied ? <Check className="text-success" /> : <CopyIcon className="size-4" />}
250
+ {isCopied ? <Check className="text-success" /> : (BtnIcon ?? <CopyIcon className="size-4" />)}
250
251
  </Button>
251
252
  );
252
253
  },
@@ -287,7 +287,7 @@ export function SelectBox<T = unknown>({
287
287
  const renderOptionsContent = () => (
288
288
  <>
289
289
  {filterBy && (
290
- <div className="flex justify-start items-center mb-1">
290
+ <div className="flex justify-start items-center mb-1 shrink-0">
291
291
  <div className="mx-2">
292
292
  <SearchIcon className="size-4" />
293
293
  </div>
@@ -300,9 +300,9 @@ export function SelectBox<T = unknown>({
300
300
  />
301
301
  </div>
302
302
  )}
303
- <Command className="overflow-hidden">
303
+ <Command className={inline ? 'min-h-0 flex-1' : 'overflow-hidden'}>
304
304
  <CommandList
305
- className={inline ? 'max-h-full overflow-y-auto' : 'max-h-[200px] overflow-y-auto'}
305
+ className={inline ? 'min-h-0 flex-1 overflow-y-auto' : 'max-h-[200px] overflow-y-auto'}
306
306
  onWheel={(e) => {
307
307
  e.currentTarget.scrollTop += e.deltaY;
308
308
  }}
@@ -342,7 +342,15 @@ export function SelectBox<T = unknown>({
342
342
 
343
343
  if (inline) {
344
344
  return (
345
- <div className={clsx(className, border && 'border border-border rounded-md', 'bg-popover p-1', popupClass)}>
345
+ <div
346
+ className={clsx(
347
+ 'flex flex-col',
348
+ className,
349
+ border && 'border border-border rounded-md',
350
+ 'bg-popover p-1',
351
+ popupClass,
352
+ )}
353
+ >
346
354
  {isLoading ? (
347
355
  <div className="flex justify-center items-center p-2 text-muted text-sm">
348
356
  <LoaderCircle className="size-4 animate-spin" />
package/src/css/index.css CHANGED
@@ -15,7 +15,7 @@
15
15
  * @import "@vertesia/ui/css/theme.css";
16
16
  *
17
17
  * Typography (.vprose) is opt-in since it requires @tailwindcss/typography:
18
- * @import "@vertesia/ui/css/typography.css" layer(base);
18
+ * @import "@vertesia/ui/css/typography.css";
19
19
  */
20
20
 
21
21
  @import "./color.css" layer(base);
@@ -1,89 +1,112 @@
1
- .vprose {
2
- @apply prose text-foreground max-w-none;
3
- }
4
-
5
- .vprose p {
6
- @apply bg-transparent my-2;
7
- }
8
-
9
- .vprose a {
10
- @apply text-info hover:text-info/80;
11
- }
12
-
13
- .vprose pre,
14
- .vprose code {
15
- @apply !bg-muted text-muted my-2;
16
- }
17
-
18
- .vprose h1,
19
- .vprose h2,
20
- .vprose h3,
21
- .vprose h4,
22
- .vprose h5,
23
- .vprose h6 {
24
- @apply text-foreground;
25
- }
26
-
27
- .vprose strong {
28
- @apply text-foreground font-bold;
29
- }
30
-
31
- .vprose em {
32
- @apply italic;
33
- }
34
-
35
- .vprose ul {
36
- @apply my-3 ps-6 list-disc space-y-1;
37
- }
38
-
39
- .vprose ol {
40
- @apply my-3 ps-6 list-decimal space-y-1;
41
- }
42
-
43
- .vprose li {
44
- @apply text-mixer-foreground/50 my-2 ps-1;
45
- }
46
-
47
- .vprose blockquote {
48
- @apply border-s-4 border-muted/30 ps-4 italic text-muted my-3;
49
- }
50
-
51
- .vprose hr {
52
- @apply border-muted/30 my-4;
53
- }
54
-
55
- .vprose h1 {
56
- @apply font-bold text-2xl my-2;
57
- }
58
-
59
- .vprose h2 {
60
- @apply font-bold text-xl my-2;
61
- }
62
-
63
- .vprose h3 {
64
- @apply font-bold text-lg my-2;
65
- }
66
-
67
- .vprose table {
68
- @apply min-w-full border-collapse border border-muted/30 my-4;
69
- }
70
-
71
- .vprose th {
72
- @apply border border-muted/30 bg-mixer-muted/50 px-4 py-2 text-start text-muted font-bold;
73
- }
74
-
75
- .vprose td {
76
- @apply border border-muted/30 px-4 py-2 text-muted;
77
- }
78
-
79
- .vprose code {
80
- @apply font-mono text-sm;
81
- }
82
-
83
- .vprose :not(pre) > code {
84
- @apply px-1.5 py-0.5 rounded text-muted bg-muted/20;
85
- }
86
-
87
- .vprose .code-language-indicator {
88
- @apply text-xs px-3 py-1 rounded-t-md font-mono border-t border-s border-e -mb-2;
1
+ /* Typography is opt-in. Own the layer here so normal imports cannot accidentally put
2
+ theme colours below the typography plugin's prose utilities. */
3
+ @layer utilities {
4
+ .vprose {
5
+ @apply prose max-w-none;
6
+
7
+ /* Tailwind Typography's public palette interface. Use the active semantic tokens so
8
+ branding and dark mode share one source of truth; consumers do not need prose-invert.
9
+ https://github.com/tailwindlabs/tailwindcss-typography#adding-custom-color-themes */
10
+ --tw-prose-body: var(--foreground);
11
+ --tw-prose-headings: var(--foreground);
12
+ --tw-prose-lead: var(--muted);
13
+ --tw-prose-links: var(--info);
14
+ --tw-prose-bold: var(--foreground);
15
+ --tw-prose-counters: var(--muted);
16
+ --tw-prose-bullets: var(--muted);
17
+ --tw-prose-hr: var(--border);
18
+ --tw-prose-quotes: var(--muted);
19
+ --tw-prose-quote-borders: var(--border);
20
+ --tw-prose-captions: var(--muted);
21
+ --tw-prose-kbd: var(--foreground);
22
+ --tw-prose-kbd-shadows: var(--border);
23
+ --tw-prose-code: var(--muted);
24
+ --tw-prose-pre-code: var(--muted);
25
+ --tw-prose-pre-bg: var(--muted-background);
26
+ --tw-prose-th-borders: var(--border);
27
+ --tw-prose-td-borders: var(--border);
28
+ }
29
+
30
+ .vprose :where(p):not(:where(.not-prose, .not-prose *)) {
31
+ @apply bg-transparent my-2;
32
+ }
33
+
34
+ .vprose :where(a):not(:where(.not-prose, .not-prose *)) {
35
+ @variant hover {
36
+ color: color-mix(in oklab, var(--tw-prose-links) 80%, transparent);
37
+ }
38
+ }
39
+
40
+ .vprose :where(pre, code):not(:where(.not-prose, .not-prose *)) {
41
+ @apply my-2;
42
+ background-color: var(--tw-prose-pre-bg);
43
+ }
44
+
45
+ .vprose :where(strong):not(:where(.not-prose, .not-prose *)) {
46
+ @apply font-bold;
47
+ }
48
+
49
+ .vprose :where(em):not(:where(.not-prose, .not-prose *)) {
50
+ @apply italic;
51
+ }
52
+
53
+ .vprose :where(ul):not(:where(.not-prose, .not-prose *)) {
54
+ @apply my-3 ps-6 list-disc space-y-1;
55
+ }
56
+
57
+ .vprose :where(ol):not(:where(.not-prose, .not-prose *)) {
58
+ @apply my-3 ps-6 list-decimal space-y-1;
59
+ }
60
+
61
+ .vprose :where(li):not(:where(.not-prose, .not-prose *)) {
62
+ @apply my-2 ps-1;
63
+ }
64
+
65
+ .vprose :where(blockquote):not(:where(.not-prose, .not-prose *)) {
66
+ @apply border-s-4 ps-4 italic my-3;
67
+ }
68
+
69
+ .vprose :where(hr):not(:where(.not-prose, .not-prose *)) {
70
+ @apply my-4;
71
+ }
72
+
73
+ .vprose :where(h1):not(:where(.not-prose, .not-prose *)) {
74
+ @apply font-bold text-2xl my-2;
75
+ }
76
+
77
+ .vprose :where(h2):not(:where(.not-prose, .not-prose *)) {
78
+ @apply font-bold text-xl my-2;
79
+ }
80
+
81
+ .vprose :where(h3):not(:where(.not-prose, .not-prose *)) {
82
+ @apply font-bold text-lg my-2;
83
+ }
84
+
85
+ .vprose :where(table):not(:where(.not-prose, .not-prose *)) {
86
+ @apply min-w-full border-collapse border my-4;
87
+ border-color: var(--tw-prose-td-borders);
88
+ }
89
+
90
+ .vprose :where(th):not(:where(.not-prose, .not-prose *)) {
91
+ @apply border px-4 py-2 text-start font-bold;
92
+ border-color: var(--tw-prose-th-borders);
93
+ background-color: var(--muted-background);
94
+ }
95
+
96
+ .vprose :where(td):not(:where(.not-prose, .not-prose *)) {
97
+ @apply border px-4 py-2;
98
+ border-color: var(--tw-prose-td-borders);
99
+ }
100
+
101
+ .vprose :where(code):not(:where(.not-prose, .not-prose *)) {
102
+ @apply font-mono text-sm;
103
+ }
104
+
105
+ .vprose :where(:not(pre) > code):not(:where(.not-prose, .not-prose *)) {
106
+ @apply px-1.5 py-0.5 rounded;
107
+ }
108
+
109
+ .vprose :where(.code-language-indicator):not(:where(.not-prose, .not-prose *)) {
110
+ @apply text-xs px-3 py-1 rounded-t-md font-mono border-t border-s border-e -mb-2;
111
+ }
89
112
  }
package/src/env/index.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  import { isTrustedAuthBrokerUrl } from '../session/auth/vertesiaHosts.js';
2
2
 
3
3
  export {
4
- FIRST_PARTY_HOST_PATTERNS,
5
- GATEWAY_HOST_PATTERNS,
6
4
  isLoopbackHostname,
7
5
  isTrustedAuthBrokerUrl,
8
6
  normalizeHostname,
@@ -169,7 +169,6 @@ export function AskUserWidget({
169
169
  submitButtonClassName,
170
170
  }: AskUserWidgetProps) {
171
171
  const { t } = useUITranslation();
172
- const resolvedPlaceholder = placeholder ?? t('agent.typeYourResponse');
173
172
  const resolvedSubmitLabel = submitLabel ?? t('agent.send');
174
173
  const [inputValue, setInputValue] = React.useState('');
175
174
  const [selectedOptions, setSelectedOptions] = React.useState<Set<string>>(new Set());
@@ -179,6 +178,9 @@ export function AskUserWidget({
179
178
  const DefaultIcon = VARIANT_ICONS[variant];
180
179
  const validOptions = isAskUserOptions(options);
181
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'));
182
184
  const invalidOptionsReported = React.useRef(false);
183
185
 
184
186
  React.useEffect(() => {
@@ -1397,10 +1397,13 @@ function StartWorkflowView({
1397
1397
  rows={2}
1398
1398
  className={cn(
1399
1399
  'min-h-[72px] resize-none border-0 bg-transparent px-0 py-0 text-sm leading-6 shadow-none focus-visible:ring-0',
1400
- // The expanded composer is a fixed-height flex box (adjustTextareaHeight
1401
- // pins it to 100%), so it has to scroll its own overflow. The auto-growing
1402
- // variant hides overflow because it resizes to fit instead.
1403
- isExpandedStartComposer ? 'min-h-0 flex-1 overflow-y-auto' : 'overflow-hidden',
1400
+ // Both variants scroll their own overflow: the expanded composer is a
1401
+ // fixed-height flex box (adjustTextareaHeight pins it to 100%), and the
1402
+ // auto-growing one stops growing at 200px. Hiding overflow there left long
1403
+ // text unscrollable, so the wheel scrolled the surrounding page instead.
1404
+ // overscroll-contain keeps that from happening at the ends of the text too.
1405
+ 'overflow-y-auto overscroll-contain',
1406
+ isExpandedStartComposer && 'min-h-0 flex-1',
1404
1407
  inputClassName,
1405
1408
  )}
1406
1409
  style={