@data-club/ai-hub 0.0.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.
Files changed (344) hide show
  1. package/dist/auth/passwordPolicy.d.ts +55 -0
  2. package/dist/auth/passwordPolicy.d.ts.map +1 -0
  3. package/dist/auth/passwordPolicy.js +63 -0
  4. package/dist/auth/passwordPolicy.js.map +1 -0
  5. package/dist/auth/texts.d.ts +815 -0
  6. package/dist/auth/texts.d.ts.map +1 -0
  7. package/dist/auth/texts.js +429 -0
  8. package/dist/auth/texts.js.map +1 -0
  9. package/dist/client/AIHubClient.d.ts +1058 -0
  10. package/dist/client/AIHubClient.d.ts.map +1 -0
  11. package/dist/client/AIHubClient.js +950 -0
  12. package/dist/client/AIHubClient.js.map +1 -0
  13. package/dist/client/index.d.ts +11 -0
  14. package/dist/client/index.d.ts.map +1 -0
  15. package/dist/client/index.js +9 -0
  16. package/dist/client/index.js.map +1 -0
  17. package/dist/client/sseParser.d.ts +41 -0
  18. package/dist/client/sseParser.d.ts.map +1 -0
  19. package/dist/client/sseParser.js +153 -0
  20. package/dist/client/sseParser.js.map +1 -0
  21. package/dist/client/tokenStorage.d.ts +56 -0
  22. package/dist/client/tokenStorage.d.ts.map +1 -0
  23. package/dist/client/tokenStorage.js +116 -0
  24. package/dist/client/tokenStorage.js.map +1 -0
  25. package/dist/components/AgentsPicker/index.d.ts +70 -0
  26. package/dist/components/AgentsPicker/index.d.ts.map +1 -0
  27. package/dist/components/AgentsPicker/index.js +63 -0
  28. package/dist/components/AgentsPicker/index.js.map +1 -0
  29. package/dist/components/AuthGate/index.d.ts +79 -0
  30. package/dist/components/AuthGate/index.d.ts.map +1 -0
  31. package/dist/components/AuthGate/index.js +61 -0
  32. package/dist/components/AuthGate/index.js.map +1 -0
  33. package/dist/components/CacheBadge/index.d.ts +88 -0
  34. package/dist/components/CacheBadge/index.d.ts.map +1 -0
  35. package/dist/components/CacheBadge/index.js +127 -0
  36. package/dist/components/CacheBadge/index.js.map +1 -0
  37. package/dist/components/ChangePassword/index.d.ts +109 -0
  38. package/dist/components/ChangePassword/index.d.ts.map +1 -0
  39. package/dist/components/ChangePassword/index.js +142 -0
  40. package/dist/components/ChangePassword/index.js.map +1 -0
  41. package/dist/components/ChatShell/ShellHeader.d.ts +44 -0
  42. package/dist/components/ChatShell/ShellHeader.d.ts.map +1 -0
  43. package/dist/components/ChatShell/ShellHeader.js +14 -0
  44. package/dist/components/ChatShell/ShellHeader.js.map +1 -0
  45. package/dist/components/ChatShell/UserMenu.d.ts +48 -0
  46. package/dist/components/ChatShell/UserMenu.d.ts.map +1 -0
  47. package/dist/components/ChatShell/UserMenu.js +86 -0
  48. package/dist/components/ChatShell/UserMenu.js.map +1 -0
  49. package/dist/components/ChatShell/index.d.ts +98 -0
  50. package/dist/components/ChatShell/index.d.ts.map +1 -0
  51. package/dist/components/ChatShell/index.js +124 -0
  52. package/dist/components/ChatShell/index.js.map +1 -0
  53. package/dist/components/DataClubAIHub/index.d.ts +132 -0
  54. package/dist/components/DataClubAIHub/index.d.ts.map +1 -0
  55. package/dist/components/DataClubAIHub/index.js +124 -0
  56. package/dist/components/DataClubAIHub/index.js.map +1 -0
  57. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts +89 -0
  58. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts.map +1 -0
  59. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js +199 -0
  60. package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js.map +1 -0
  61. package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts +33 -0
  62. package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts.map +1 -0
  63. package/dist/components/DataClubAIHubAdmin/AgentsPage.js +137 -0
  64. package/dist/components/DataClubAIHubAdmin/AgentsPage.js.map +1 -0
  65. package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts +50 -0
  66. package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts.map +1 -0
  67. package/dist/components/DataClubAIHubAdmin/CreateUserModal.js +93 -0
  68. package/dist/components/DataClubAIHubAdmin/CreateUserModal.js.map +1 -0
  69. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts +49 -0
  70. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts.map +1 -0
  71. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js +94 -0
  72. package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js.map +1 -0
  73. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts +37 -0
  74. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts.map +1 -0
  75. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js +44 -0
  76. package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js.map +1 -0
  77. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts +56 -0
  78. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts.map +1 -0
  79. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js +158 -0
  80. package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js.map +1 -0
  81. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts +29 -0
  82. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts.map +1 -0
  83. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js +183 -0
  84. package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js.map +1 -0
  85. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts +38 -0
  86. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts.map +1 -0
  87. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js +294 -0
  88. package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js.map +1 -0
  89. package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts +36 -0
  90. package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts.map +1 -0
  91. package/dist/components/DataClubAIHubAdmin/SettingsPage.js +373 -0
  92. package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -0
  93. package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts +47 -0
  94. package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts.map +1 -0
  95. package/dist/components/DataClubAIHubAdmin/UsersPage.js +233 -0
  96. package/dist/components/DataClubAIHubAdmin/UsersPage.js.map +1 -0
  97. package/dist/components/DataClubAIHubAdmin/index.d.ts +87 -0
  98. package/dist/components/DataClubAIHubAdmin/index.d.ts.map +1 -0
  99. package/dist/components/DataClubAIHubAdmin/index.js +137 -0
  100. package/dist/components/DataClubAIHubAdmin/index.js.map +1 -0
  101. package/dist/components/DataClubAIHubLogin/index.d.ts +113 -0
  102. package/dist/components/DataClubAIHubLogin/index.d.ts.map +1 -0
  103. package/dist/components/DataClubAIHubLogin/index.js +123 -0
  104. package/dist/components/DataClubAIHubLogin/index.js.map +1 -0
  105. package/dist/components/EffortControl/index.d.ts +57 -0
  106. package/dist/components/EffortControl/index.d.ts.map +1 -0
  107. package/dist/components/EffortControl/index.js +84 -0
  108. package/dist/components/EffortControl/index.js.map +1 -0
  109. package/dist/components/Layout/index.d.ts +63 -0
  110. package/dist/components/Layout/index.d.ts.map +1 -0
  111. package/dist/components/Layout/index.js +45 -0
  112. package/dist/components/Layout/index.js.map +1 -0
  113. package/dist/components/MainColumn/index.d.ts +69 -0
  114. package/dist/components/MainColumn/index.d.ts.map +1 -0
  115. package/dist/components/MainColumn/index.js +47 -0
  116. package/dist/components/MainColumn/index.js.map +1 -0
  117. package/dist/components/MarkdownRenderer/index.d.ts +66 -0
  118. package/dist/components/MarkdownRenderer/index.d.ts.map +1 -0
  119. package/dist/components/MarkdownRenderer/index.js +153 -0
  120. package/dist/components/MarkdownRenderer/index.js.map +1 -0
  121. package/dist/components/MessageInput/index.d.ts +85 -0
  122. package/dist/components/MessageInput/index.d.ts.map +1 -0
  123. package/dist/components/MessageInput/index.js +139 -0
  124. package/dist/components/MessageInput/index.js.map +1 -0
  125. package/dist/components/MessageList/index.d.ts +156 -0
  126. package/dist/components/MessageList/index.d.ts.map +1 -0
  127. package/dist/components/MessageList/index.js +352 -0
  128. package/dist/components/MessageList/index.js.map +1 -0
  129. package/dist/components/ReasoningSelector/index.d.ts +77 -0
  130. package/dist/components/ReasoningSelector/index.d.ts.map +1 -0
  131. package/dist/components/ReasoningSelector/index.js +156 -0
  132. package/dist/components/ReasoningSelector/index.js.map +1 -0
  133. package/dist/components/ReasoningView/index.d.ts +94 -0
  134. package/dist/components/ReasoningView/index.d.ts.map +1 -0
  135. package/dist/components/ReasoningView/index.js +83 -0
  136. package/dist/components/ReasoningView/index.js.map +1 -0
  137. package/dist/components/ScrollToPresentButton/index.d.ts +54 -0
  138. package/dist/components/ScrollToPresentButton/index.d.ts.map +1 -0
  139. package/dist/components/ScrollToPresentButton/index.js +44 -0
  140. package/dist/components/ScrollToPresentButton/index.js.map +1 -0
  141. package/dist/components/SessionsPanel/SessionTitleModal.d.ts +79 -0
  142. package/dist/components/SessionsPanel/SessionTitleModal.d.ts.map +1 -0
  143. package/dist/components/SessionsPanel/SessionTitleModal.js +102 -0
  144. package/dist/components/SessionsPanel/SessionTitleModal.js.map +1 -0
  145. package/dist/components/SessionsPanel/index.d.ts +100 -0
  146. package/dist/components/SessionsPanel/index.d.ts.map +1 -0
  147. package/dist/components/SessionsPanel/index.js +176 -0
  148. package/dist/components/SessionsPanel/index.js.map +1 -0
  149. package/dist/components/SystemNotice/index.d.ts +66 -0
  150. package/dist/components/SystemNotice/index.d.ts.map +1 -0
  151. package/dist/components/SystemNotice/index.js +81 -0
  152. package/dist/components/SystemNotice/index.js.map +1 -0
  153. package/dist/components/ThemeSwitcher/index.d.ts +66 -0
  154. package/dist/components/ThemeSwitcher/index.d.ts.map +1 -0
  155. package/dist/components/ThemeSwitcher/index.js +73 -0
  156. package/dist/components/ThemeSwitcher/index.js.map +1 -0
  157. package/dist/components/ToolCallView/formatContent.d.ts +36 -0
  158. package/dist/components/ToolCallView/formatContent.d.ts.map +1 -0
  159. package/dist/components/ToolCallView/formatContent.js +53 -0
  160. package/dist/components/ToolCallView/formatContent.js.map +1 -0
  161. package/dist/components/ToolCallView/index.d.ts +120 -0
  162. package/dist/components/ToolCallView/index.d.ts.map +1 -0
  163. package/dist/components/ToolCallView/index.js +104 -0
  164. package/dist/components/ToolCallView/index.js.map +1 -0
  165. package/dist/components/TpmBanner/index.d.ts +53 -0
  166. package/dist/components/TpmBanner/index.d.ts.map +1 -0
  167. package/dist/components/TpmBanner/index.js +116 -0
  168. package/dist/components/TpmBanner/index.js.map +1 -0
  169. package/dist/components/TurnStatusIndicator/index.d.ts +77 -0
  170. package/dist/components/TurnStatusIndicator/index.d.ts.map +1 -0
  171. package/dist/components/TurnStatusIndicator/index.js +122 -0
  172. package/dist/components/TurnStatusIndicator/index.js.map +1 -0
  173. package/dist/components/internal/PasswordInput.d.ts +62 -0
  174. package/dist/components/internal/PasswordInput.d.ts.map +1 -0
  175. package/dist/components/internal/PasswordInput.js +42 -0
  176. package/dist/components/internal/PasswordInput.js.map +1 -0
  177. package/dist/context/AIHubContext.d.ts +220 -0
  178. package/dist/context/AIHubContext.d.ts.map +1 -0
  179. package/dist/context/AIHubContext.js +19 -0
  180. package/dist/context/AIHubContext.js.map +1 -0
  181. package/dist/context/AIHubProvider.d.ts +75 -0
  182. package/dist/context/AIHubProvider.d.ts.map +1 -0
  183. package/dist/context/AIHubProvider.js +954 -0
  184. package/dist/context/AIHubProvider.js.map +1 -0
  185. package/dist/context/index.d.ts +9 -0
  186. package/dist/context/index.d.ts.map +1 -0
  187. package/dist/context/index.js +9 -0
  188. package/dist/context/index.js.map +1 -0
  189. package/dist/context/theme.d.ts +43 -0
  190. package/dist/context/theme.d.ts.map +1 -0
  191. package/dist/context/theme.js +70 -0
  192. package/dist/context/theme.js.map +1 -0
  193. package/dist/context/useAIHub.d.ts +104 -0
  194. package/dist/context/useAIHub.d.ts.map +1 -0
  195. package/dist/context/useAIHub.js +63 -0
  196. package/dist/context/useAIHub.js.map +1 -0
  197. package/dist/context/useAIHubAuth.d.ts +44 -0
  198. package/dist/context/useAIHubAuth.d.ts.map +1 -0
  199. package/dist/context/useAIHubAuth.js +35 -0
  200. package/dist/context/useAIHubAuth.js.map +1 -0
  201. package/dist/index.d.ts +76 -0
  202. package/dist/index.d.ts.map +1 -0
  203. package/dist/index.js +57 -0
  204. package/dist/index.js.map +1 -0
  205. package/dist/primitives/index.d.ts +99 -0
  206. package/dist/primitives/index.d.ts.map +1 -0
  207. package/dist/primitives/index.js +97 -0
  208. package/dist/primitives/index.js.map +1 -0
  209. package/dist/state/affordances.d.ts +182 -0
  210. package/dist/state/affordances.d.ts.map +1 -0
  211. package/dist/state/affordances.js +189 -0
  212. package/dist/state/affordances.js.map +1 -0
  213. package/dist/state/stateReducer.d.ts +72 -0
  214. package/dist/state/stateReducer.d.ts.map +1 -0
  215. package/dist/state/stateReducer.js +122 -0
  216. package/dist/state/stateReducer.js.map +1 -0
  217. package/dist/stories/EffortControl.stories.d.ts +14 -0
  218. package/dist/stories/EffortControl.stories.d.ts.map +1 -0
  219. package/dist/stories/EffortControl.stories.js +43 -0
  220. package/dist/stories/EffortControl.stories.js.map +1 -0
  221. package/dist/stories/MarkdownRenderer.stories.d.ts +25 -0
  222. package/dist/stories/MarkdownRenderer.stories.d.ts.map +1 -0
  223. package/dist/stories/MarkdownRenderer.stories.js +178 -0
  224. package/dist/stories/MarkdownRenderer.stories.js.map +1 -0
  225. package/dist/stories/MessageBubble.stories.d.ts +21 -0
  226. package/dist/stories/MessageBubble.stories.d.ts.map +1 -0
  227. package/dist/stories/MessageBubble.stories.js +59 -0
  228. package/dist/stories/MessageBubble.stories.js.map +1 -0
  229. package/dist/stories/ReasoningView.stories.d.ts +18 -0
  230. package/dist/stories/ReasoningView.stories.d.ts.map +1 -0
  231. package/dist/stories/ReasoningView.stories.js +62 -0
  232. package/dist/stories/ReasoningView.stories.js.map +1 -0
  233. package/dist/stories/ScrollToPresentButton.stories.d.ts +18 -0
  234. package/dist/stories/ScrollToPresentButton.stories.d.ts.map +1 -0
  235. package/dist/stories/ScrollToPresentButton.stories.js +34 -0
  236. package/dist/stories/ScrollToPresentButton.stories.js.map +1 -0
  237. package/dist/stories/SystemNotice.stories.d.ts +17 -0
  238. package/dist/stories/SystemNotice.stories.d.ts.map +1 -0
  239. package/dist/stories/SystemNotice.stories.js +42 -0
  240. package/dist/stories/SystemNotice.stories.js.map +1 -0
  241. package/dist/stories/ThemeSwitcher.stories.d.ts +15 -0
  242. package/dist/stories/ThemeSwitcher.stories.d.ts.map +1 -0
  243. package/dist/stories/ThemeSwitcher.stories.js +35 -0
  244. package/dist/stories/ThemeSwitcher.stories.js.map +1 -0
  245. package/dist/stories/ToolCallView.stories.d.ts +20 -0
  246. package/dist/stories/ToolCallView.stories.d.ts.map +1 -0
  247. package/dist/stories/ToolCallView.stories.js +65 -0
  248. package/dist/stories/ToolCallView.stories.js.map +1 -0
  249. package/dist/stories/TpmBanner.stories.d.ts +24 -0
  250. package/dist/stories/TpmBanner.stories.d.ts.map +1 -0
  251. package/dist/stories/TpmBanner.stories.js +63 -0
  252. package/dist/stories/TpmBanner.stories.js.map +1 -0
  253. package/dist/stories/TurnStatusIndicator.stories.d.ts +25 -0
  254. package/dist/stories/TurnStatusIndicator.stories.d.ts.map +1 -0
  255. package/dist/stories/TurnStatusIndicator.stories.js +56 -0
  256. package/dist/stories/TurnStatusIndicator.stories.js.map +1 -0
  257. package/dist/stories/_mockProvider.d.ts +44 -0
  258. package/dist/stories/_mockProvider.d.ts.map +1 -0
  259. package/dist/stories/_mockProvider.js +84 -0
  260. package/dist/stories/_mockProvider.js.map +1 -0
  261. package/dist/types.d.ts +787 -0
  262. package/dist/types.d.ts.map +1 -0
  263. package/dist/types.js +19 -0
  264. package/dist/types.js.map +1 -0
  265. package/dist/webcomponent/defineWebComponent.d.ts +106 -0
  266. package/dist/webcomponent/defineWebComponent.d.ts.map +1 -0
  267. package/dist/webcomponent/defineWebComponent.js +126 -0
  268. package/dist/webcomponent/defineWebComponent.js.map +1 -0
  269. package/dist/webcomponent/index.d.ts +7 -0
  270. package/dist/webcomponent/index.d.ts.map +1 -0
  271. package/dist/webcomponent/index.js +7 -0
  272. package/dist/webcomponent/index.js.map +1 -0
  273. package/package.json +63 -0
  274. package/src/__lint-canary__/step1-lint-canary.ts.disabled +28 -0
  275. package/src/auth/passwordPolicy.ts +75 -0
  276. package/src/auth/texts.ts +1294 -0
  277. package/src/client/AIHubClient.ts +1718 -0
  278. package/src/client/index.ts +30 -0
  279. package/src/client/sseParser.ts +168 -0
  280. package/src/client/tokenStorage.ts +131 -0
  281. package/src/components/AgentsPicker/index.tsx +147 -0
  282. package/src/components/AuthGate/index.tsx +132 -0
  283. package/src/components/CacheBadge/index.tsx +245 -0
  284. package/src/components/ChangePassword/index.tsx +375 -0
  285. package/src/components/ChatShell/ShellHeader.tsx +80 -0
  286. package/src/components/ChatShell/UserMenu.tsx +196 -0
  287. package/src/components/ChatShell/index.tsx +307 -0
  288. package/src/components/DataClubAIHub/index.tsx +300 -0
  289. package/src/components/DataClubAIHubAdmin/AgentEditorModal.tsx +505 -0
  290. package/src/components/DataClubAIHubAdmin/AgentsPage.tsx +342 -0
  291. package/src/components/DataClubAIHubAdmin/CreateUserModal.tsx +214 -0
  292. package/src/components/DataClubAIHubAdmin/PasswordRevealModal.tsx +204 -0
  293. package/src/components/DataClubAIHubAdmin/ResetPasswordModal.tsx +122 -0
  294. package/src/components/DataClubAIHubAdmin/RotatingMessageEditorModal.tsx +408 -0
  295. package/src/components/DataClubAIHubAdmin/RotatingMessagesPage.tsx +373 -0
  296. package/src/components/DataClubAIHubAdmin/SessionsOverviewPage.tsx +639 -0
  297. package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +979 -0
  298. package/src/components/DataClubAIHubAdmin/UsersPage.tsx +495 -0
  299. package/src/components/DataClubAIHubAdmin/index.tsx +277 -0
  300. package/src/components/DataClubAIHubLogin/index.tsx +282 -0
  301. package/src/components/EffortControl/index.tsx +138 -0
  302. package/src/components/Layout/index.tsx +92 -0
  303. package/src/components/MainColumn/index.tsx +131 -0
  304. package/src/components/MarkdownRenderer/index.tsx +256 -0
  305. package/src/components/MessageInput/index.tsx +283 -0
  306. package/src/components/MessageList/index.tsx +753 -0
  307. package/src/components/ReasoningSelector/index.tsx +268 -0
  308. package/src/components/ReasoningView/index.tsx +183 -0
  309. package/src/components/ScrollToPresentButton/index.tsx +85 -0
  310. package/src/components/SessionsPanel/SessionTitleModal.tsx +256 -0
  311. package/src/components/SessionsPanel/index.tsx +406 -0
  312. package/src/components/SystemNotice/index.tsx +151 -0
  313. package/src/components/ThemeSwitcher/index.tsx +136 -0
  314. package/src/components/ToolCallView/formatContent.ts +50 -0
  315. package/src/components/ToolCallView/index.tsx +274 -0
  316. package/src/components/TpmBanner/index.tsx +172 -0
  317. package/src/components/TurnStatusIndicator/index.tsx +203 -0
  318. package/src/components/internal/PasswordInput.tsx +145 -0
  319. package/src/context/AIHubContext.ts +249 -0
  320. package/src/context/AIHubProvider.tsx +1084 -0
  321. package/src/context/index.ts +9 -0
  322. package/src/context/theme.ts +76 -0
  323. package/src/context/useAIHub.ts +151 -0
  324. package/src/context/useAIHubAuth.ts +65 -0
  325. package/src/index.ts +275 -0
  326. package/src/primitives/index.ts +101 -0
  327. package/src/state/affordances.ts +321 -0
  328. package/src/state/stateReducer.ts +131 -0
  329. package/src/stories/EffortControl.stories.tsx +63 -0
  330. package/src/stories/MarkdownRenderer.stories.tsx +201 -0
  331. package/src/stories/MessageBubble.stories.tsx +101 -0
  332. package/src/stories/ReasoningView.stories.tsx +81 -0
  333. package/src/stories/ScrollToPresentButton.stories.tsx +52 -0
  334. package/src/stories/SystemNotice.stories.tsx +59 -0
  335. package/src/stories/ThemeSwitcher.stories.tsx +55 -0
  336. package/src/stories/ToolCallView.stories.tsx +95 -0
  337. package/src/stories/TpmBanner.stories.tsx +93 -0
  338. package/src/stories/TurnStatusIndicator.stories.tsx +86 -0
  339. package/src/stories/_mockProvider.tsx +116 -0
  340. package/src/styles/CLASSES.md +356 -0
  341. package/src/styles/default.css +2937 -0
  342. package/src/types.ts +867 -0
  343. package/src/webcomponent/defineWebComponent.tsx +193 -0
  344. package/src/webcomponent/index.ts +11 -0
@@ -0,0 +1,256 @@
1
+ /**
2
+ * `<SessionTitleModal />` — the modal used for both new-session creation
3
+ * and edit-title flow on existing sessions.
4
+ *
5
+ * Single text input ("Chat title"), `[Cancel]` / `[Create]` (new) or
6
+ * `[Cancel]` / `[Save]` (edit). Title is required and 1..80 chars —
7
+ * validated client-side before submit (the server's Zod schema enforces
8
+ * the same bounds for defense-in-depth).
9
+ *
10
+ * The modal is uncontrolled state-wise — `onSubmit` resolves with the
11
+ * server's returned `Session` on success; the caller (`SessionsPanel`)
12
+ * decides whether to close. We close on a successful submit by tying
13
+ * `open` back to the parent's state.
14
+ *
15
+ * @see SPECIFICATIONS.md §6.3.1 — sessions
16
+ * @see ui-sketches/03-chat.md — new_session_modal / edit_session_title_modal
17
+ */
18
+
19
+ import {
20
+ useEffect,
21
+ useRef,
22
+ useState,
23
+ type FormEvent,
24
+ type ReactNode,
25
+ } from "react";
26
+
27
+ import type { AIHubChatTexts } from "../../auth/texts.js";
28
+
29
+ /**
30
+ * BEM class names emitted by `<SessionTitleModal />`. Shared with the
31
+ * generic-modal styles in the default stylesheet so admin / settings
32
+ * modals layered in Steps 11+ can reuse them.
33
+ */
34
+ export interface SessionTitleModalClassNames {
35
+ /** Overlay backdrop. */
36
+ overlay: string;
37
+ /** The modal card. */
38
+ modal: string;
39
+ /** Title bar. */
40
+ title: string;
41
+ /** Form. */
42
+ form: string;
43
+ /** Field group (label + input). */
44
+ fieldGroup: string;
45
+ /** Field label. */
46
+ fieldLabel: string;
47
+ /** Text input. */
48
+ input: string;
49
+ /** Inline-validation error under the input. */
50
+ fieldError: string;
51
+ /** Button row at the bottom of the modal. */
52
+ actions: string;
53
+ /** Cancel button. */
54
+ cancelButton: string;
55
+ /** Submit button. */
56
+ submitButton: string;
57
+ }
58
+
59
+ const DEFAULT_CLASS_NAMES: SessionTitleModalClassNames = Object.freeze({
60
+ overlay: "data-club-ai-hub__modal-overlay",
61
+ modal: "data-club-ai-hub__modal",
62
+ title: "data-club-ai-hub__modal__title",
63
+ form: "data-club-ai-hub__modal__form",
64
+ fieldGroup: "data-club-ai-hub__modal__field",
65
+ fieldLabel: "data-club-ai-hub__modal__label",
66
+ input: "data-club-ai-hub__modal__input",
67
+ fieldError: "data-club-ai-hub__modal__field-error",
68
+ actions: "data-club-ai-hub__modal__actions",
69
+ cancelButton: "data-club-ai-hub__modal__cancel",
70
+ submitButton: "data-club-ai-hub__modal__confirm",
71
+ }) as SessionTitleModalClassNames;
72
+
73
+ /** The maximum title length enforced both server- and client-side. */
74
+ const TITLE_MAX = 80;
75
+
76
+ /**
77
+ * Props for `<SessionTitleModal />`.
78
+ */
79
+ export interface SessionTitleModalProps {
80
+ /** Discriminates the title + submit-button label. */
81
+ mode: "create" | "edit";
82
+ /** Pre-filled value for edit-mode; empty string for create-mode. */
83
+ initialTitle?: string;
84
+ /** Whether the modal is currently visible. */
85
+ open: boolean;
86
+ /** Called when the user dismisses without saving. */
87
+ onCancel: () => void;
88
+ /**
89
+ * Submission handler. Should `await` the server call and throw on
90
+ * failure — the modal renders the error inline and stays open.
91
+ */
92
+ onSubmit: (title: string) => Promise<void>;
93
+ /** Resolved chat texts. */
94
+ texts: AIHubChatTexts;
95
+ /** Per-element class overrides. */
96
+ classNames?: Partial<SessionTitleModalClassNames>;
97
+ }
98
+
99
+ /**
100
+ * Renders the modal. Validates the input client-side and surfaces server
101
+ * errors inline.
102
+ *
103
+ * @param props - see `SessionTitleModalProps`.
104
+ * @returns the rendered modal, or `null` when `open === false`.
105
+ */
106
+ export function SessionTitleModal(props: SessionTitleModalProps): ReactNode {
107
+ const {
108
+ mode,
109
+ initialTitle = "",
110
+ open,
111
+ onCancel,
112
+ onSubmit,
113
+ texts,
114
+ } = props;
115
+ const cls = { ...DEFAULT_CLASS_NAMES, ...(props.classNames ?? {}) };
116
+
117
+ const [title, setTitle] = useState<string>(initialTitle);
118
+ const [validationError, setValidationError] = useState<string | null>(null);
119
+ const [serverError, setServerError] = useState<string | null>(null);
120
+ const [submitting, setSubmitting] = useState(false);
121
+ const inputRef = useRef<HTMLInputElement | null>(null);
122
+
123
+ // Re-seed on open / initialTitle change so reopening for a different
124
+ // session doesn't carry stale text.
125
+ useEffect(() => {
126
+ if (open) {
127
+ setTitle(initialTitle);
128
+ setValidationError(null);
129
+ setServerError(null);
130
+ setSubmitting(false);
131
+ // Defer focus to next tick so the input is mounted.
132
+ queueMicrotask(() => inputRef.current?.focus());
133
+ }
134
+ }, [open, initialTitle]);
135
+
136
+ if (!open) return null;
137
+
138
+ const trimmed = title.trim();
139
+ const isEmpty = trimmed.length === 0;
140
+ const tooLong = trimmed.length > TITLE_MAX;
141
+ const submitDisabled = isEmpty || tooLong || submitting;
142
+
143
+ function handleSubmit(e: FormEvent<HTMLFormElement>): void {
144
+ e.preventDefault();
145
+ if (isEmpty) {
146
+ setValidationError(texts.sessionTitleRequiredError);
147
+ return;
148
+ }
149
+ if (tooLong) {
150
+ setValidationError(texts.sessionTitleTooLongError);
151
+ return;
152
+ }
153
+ setValidationError(null);
154
+ setServerError(null);
155
+ setSubmitting(true);
156
+ onSubmit(trimmed)
157
+ .catch((err: unknown) => {
158
+ const msg = err instanceof Error ? err.message : String(err);
159
+ setServerError(msg || texts.genericRequestError);
160
+ })
161
+ .finally(() => setSubmitting(false));
162
+ }
163
+
164
+ const modalTitle =
165
+ mode === "create"
166
+ ? texts.newSessionModalTitle
167
+ : texts.editSessionModalTitle;
168
+ const submitLabel =
169
+ mode === "create" ? texts.modalCreateButton : texts.modalSaveButton;
170
+
171
+ return (
172
+ <div
173
+ className={cls.overlay}
174
+ role="presentation"
175
+ data-testid="session-title-modal-overlay"
176
+ onClick={(e) => {
177
+ // Click on the backdrop (not the modal itself) cancels.
178
+ if (e.target === e.currentTarget && !submitting) onCancel();
179
+ }}
180
+ >
181
+ <div
182
+ className={cls.modal}
183
+ role="dialog"
184
+ aria-modal="true"
185
+ aria-labelledby="data-club-ai-hub__session-title-modal__heading"
186
+ data-testid="session-title-modal"
187
+ data-mode={mode}
188
+ >
189
+ <h2
190
+ id="data-club-ai-hub__session-title-modal__heading"
191
+ className={cls.title}
192
+ >
193
+ {modalTitle}
194
+ </h2>
195
+ <form className={cls.form} onSubmit={handleSubmit} noValidate>
196
+ <div className={cls.fieldGroup}>
197
+ <label className={cls.fieldLabel} htmlFor="session-title-input">
198
+ {texts.sessionTitleLabel}
199
+ </label>
200
+ <input
201
+ id="session-title-input"
202
+ ref={inputRef}
203
+ type="text"
204
+ className={cls.input}
205
+ value={title}
206
+ onChange={(e) => {
207
+ setTitle(e.target.value);
208
+ if (validationError) setValidationError(null);
209
+ }}
210
+ placeholder={texts.sessionTitlePlaceholder}
211
+ maxLength={TITLE_MAX + 16 /* let the user paste over, validate on submit */}
212
+ autoComplete="off"
213
+ disabled={submitting}
214
+ data-testid="session-title-input"
215
+ />
216
+ {validationError ? (
217
+ <p
218
+ className={cls.fieldError}
219
+ data-testid="session-title-validation-error"
220
+ >
221
+ {validationError}
222
+ </p>
223
+ ) : null}
224
+ {serverError ? (
225
+ <p
226
+ className={cls.fieldError}
227
+ data-testid="session-title-server-error"
228
+ >
229
+ {serverError}
230
+ </p>
231
+ ) : null}
232
+ </div>
233
+ <div className={cls.actions}>
234
+ <button
235
+ type="button"
236
+ className={cls.cancelButton}
237
+ onClick={onCancel}
238
+ disabled={submitting}
239
+ data-testid="session-title-cancel"
240
+ >
241
+ {texts.modalCancelButton}
242
+ </button>
243
+ <button
244
+ type="submit"
245
+ className={cls.submitButton}
246
+ disabled={submitDisabled}
247
+ data-testid="session-title-submit"
248
+ >
249
+ {submitLabel}
250
+ </button>
251
+ </div>
252
+ </form>
253
+ </div>
254
+ </div>
255
+ );
256
+ }
@@ -0,0 +1,406 @@
1
+ /**
2
+ * `<SessionsPanel />` — the left-side list of the caller's sessions for
3
+ * the currently-opened agent.
4
+ *
5
+ * Includes:
6
+ *
7
+ * - `new_session_button` → opens `SessionTitleModal` in create mode.
8
+ * - One `session_item` per row with hover-revealed `edit_title_button` +
9
+ * `archive_session_button` actions. Edit opens the same modal in
10
+ * `edit` mode pre-filled with the row's title. Archive uses a native
11
+ * `confirm()` (v1; future polish is a styled modal).
12
+ * - `archived_filter_toggle` at the bottom of the panel — flipping it
13
+ * re-fetches with `include_archived=true`. Archived rows render with
14
+ * the `--archived` modifier and their hover-actions swap to an
15
+ * Unarchive button.
16
+ * - Collapsible / resizable variants — Step 9 ships the collapsible
17
+ * reveal strip; resizable drag is layout-only and uses a CSS gutter
18
+ * the parent layout owns.
19
+ *
20
+ * **No delete action exists anywhere on this panel by design** — v1
21
+ * sessions are archive-only (§6.3.1).
22
+ *
23
+ * Exported under `AIHub.SessionsPanel`.
24
+ *
25
+ * @see SPECIFICATIONS.md §7.1 — exports
26
+ * @see SPECIFICATIONS.md §6.3.1 — sessions
27
+ * @see ui-sketches/03-chat.md — sessions_panel
28
+ */
29
+
30
+ import { useEffect, useMemo, useState, type ReactNode } from "react";
31
+
32
+ import {
33
+ resolveAIHubTexts,
34
+ type AIHubTexts,
35
+ type DeepPartial,
36
+ } from "../../auth/texts.js";
37
+ import { useAIHub } from "../../context/useAIHub.js";
38
+ import type { Session } from "../../types.js";
39
+
40
+ import { SessionTitleModal } from "./SessionTitleModal.js";
41
+
42
+ /**
43
+ * BEM class names emitted by `<SessionsPanel />`.
44
+ */
45
+ export interface SessionsPanelClassNames {
46
+ /** Outer panel container (the column). */
47
+ root: string;
48
+ /** Modifier applied when the panel is collapsed. */
49
+ rootCollapsed: string;
50
+ /** Optional custom-header slot wrapper at the top of the panel. */
51
+ customHeader: string;
52
+ /** "+ New chat" button. */
53
+ newSessionButton: string;
54
+ /** Reveal-strip rendered when collapsed. */
55
+ reveal: string;
56
+ /** Collapse / expand toggle. */
57
+ collapseToggle: string;
58
+ /** Scrollable list region. */
59
+ list: string;
60
+ /** Empty-state line ("No chats yet."). */
61
+ empty: string;
62
+ /** Loading state. */
63
+ loading: string;
64
+ /** A session-list row. */
65
+ item: string;
66
+ /** Modifier applied to the currently-selected row. */
67
+ itemActive: string;
68
+ /** Modifier applied to archived rows. */
69
+ itemArchived: string;
70
+ /** Row title text. */
71
+ itemTitle: string;
72
+ /** Row meta (relative timestamp). */
73
+ itemMeta: string;
74
+ /** Row hover-action group wrapper. */
75
+ itemActions: string;
76
+ /** A single action button (edit / archive / unarchive). */
77
+ actionButton: string;
78
+ /** Footer wrapper holding the archived-filter toggle + custom footer. */
79
+ footer: string;
80
+ /** Show-archived toggle. */
81
+ archivedToggle: string;
82
+ /** Optional custom-footer slot wrapper. */
83
+ customFooter: string;
84
+ }
85
+
86
+ const DEFAULT_CLASS_NAMES: SessionsPanelClassNames = Object.freeze({
87
+ root: "data-club-ai-hub__sessions-panel",
88
+ rootCollapsed: "data-club-ai-hub__sessions-panel--collapsed",
89
+ customHeader: "data-club-ai-hub__sessions-panel__custom-header",
90
+ newSessionButton: "data-club-ai-hub__sessions-panel__new-button",
91
+ reveal: "data-club-ai-hub__sessions-panel__reveal",
92
+ collapseToggle: "data-club-ai-hub__sessions-panel__collapse-toggle",
93
+ list: "data-club-ai-hub__sessions-panel__list",
94
+ empty: "data-club-ai-hub__sessions-panel__empty",
95
+ loading: "data-club-ai-hub__sessions-panel__loading",
96
+ item: "data-club-ai-hub__sessions-panel__item",
97
+ itemActive: "data-club-ai-hub__sessions-panel__item--active",
98
+ itemArchived: "data-club-ai-hub__sessions-panel__item--archived",
99
+ itemTitle: "data-club-ai-hub__sessions-panel__item-title",
100
+ itemMeta: "data-club-ai-hub__sessions-panel__item-meta",
101
+ itemActions: "data-club-ai-hub__sessions-panel__item-actions",
102
+ actionButton: "data-club-ai-hub__sessions-panel__action-button",
103
+ footer: "data-club-ai-hub__sessions-panel__footer",
104
+ archivedToggle: "data-club-ai-hub__sessions-panel__archived-toggle",
105
+ customFooter: "data-club-ai-hub__sessions-panel__custom-footer",
106
+ }) as SessionsPanelClassNames;
107
+
108
+ /**
109
+ * Props for `<SessionsPanel />`.
110
+ */
111
+ export interface SessionsPanelProps {
112
+ /** Whether to render the "+ New chat" button. Defaults to true. */
113
+ showCreateButton?: boolean;
114
+ /** Whether the panel can be collapsed. Defaults to true. */
115
+ collapsible?: boolean;
116
+ /** Top-of-panel slot. */
117
+ customHeader?: ReactNode;
118
+ /** Bottom-of-panel slot, rendered above the archived-filter toggle. */
119
+ customFooter?: ReactNode;
120
+ /** Extra class merged onto the root. */
121
+ className?: string;
122
+ /** Per-element class overrides. */
123
+ classNames?: Partial<SessionsPanelClassNames>;
124
+ /** Wrapper-supplied i18n. */
125
+ texts?: DeepPartial<AIHubTexts>;
126
+ }
127
+
128
+ /**
129
+ * Formats an ISO-8601 timestamp into a short "N min ago" / "yesterday" /
130
+ * "N days ago" line. Intentionally minimal — i18n libraries can take this
131
+ * over later via the `texts.*` map if needed.
132
+ *
133
+ * @param iso - ISO timestamp.
134
+ * @returns the short relative-time string.
135
+ */
136
+ function formatRelative(iso: string): string {
137
+ const then = Date.parse(iso);
138
+ if (Number.isNaN(then)) return "";
139
+ const diffMs = Date.now() - then;
140
+ const minutes = Math.round(diffMs / 60_000);
141
+ if (minutes < 1) return "just now";
142
+ if (minutes === 1) return "1 min ago";
143
+ if (minutes < 60) return `${minutes} min ago`;
144
+ const hours = Math.round(minutes / 60);
145
+ if (hours === 1) return "1 hr ago";
146
+ if (hours < 24) return `${hours} hr ago`;
147
+ const days = Math.round(hours / 24);
148
+ if (days === 1) return "yesterday";
149
+ return `${days} days ago`;
150
+ }
151
+
152
+ /**
153
+ * Renders the sessions panel.
154
+ *
155
+ * @param props - see `SessionsPanelProps`.
156
+ * @returns the rendered panel.
157
+ */
158
+ export function SessionsPanel(props: SessionsPanelProps): ReactNode {
159
+ const {
160
+ sessions,
161
+ sessionsLoading,
162
+ activeSessionId,
163
+ setActiveSession,
164
+ createSession,
165
+ patchSession,
166
+ includeArchivedSessions,
167
+ setIncludeArchivedSessions,
168
+ } = useAIHub();
169
+ const texts = resolveAIHubTexts(props.texts);
170
+ const cls = { ...DEFAULT_CLASS_NAMES, ...(props.classNames ?? {}) };
171
+ const showCreate = props.showCreateButton ?? true;
172
+ const collapsible = props.collapsible ?? true;
173
+
174
+ const [collapsed, setCollapsed] = useState(false);
175
+ const [modalOpen, setModalOpen] = useState(false);
176
+ const [editingSessionId, setEditingSessionId] = useState<string | null>(null);
177
+
178
+ const editingSession = useMemo<Session | null>(() => {
179
+ if (!editingSessionId) return null;
180
+ return sessions.find((s) => s.id === editingSessionId) ?? null;
181
+ }, [editingSessionId, sessions]);
182
+
183
+ // Stale-modal guard: if the row being edited gets archived/removed under
184
+ // us, close the modal so we don't submit edits against a stale id.
185
+ useEffect(() => {
186
+ if (editingSessionId && !editingSession) {
187
+ setEditingSessionId(null);
188
+ }
189
+ }, [editingSessionId, editingSession]);
190
+
191
+ if (collapsed) {
192
+ return (
193
+ <div
194
+ className={`${cls.root} ${cls.rootCollapsed}`}
195
+ data-testid="sessions-panel"
196
+ data-collapsed="true"
197
+ >
198
+ <button
199
+ type="button"
200
+ className={cls.reveal}
201
+ onClick={() => setCollapsed(false)}
202
+ aria-label={texts.chat.expandSessionsPanel}
203
+ data-testid="sessions-panel-reveal"
204
+ >
205
+ {/* "≫" — chevron-right glyph. SVG would be more polished, the
206
+ text glyph keeps the bundle small and is accessible via
207
+ aria-label. */}
208
+
209
+ </button>
210
+ </div>
211
+ );
212
+ }
213
+
214
+ const rootClass = [cls.root, props.className].filter(Boolean).join(" ");
215
+
216
+ async function handleArchive(session: Session): Promise<void> {
217
+ const prompt = texts.chat.archiveConfirmPrompt.replace(
218
+ "{title}",
219
+ session.title,
220
+ );
221
+ // eslint-disable-next-line no-alert
222
+ if (typeof window !== "undefined" && !window.confirm(prompt)) {
223
+ return;
224
+ }
225
+ try {
226
+ await patchSession(session.id, { isArchived: true });
227
+ } catch {
228
+ /* surfaced via provider's onError; row stays */
229
+ }
230
+ }
231
+
232
+ async function handleUnarchive(session: Session): Promise<void> {
233
+ try {
234
+ await patchSession(session.id, { isArchived: false });
235
+ } catch {
236
+ /* surfaced via provider's onError */
237
+ }
238
+ }
239
+
240
+ return (
241
+ <aside
242
+ className={rootClass}
243
+ data-testid="sessions-panel"
244
+ aria-label="Sessions"
245
+ >
246
+ {props.customHeader ? (
247
+ <div className={cls.customHeader} data-testid="sessions-panel-custom-header">
248
+ {props.customHeader}
249
+ </div>
250
+ ) : null}
251
+ {showCreate ? (
252
+ <button
253
+ type="button"
254
+ className={cls.newSessionButton}
255
+ onClick={() => {
256
+ setEditingSessionId(null);
257
+ setModalOpen(true);
258
+ }}
259
+ data-testid="sessions-panel-new-button"
260
+ >
261
+ {texts.chat.newSessionButton}
262
+ </button>
263
+ ) : null}
264
+ {collapsible ? (
265
+ <button
266
+ type="button"
267
+ className={cls.collapseToggle}
268
+ onClick={() => setCollapsed(true)}
269
+ aria-label={texts.chat.collapseSessionsPanel}
270
+ data-testid="sessions-panel-collapse"
271
+ >
272
+
273
+ </button>
274
+ ) : null}
275
+ {sessionsLoading && sessions.length === 0 ? (
276
+ <p className={cls.loading} data-testid="sessions-panel-loading">
277
+ {texts.chat.sessionsLoading}
278
+ </p>
279
+ ) : null}
280
+ {!sessionsLoading && sessions.length === 0 ? (
281
+ <p className={cls.empty} data-testid="sessions-panel-empty">
282
+ {texts.chat.sessionsEmpty}
283
+ </p>
284
+ ) : null}
285
+ <ul className={cls.list} data-testid="sessions-panel-list">
286
+ {sessions.map((session) => {
287
+ const isActive = session.id === activeSessionId;
288
+ const itemClasses = [
289
+ cls.item,
290
+ isActive ? cls.itemActive : "",
291
+ session.is_archived ? cls.itemArchived : "",
292
+ ]
293
+ .filter(Boolean)
294
+ .join(" ");
295
+ const displayTitle =
296
+ session.title.trim().length > 0
297
+ ? session.title
298
+ : texts.chat.untitledSessionFallback;
299
+ return (
300
+ <li
301
+ key={session.id}
302
+ className={itemClasses}
303
+ data-testid="session-item"
304
+ data-active={isActive ? "true" : "false"}
305
+ data-archived={session.is_archived ? "true" : "false"}
306
+ data-session-id={session.id}
307
+ >
308
+ <button
309
+ type="button"
310
+ className={cls.itemTitle}
311
+ onClick={() => setActiveSession(session.id)}
312
+ data-testid="session-item-open"
313
+ >
314
+ <span>{displayTitle}</span>
315
+ <span className={cls.itemMeta}>
316
+ {formatRelative(session.updated_at)}
317
+ </span>
318
+ </button>
319
+ <div className={cls.itemActions} data-testid="session-item-actions">
320
+ {!session.is_archived ? (
321
+ <>
322
+ <button
323
+ type="button"
324
+ className={cls.actionButton}
325
+ aria-label={texts.chat.editSessionTitleAriaLabel}
326
+ onClick={() => {
327
+ setEditingSessionId(session.id);
328
+ setModalOpen(true);
329
+ }}
330
+ data-testid="session-item-edit"
331
+ >
332
+
333
+ </button>
334
+ <button
335
+ type="button"
336
+ className={cls.actionButton}
337
+ aria-label={texts.chat.archiveSessionAriaLabel}
338
+ onClick={() => {
339
+ void handleArchive(session);
340
+ }}
341
+ data-testid="session-item-archive"
342
+ >
343
+ 🗄
344
+ </button>
345
+ </>
346
+ ) : (
347
+ <button
348
+ type="button"
349
+ className={cls.actionButton}
350
+ aria-label={texts.chat.unarchiveSessionAriaLabel}
351
+ onClick={() => {
352
+ void handleUnarchive(session);
353
+ }}
354
+ data-testid="session-item-unarchive"
355
+ >
356
+
357
+ </button>
358
+ )}
359
+ </div>
360
+ </li>
361
+ );
362
+ })}
363
+ </ul>
364
+ <div className={cls.footer}>
365
+ {props.customFooter ? (
366
+ <div
367
+ className={cls.customFooter}
368
+ data-testid="sessions-panel-custom-footer"
369
+ >
370
+ {props.customFooter}
371
+ </div>
372
+ ) : null}
373
+ <button
374
+ type="button"
375
+ className={cls.archivedToggle}
376
+ onClick={() => setIncludeArchivedSessions(!includeArchivedSessions)}
377
+ aria-pressed={includeArchivedSessions}
378
+ data-testid="sessions-panel-archived-toggle"
379
+ >
380
+ {includeArchivedSessions
381
+ ? texts.chat.hideArchivedToggle
382
+ : texts.chat.showArchivedToggle}
383
+ </button>
384
+ </div>
385
+ <SessionTitleModal
386
+ mode={editingSession ? "edit" : "create"}
387
+ open={modalOpen}
388
+ initialTitle={editingSession?.title ?? ""}
389
+ onCancel={() => {
390
+ setModalOpen(false);
391
+ setEditingSessionId(null);
392
+ }}
393
+ onSubmit={async (title) => {
394
+ if (editingSession) {
395
+ await patchSession(editingSession.id, { title });
396
+ } else {
397
+ await createSession(title);
398
+ }
399
+ setModalOpen(false);
400
+ setEditingSessionId(null);
401
+ }}
402
+ texts={texts.chat}
403
+ />
404
+ </aside>
405
+ );
406
+ }