@cryptiklemur/lattice 4.0.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (394) hide show
  1. package/bin/lattice +1 -9
  2. package/dist/client/assets/{angular-html-N8PCEquT.js → angular-html-DKTL-XDO.js} +1 -1
  3. package/dist/client/assets/{angular-ts-CJ8RJIPD.js → angular-ts-tvBzOwQR.js} +1 -1
  4. package/dist/client/assets/{apl-BD6tCLWN.js → apl-CCzl5qFl.js} +1 -1
  5. package/dist/client/assets/{astro-CpIIfBs6.js → astro-DNQTpO2Y.js} +1 -1
  6. package/dist/client/assets/{blade-D3qgnjiV.js → blade-CyJoIMeJ.js} +1 -1
  7. package/dist/client/assets/{c-Dr6ADN_t.js → c-OEwk5KN8.js} +1 -1
  8. package/dist/client/assets/{cobol-BIfDE0Hr.js → cobol-DpHyJzz2.js} +1 -1
  9. package/dist/client/assets/{coffee-DHQ57vfY.js → coffee-BX5dbDzZ.js} +1 -1
  10. package/dist/client/assets/{cpp-CEBY6JOp.js → cpp-BTBjNg2U.js} +1 -1
  11. package/dist/client/assets/{crystal-D125CSmP.js → crystal-CNzZd6DW.js} +1 -1
  12. package/dist/client/assets/{css-CBmrkYSr.js → css-BuKsNmms.js} +1 -1
  13. package/dist/client/assets/{dist-A_mCRD1f.js → dist-CKpDHMy6.js} +2 -2
  14. package/dist/client/assets/{edge-Ccsz7cJW.js → edge-DzhnGgJE.js} +1 -1
  15. package/dist/client/assets/{elixir-Do6gk14X.js → elixir-Dqs0waqF.js} +1 -1
  16. package/dist/client/assets/{elm-Db22zT4C.js → elm-BtWwjxWn.js} +1 -1
  17. package/dist/client/assets/{erb-MXVqAAJD.js → erb-iPD89b4v.js} +1 -1
  18. package/dist/client/assets/{git-rebase-B-LLWBOA.js → git-rebase-BxVNXJL4.js} +1 -1
  19. package/dist/client/assets/{glimmer-js-eWszRU73.js → glimmer-js-BlyCupwF.js} +1 -1
  20. package/dist/client/assets/{glimmer-ts-VQmwGqUp.js → glimmer-ts-DjIxWOS9.js} +1 -1
  21. package/dist/client/assets/{glsl-B8ilOfAl.js → glsl-CGIL-65r.js} +1 -1
  22. package/dist/client/assets/{graphql-DnTqxeOc.js → graphql-DeOn6mNV.js} +1 -1
  23. package/dist/client/assets/{hack-XJsHYSQb.js → hack-DVppeCmS.js} +1 -1
  24. package/dist/client/assets/{haml-CQ7Vqzwp.js → haml-WDhua0Mp.js} +1 -1
  25. package/dist/client/assets/{handlebars-C4szooBf.js → handlebars-i2Fu_9HI.js} +1 -1
  26. package/dist/client/assets/{html-B6EgAiSd.js → html-B1e6oxzK.js} +1 -1
  27. package/dist/client/assets/{html-derivative-DdinogQX.js → html-derivative-MofKXIVd.js} +1 -1
  28. package/dist/client/assets/{http-BSLxCgRq.js → http-Dk6S5pRD.js} +1 -1
  29. package/dist/client/assets/{hurl-pOsTwNfp.js → hurl-CroFYYJG.js} +1 -1
  30. package/dist/client/assets/{index-BHQ_8mvl.js → index-CVu-S6Yk.js} +2 -2
  31. package/dist/client/assets/{java-DRQLiiST.js → java-B89FYjqS.js} +1 -1
  32. package/dist/client/assets/{javascript-DvEK2-47.js → javascript-m6CO1Uiy.js} +1 -1
  33. package/dist/client/assets/{jinja-D2NYJ25y.js → jinja-DC9Wi41X.js} +1 -1
  34. package/dist/client/assets/{jison-DDZaLNAp.js → jison-6xiegwDk.js} +1 -1
  35. package/dist/client/assets/{json-TGR0NIWd.js → json-HA-96-qr.js} +1 -1
  36. package/dist/client/assets/{jsx-BjUoPYga.js → jsx-Wt1a8i8U.js} +1 -1
  37. package/dist/client/assets/{julia-C4gjSpFu.js → julia-7M93VBON.js} +1 -1
  38. package/dist/client/assets/{just-H351x5u_.js → just-CjfDLYLv.js} +1 -1
  39. package/dist/client/assets/{latex-BiTmf6gf.js → latex-CitsJ46x.js} +1 -1
  40. package/dist/client/assets/{liquid-86ufjRy-.js → liquid-C8VIFin8.js} +1 -1
  41. package/dist/client/assets/{lua-BNxR0F_8.js → lua-Ba2N7esc.js} +1 -1
  42. package/dist/client/assets/{marko-CvRxpRjM.js → marko-lTLvb2wu.js} +1 -1
  43. package/dist/client/assets/{mdc-CYbAIy2C.js → mdc-D6IV-8FD.js} +1 -1
  44. package/dist/client/assets/{nginx-egdgMq-F.js → nginx-Ch5AjE6S.js} +1 -1
  45. package/dist/client/assets/{nim-CXBJVz_w.js → nim-WmDDC6LW.js} +1 -1
  46. package/dist/client/assets/{perl-XRfMobzg.js → perl-CQv0gYuq.js} +1 -1
  47. package/dist/client/assets/{php-Br7a8uil.js → php-BJmH0qOB.js} +1 -1
  48. package/dist/client/assets/{pug-BVbbUVvy.js → pug-CsHPkzc9.js} +1 -1
  49. package/dist/client/assets/{qml-ByKvrL1j.js → qml-B36ecArG.js} +1 -1
  50. package/dist/client/assets/{r-mVoV0Ni6.js → r-D5Yi5Z4y.js} +1 -1
  51. package/dist/client/assets/{razor-T5O-9UJL.js → razor-CHAxVq4R.js} +1 -1
  52. package/dist/client/assets/{regexp-CioRuhuN.js → regexp-gfs--3M7.js} +1 -1
  53. package/dist/client/assets/{rst-V__uTudD.js → rst-ugdlp-hl.js} +1 -1
  54. package/dist/client/assets/{ruby-C_PuKPTI.js → ruby-CDRRW37j.js} +1 -1
  55. package/dist/client/assets/{sas-D_DqqQH4.js → sas-DZaNQaIP.js} +1 -1
  56. package/dist/client/assets/{scss-D-TjzZ4c.js → scss-CzWQEplj.js} +1 -1
  57. package/dist/client/assets/{shellscript-E5759VHu.js → shellscript-fgYvpu9N.js} +1 -1
  58. package/dist/client/assets/{shellsession-AESTM-Pv.js → shellsession-BoAohHh7.js} +1 -1
  59. package/dist/client/assets/{soy-QrbrrcDv.js → soy-DBzVgv9x.js} +1 -1
  60. package/dist/client/assets/{sql-0M8VcDHD.js → sql-BYXpAYTs.js} +1 -1
  61. package/dist/client/assets/{stata-CgeIpGtc.js → stata-I71MMY3p.js} +1 -1
  62. package/dist/client/assets/{surrealql-DBGwnZbw.js → surrealql-C9U8_1VO.js} +1 -1
  63. package/dist/client/assets/{svelte-Cv0PvUc_.js → svelte-VOFrPnWT.js} +1 -1
  64. package/dist/client/assets/{templ-B9t7xRE4.js → templ-BIaxAEtC.js} +1 -1
  65. package/dist/client/assets/{tex-DhZZ8dr2.js → tex-D1dwnBE5.js} +1 -1
  66. package/dist/client/assets/{ts-tags-BFv8sbnd.js → ts-tags-C0L2Q0r5.js} +1 -1
  67. package/dist/client/assets/{tsx-CXC9KSbY.js → tsx-BqcycEv1.js} +1 -1
  68. package/dist/client/assets/{twig-CM_OO66r.js → twig-vyWqOhpM.js} +1 -1
  69. package/dist/client/assets/{typescript-BdgOTaoD.js → typescript-B2YbovqG.js} +1 -1
  70. package/dist/client/assets/{vue-BnQhjnCm.js → vue-CbXxGdjo.js} +1 -1
  71. package/dist/client/assets/{vue-html-CNnGecRI.js → vue-html-DDX4KXW7.js} +1 -1
  72. package/dist/client/assets/{vue-vine-DCuMkRhK.js → vue-vine-DsyY1LR5.js} +1 -1
  73. package/dist/client/assets/{xml-CbTD7cB8.js → xml-Ddi0-r0D.js} +1 -1
  74. package/dist/client/assets/{xsl-uOqqo7cf.js → xsl-CsFcZHFS.js} +1 -1
  75. package/dist/client/assets/{yaml-BNrLoH59.js → yaml-tGJWoH6Y.js} +1 -1
  76. package/dist/client/index.html +1 -1
  77. package/dist/client/sw.js +1 -1
  78. package/dist/server/analytics/engine.js +832 -0
  79. package/dist/server/assets.js +39 -0
  80. package/dist/server/auth/passphrase.js +70 -0
  81. package/dist/server/config.js +47 -0
  82. package/dist/server/daemon.js +535 -0
  83. package/dist/server/features/ralph-loop.js +138 -0
  84. package/dist/server/features/scheduler.js +260 -0
  85. package/dist/server/features/sticky-notes.js +99 -0
  86. package/dist/server/handlers/analytics.js +28 -0
  87. package/dist/server/handlers/attachment.js +158 -0
  88. package/dist/server/handlers/bookmarks.js +41 -0
  89. package/dist/server/handlers/chat.js +350 -0
  90. package/dist/server/handlers/editor.js +72 -0
  91. package/dist/server/handlers/fs.js +234 -0
  92. package/dist/server/handlers/loop.js +33 -0
  93. package/dist/server/handlers/memory.js +181 -0
  94. package/dist/server/handlers/mesh.js +322 -0
  95. package/dist/server/handlers/notes.js +36 -0
  96. package/dist/server/handlers/plugins.js +593 -0
  97. package/dist/server/handlers/project-settings.js +166 -0
  98. package/dist/server/handlers/scheduler.js +52 -0
  99. package/dist/server/handlers/session.js +194 -0
  100. package/dist/server/handlers/settings.js +148 -0
  101. package/dist/server/handlers/skills.js +360 -0
  102. package/dist/server/handlers/terminal.js +75 -0
  103. package/dist/server/handlers/themes.js +102 -0
  104. package/dist/server/handlers/update.js +124 -0
  105. package/dist/server/identity.js +45 -0
  106. package/dist/server/index.js +435 -0
  107. package/dist/server/logger.js +20 -0
  108. package/dist/server/mesh/connector.js +355 -0
  109. package/dist/server/mesh/crypto.js +88 -0
  110. package/dist/server/mesh/discovery.js +95 -0
  111. package/dist/server/mesh/pairing.js +104 -0
  112. package/dist/server/mesh/peers.js +54 -0
  113. package/dist/server/mesh/proxy.js +86 -0
  114. package/dist/server/mesh/session-sync.js +85 -0
  115. package/dist/server/project/bookmarks.js +77 -0
  116. package/dist/server/project/context-breakdown.js +279 -0
  117. package/dist/server/project/file-browser.js +97 -0
  118. package/dist/server/project/project-files.js +274 -0
  119. package/dist/server/project/registry.js +51 -0
  120. package/dist/server/project/sdk-bridge.js +960 -0
  121. package/dist/server/project/session.js +696 -0
  122. package/dist/server/project/terminal.js +87 -0
  123. package/dist/server/project/warmup.js +242 -0
  124. package/dist/server/push.js +87 -0
  125. package/dist/server/tls.js +50 -0
  126. package/dist/server/tui.js +83 -0
  127. package/dist/server/update-checker.js +119 -0
  128. package/dist/server/ws/broadcast.js +50 -0
  129. package/dist/server/ws/router.js +105 -0
  130. package/dist/server/ws/server.js +2 -0
  131. package/dist/shared/analytics.js +1 -0
  132. package/dist/shared/messages.js +1 -0
  133. package/dist/shared/models.js +1 -0
  134. package/dist/shared/project-settings.js +1 -0
  135. package/package.json +5 -8
  136. package/themes/alabaster.json +9 -0
  137. package/themes/amoled.json +20 -0
  138. package/themes/ayu-light.json +9 -0
  139. package/themes/catppuccin-latte.json +9 -0
  140. package/themes/catppuccin-mocha.json +9 -0
  141. package/themes/clay-light.json +10 -0
  142. package/themes/clay.json +10 -0
  143. package/themes/dracula.json +9 -0
  144. package/themes/everforest-light.json +9 -0
  145. package/themes/everforest.json +9 -0
  146. package/themes/github-light.json +9 -0
  147. package/themes/gruvbox-dark.json +9 -0
  148. package/themes/gruvbox-light.json +9 -0
  149. package/themes/horizon-light.json +9 -0
  150. package/themes/kanagawa-lotus.json +9 -0
  151. package/themes/kanagawa.json +9 -0
  152. package/themes/modus-operandi.json +9 -0
  153. package/themes/monokai.json +9 -0
  154. package/themes/nightfox.json +9 -0
  155. package/themes/nord-light.json +9 -0
  156. package/themes/nord.json +9 -0
  157. package/themes/one-dark.json +9 -0
  158. package/themes/one-light.json +9 -0
  159. package/themes/palenight.json +9 -0
  160. package/themes/paper.json +9 -0
  161. package/themes/penumbra-light.json +9 -0
  162. package/themes/poimandres.json +9 -0
  163. package/themes/quiet-light.json +9 -0
  164. package/themes/rose-pine-dawn.json +9 -0
  165. package/themes/rose-pine.json +9 -0
  166. package/themes/solarized-dark.json +9 -0
  167. package/themes/solarized-light.json +9 -0
  168. package/themes/synthwave84.json +9 -0
  169. package/themes/tokyo-night-light.json +9 -0
  170. package/themes/tokyo-night.json +9 -0
  171. package/themes/vesper.json +9 -0
  172. package/index.html +0 -20
  173. package/public/icons/icon-192.svg +0 -11
  174. package/public/icons/icon-512.svg +0 -11
  175. package/public/sw-push.js +0 -53
  176. package/src/client/App.tsx +0 -42
  177. package/src/client/commands.ts +0 -36
  178. package/src/client/components/analytics/AnalyticsView.tsx +0 -244
  179. package/src/client/components/analytics/ChartCard.tsx +0 -194
  180. package/src/client/components/analytics/PeriodSelector.tsx +0 -42
  181. package/src/client/components/analytics/QuickStats.tsx +0 -122
  182. package/src/client/components/analytics/chartTokens.ts +0 -188
  183. package/src/client/components/analytics/charts/ActivityCalendar.tsx +0 -204
  184. package/src/client/components/analytics/charts/CacheEfficiencyChart.tsx +0 -56
  185. package/src/client/components/analytics/charts/ContextUtilizationChart.tsx +0 -106
  186. package/src/client/components/analytics/charts/CostAreaChart.tsx +0 -79
  187. package/src/client/components/analytics/charts/CostDistributionChart.tsx +0 -59
  188. package/src/client/components/analytics/charts/CostDonutChart.tsx +0 -84
  189. package/src/client/components/analytics/charts/CumulativeCostChart.tsx +0 -59
  190. package/src/client/components/analytics/charts/DailySummaryCards.tsx +0 -86
  191. package/src/client/components/analytics/charts/HourlyHeatmap.tsx +0 -133
  192. package/src/client/components/analytics/charts/NodeFleetOverview.tsx +0 -89
  193. package/src/client/components/analytics/charts/PermissionBreakdown.tsx +0 -98
  194. package/src/client/components/analytics/charts/ProjectRadar.tsx +0 -126
  195. package/src/client/components/analytics/charts/ResponseTimeScatter.tsx +0 -96
  196. package/src/client/components/analytics/charts/SessionBubbleChart.tsx +0 -114
  197. package/src/client/components/analytics/charts/SessionComplexityList.tsx +0 -65
  198. package/src/client/components/analytics/charts/SessionTimeline.tsx +0 -107
  199. package/src/client/components/analytics/charts/TokenFlowChart.tsx +0 -78
  200. package/src/client/components/analytics/charts/TokenSankeyChart.tsx +0 -93
  201. package/src/client/components/analytics/charts/ToolSunburst.tsx +0 -123
  202. package/src/client/components/analytics/charts/ToolTreemap.tsx +0 -110
  203. package/src/client/components/auth/PassphrasePrompt.tsx +0 -70
  204. package/src/client/components/chat/AttachmentChips.tsx +0 -116
  205. package/src/client/components/chat/ChatInput.tsx +0 -533
  206. package/src/client/components/chat/ChatView.tsx +0 -1076
  207. package/src/client/components/chat/CommandPalette.tsx +0 -162
  208. package/src/client/components/chat/ElicitationCard.tsx +0 -238
  209. package/src/client/components/chat/Message.tsx +0 -825
  210. package/src/client/components/chat/ModelSelector.tsx +0 -108
  211. package/src/client/components/chat/PermissionModeSelector.tsx +0 -41
  212. package/src/client/components/chat/PromptQuestion.tsx +0 -271
  213. package/src/client/components/chat/StatusBar.tsx +0 -50
  214. package/src/client/components/chat/TodoCard.tsx +0 -57
  215. package/src/client/components/chat/ToolGroup.tsx +0 -129
  216. package/src/client/components/chat/ToolResultRenderer.tsx +0 -348
  217. package/src/client/components/chat/VoiceRecorder.tsx +0 -85
  218. package/src/client/components/chat/toolSummary.ts +0 -41
  219. package/src/client/components/dashboard/DashboardView.tsx +0 -200
  220. package/src/client/components/dashboard/ProjectDashboardView.tsx +0 -179
  221. package/src/client/components/mesh/NodeBadge.tsx +0 -24
  222. package/src/client/components/mesh/PairingDialog.tsx +0 -340
  223. package/src/client/components/project-settings/ProjectClaude.tsx +0 -318
  224. package/src/client/components/project-settings/ProjectEnvironment.tsx +0 -235
  225. package/src/client/components/project-settings/ProjectGeneral.tsx +0 -76
  226. package/src/client/components/project-settings/ProjectMcp.tsx +0 -232
  227. package/src/client/components/project-settings/ProjectMemory.tsx +0 -488
  228. package/src/client/components/project-settings/ProjectNotifications.tsx +0 -48
  229. package/src/client/components/project-settings/ProjectPermissions.tsx +0 -209
  230. package/src/client/components/project-settings/ProjectPlugins.tsx +0 -117
  231. package/src/client/components/project-settings/ProjectRules.tsx +0 -286
  232. package/src/client/components/project-settings/ProjectSettingsView.tsx +0 -117
  233. package/src/client/components/project-settings/ProjectSkills.tsx +0 -91
  234. package/src/client/components/settings/Appearance.tsx +0 -275
  235. package/src/client/components/settings/BudgetSettings.tsx +0 -165
  236. package/src/client/components/settings/ClaudeSettings.tsx +0 -175
  237. package/src/client/components/settings/Editor.tsx +0 -123
  238. package/src/client/components/settings/Environment.tsx +0 -185
  239. package/src/client/components/settings/GlobalMcp.tsx +0 -216
  240. package/src/client/components/settings/GlobalMemory.tsx +0 -19
  241. package/src/client/components/settings/GlobalPlugins.tsx +0 -806
  242. package/src/client/components/settings/GlobalRules.tsx +0 -149
  243. package/src/client/components/settings/GlobalSkills.tsx +0 -140
  244. package/src/client/components/settings/MeshStatus.tsx +0 -183
  245. package/src/client/components/settings/Notifications.tsx +0 -123
  246. package/src/client/components/settings/SettingsView.tsx +0 -75
  247. package/src/client/components/settings/SkillMarketplace.tsx +0 -175
  248. package/src/client/components/settings/ThemePreview.tsx +0 -140
  249. package/src/client/components/settings/ThemeWizard.tsx +0 -405
  250. package/src/client/components/settings/mcp-shared.tsx +0 -194
  251. package/src/client/components/settings/skill-shared.tsx +0 -186
  252. package/src/client/components/setup/SetupWizard.tsx +0 -755
  253. package/src/client/components/sidebar/AddProjectModal.tsx +0 -438
  254. package/src/client/components/sidebar/NodeSettingsModal.tsx +0 -206
  255. package/src/client/components/sidebar/ProjectDropdown.tsx +0 -211
  256. package/src/client/components/sidebar/ProjectRail.tsx +0 -353
  257. package/src/client/components/sidebar/SearchFilter.tsx +0 -52
  258. package/src/client/components/sidebar/SessionList.tsx +0 -599
  259. package/src/client/components/sidebar/SettingsSidebar.tsx +0 -139
  260. package/src/client/components/sidebar/Sidebar.tsx +0 -469
  261. package/src/client/components/sidebar/UserIsland.tsx +0 -282
  262. package/src/client/components/sidebar/UserMenu.tsx +0 -107
  263. package/src/client/components/ui/CommandPalette.tsx +0 -321
  264. package/src/client/components/ui/ContextMenu.tsx +0 -153
  265. package/src/client/components/ui/ErrorBoundary.tsx +0 -56
  266. package/src/client/components/ui/IconPicker.tsx +0 -184
  267. package/src/client/components/ui/KeyboardShortcuts.tsx +0 -129
  268. package/src/client/components/ui/LatticeLogomark.tsx +0 -19
  269. package/src/client/components/ui/NodeDisconnectedOverlay.tsx +0 -35
  270. package/src/client/components/ui/PopupMenu.tsx +0 -120
  271. package/src/client/components/ui/SaveFooter.tsx +0 -63
  272. package/src/client/components/ui/Toast.tsx +0 -132
  273. package/src/client/components/ui/UpdateBanner.tsx +0 -110
  274. package/src/client/components/ui/UpdatePrompt.tsx +0 -47
  275. package/src/client/components/workspace/BookmarksView.tsx +0 -156
  276. package/src/client/components/workspace/FileBrowser.tsx +0 -174
  277. package/src/client/components/workspace/FileTree.tsx +0 -129
  278. package/src/client/components/workspace/FileViewer.tsx +0 -211
  279. package/src/client/components/workspace/NoteCard.tsx +0 -120
  280. package/src/client/components/workspace/NotesView.tsx +0 -102
  281. package/src/client/components/workspace/ScheduledTasksView.tsx +0 -117
  282. package/src/client/components/workspace/SplitPane.tsx +0 -81
  283. package/src/client/components/workspace/TabBar.tsx +0 -170
  284. package/src/client/components/workspace/TaskCard.tsx +0 -159
  285. package/src/client/components/workspace/TaskEditModal.tsx +0 -129
  286. package/src/client/components/workspace/TerminalInstance.tsx +0 -171
  287. package/src/client/components/workspace/TerminalView.tsx +0 -110
  288. package/src/client/components/workspace/WorkspaceView.tsx +0 -141
  289. package/src/client/hooks/useAnalytics.ts +0 -84
  290. package/src/client/hooks/useAttachments.ts +0 -313
  291. package/src/client/hooks/useBookmarks.ts +0 -57
  292. package/src/client/hooks/useEditorConfig.ts +0 -28
  293. package/src/client/hooks/useFocusTrap.ts +0 -74
  294. package/src/client/hooks/useIdleDetection.ts +0 -50
  295. package/src/client/hooks/useInstallPrompt.ts +0 -53
  296. package/src/client/hooks/useMesh.ts +0 -89
  297. package/src/client/hooks/useNotifications.ts +0 -54
  298. package/src/client/hooks/useOnline.ts +0 -6
  299. package/src/client/hooks/useProjectSettings.ts +0 -56
  300. package/src/client/hooks/useProjects.ts +0 -98
  301. package/src/client/hooks/usePushNotifications.ts +0 -92
  302. package/src/client/hooks/useSaveState.ts +0 -65
  303. package/src/client/hooks/useSession.ts +0 -580
  304. package/src/client/hooks/useSidebar.ts +0 -90
  305. package/src/client/hooks/useSkills.ts +0 -30
  306. package/src/client/hooks/useSpinnerVerb.ts +0 -36
  307. package/src/client/hooks/useSwipeDrawer.ts +0 -299
  308. package/src/client/hooks/useTheme.ts +0 -114
  309. package/src/client/hooks/useTimeTick.ts +0 -35
  310. package/src/client/hooks/useVoiceRecorder.ts +0 -169
  311. package/src/client/hooks/useWebSocket.ts +0 -27
  312. package/src/client/hooks/useWorkspace.ts +0 -57
  313. package/src/client/lib/theme-derive.ts +0 -196
  314. package/src/client/lib/workspace-url.ts +0 -219
  315. package/src/client/main.tsx +0 -10
  316. package/src/client/providers/WebSocketProvider.tsx +0 -186
  317. package/src/client/router.tsx +0 -578
  318. package/src/client/stores/analytics.ts +0 -68
  319. package/src/client/stores/bookmarks.ts +0 -45
  320. package/src/client/stores/mesh.ts +0 -78
  321. package/src/client/stores/session.ts +0 -569
  322. package/src/client/stores/sidebar.ts +0 -530
  323. package/src/client/stores/theme.ts +0 -44
  324. package/src/client/stores/workspace.ts +0 -518
  325. package/src/client/styles/global.css +0 -391
  326. package/src/client/styles/theme-vars.css +0 -18
  327. package/src/client/themes/index.ts +0 -105
  328. package/src/client/utils/editorUrl.ts +0 -55
  329. package/src/client/utils/findDuplicateKeys.ts +0 -12
  330. package/src/client/utils/formatSessionTitle.ts +0 -17
  331. package/src/client/vite-env.d.ts +0 -6
  332. package/src/server/analytics/engine.ts +0 -920
  333. package/src/server/assets.ts +0 -45
  334. package/src/server/auth/passphrase.ts +0 -78
  335. package/src/server/config.ts +0 -55
  336. package/src/server/daemon.ts +0 -567
  337. package/src/server/features/ralph-loop.ts +0 -173
  338. package/src/server/features/scheduler.ts +0 -304
  339. package/src/server/features/sticky-notes.ts +0 -104
  340. package/src/server/handlers/analytics.ts +0 -39
  341. package/src/server/handlers/attachment.ts +0 -189
  342. package/src/server/handlers/bookmarks.ts +0 -50
  343. package/src/server/handlers/chat.ts +0 -381
  344. package/src/server/handlers/editor.ts +0 -76
  345. package/src/server/handlers/fs.ts +0 -251
  346. package/src/server/handlers/loop.ts +0 -37
  347. package/src/server/handlers/memory.ts +0 -182
  348. package/src/server/handlers/mesh.ts +0 -362
  349. package/src/server/handlers/notes.ts +0 -47
  350. package/src/server/handlers/plugins.ts +0 -655
  351. package/src/server/handlers/project-settings.ts +0 -180
  352. package/src/server/handlers/scheduler.ts +0 -64
  353. package/src/server/handlers/session.ts +0 -226
  354. package/src/server/handlers/settings.ts +0 -157
  355. package/src/server/handlers/skills.ts +0 -378
  356. package/src/server/handlers/terminal.ts +0 -88
  357. package/src/server/handlers/themes.ts +0 -121
  358. package/src/server/handlers/update.ts +0 -133
  359. package/src/server/identity.ts +0 -56
  360. package/src/server/index.ts +0 -454
  361. package/src/server/logger.ts +0 -21
  362. package/src/server/mesh/connector.ts +0 -419
  363. package/src/server/mesh/crypto.ts +0 -106
  364. package/src/server/mesh/discovery.ts +0 -126
  365. package/src/server/mesh/pairing.ts +0 -123
  366. package/src/server/mesh/peers.ts +0 -60
  367. package/src/server/mesh/proxy.ts +0 -106
  368. package/src/server/mesh/session-sync.ts +0 -107
  369. package/src/server/project/bookmarks.ts +0 -83
  370. package/src/server/project/context-breakdown.ts +0 -307
  371. package/src/server/project/file-browser.ts +0 -106
  372. package/src/server/project/project-files.ts +0 -267
  373. package/src/server/project/pty-worker.cjs +0 -83
  374. package/src/server/project/registry.ts +0 -57
  375. package/src/server/project/sdk-bridge.ts +0 -1100
  376. package/src/server/project/session.ts +0 -723
  377. package/src/server/project/terminal.ts +0 -111
  378. package/src/server/project/warmup.ts +0 -285
  379. package/src/server/push.ts +0 -121
  380. package/src/server/tls.ts +0 -65
  381. package/src/server/tui.ts +0 -103
  382. package/src/server/update-checker.ts +0 -147
  383. package/src/server/ws/broadcast.ts +0 -61
  384. package/src/server/ws/router.ts +0 -123
  385. package/src/server/ws/server.ts +0 -2
  386. package/src/shared/analytics.ts +0 -40
  387. package/src/shared/messages.ts +0 -1302
  388. package/src/shared/models.ts +0 -255
  389. package/src/shared/project-settings.ts +0 -45
  390. package/tsconfig.json +0 -25
  391. package/vite.config.ts +0 -71
  392. /package/{src/server/runtime.ts → dist/server/runtime.js} +0 -0
  393. /package/{src/shared/constants.ts → dist/shared/constants.js} +0 -0
  394. /package/{src/shared/index.ts → dist/shared/index.js} +0 -0
@@ -1,1076 +0,0 @@
1
- import { useEffect, useRef, useCallback, useState, useMemo } from "react";
2
- import { Terminal, Info, ArrowDown, Pencil, Copy, Check, Menu, AlertTriangle, Zap, Square, X, Bookmark, RefreshCw, Loader2 } from "lucide-react";
3
- import { LatticeLogomark } from "../ui/LatticeLogomark";
4
- import { useVirtualizer } from "@tanstack/react-virtual";
5
- import { useSession } from "../../hooks/useSession";
6
- import { useProjects } from "../../hooks/useProjects";
7
- import { useWebSocket } from "../../hooks/useWebSocket";
8
- import { setSessionTitle, setIsProcessing, setCurrentStatus, setWasInterrupted, setPendingPrefill, getSessionStore, invalidateSessionMessageCache } from "../../stores/session";
9
- import { openSettings, openProjectSettings } from "../../stores/sidebar";
10
- import { openTab, updateSessionTabTitle } from "../../stores/workspace";
11
- import { builtinCommands } from "../../commands";
12
- import { Message } from "./Message";
13
- import { ToolGroup } from "./ToolGroup";
14
- import { ChatInput } from "./ChatInput";
15
- import { ModelSelector } from "./ModelSelector";
16
- import { PermissionModeSelector } from "./PermissionModeSelector";
17
- import { StatusBar } from "./StatusBar";
18
- import { useSidebar } from "../../hooks/useSidebar";
19
- import { useOnline } from "../../hooks/useOnline";
20
- import { useSpinnerVerb } from "../../hooks/useSpinnerVerb";
21
- import { useBookmarks } from "../../hooks/useBookmarks";
22
- import { formatSessionTitle } from "../../utils/formatSessionTitle";
23
-
24
- function SessionLoadingState({ fileSize }: { fileSize: number | null }) {
25
- var [progress, setProgress] = useState(0);
26
-
27
- useEffect(function () {
28
- var start = Date.now();
29
- var raf = 0;
30
- // Scale the time constant based on file size:
31
- // ~50KB → 800ms, ~500KB → 2s, ~5MB → 8s, unknown → 3s
32
- var timeConstant = fileSize != null
33
- ? Math.max(800, Math.min(fileSize / 60, 8000))
34
- : 3000;
35
-
36
- function tick() {
37
- var elapsed = Date.now() - start;
38
- var raw = 1 - Math.exp(-elapsed / timeConstant);
39
- setProgress(Math.min(raw * 90, 90));
40
- raf = requestAnimationFrame(tick);
41
- }
42
-
43
- raf = requestAnimationFrame(tick);
44
- return function () { cancelAnimationFrame(raf); };
45
- }, [fileSize]);
46
-
47
- return (
48
- <div className="flex flex-col items-center justify-center h-full gap-5 select-none">
49
- <div className="flex flex-col items-center gap-3 w-full max-w-[260px]">
50
- <p className="text-[11px] font-mono text-base-content/40 tracking-wide">Loading session…</p>
51
- <div className="w-full h-[3px] rounded-full bg-base-content/8 overflow-hidden">
52
- <div
53
- className="h-full rounded-full bg-primary/60 transition-all duration-300 ease-out"
54
- style={{ width: progress + "%" }}
55
- />
56
- </div>
57
- </div>
58
- </div>
59
- );
60
- }
61
-
62
- export function ChatView({ sessionId: tabSessionId, projectSlug: tabProjectSlug }: { sessionId?: string; projectSlug?: string } = {}) {
63
- var { messages, isProcessing, sendMessage, activeSessionId, activeSessionTitle, currentStatus, contextUsage, contextBreakdown, lastResponseCost, lastResponseDuration, historyLoading, historyLoadingFileSize, historyHasMore, loadMoreHistory, wasInterrupted, promptSuggestion, failedInput, clearFailedInput, messageQueue, enqueueMessage, removeQueuedMessage, updateQueuedMessage, isPlanMode, pendingPrefill, activateSession, budgetStatus, budgetExceeded, sendBudgetOverride, dismissBudgetExceeded } = useSession();
64
- var { activeProject } = useProjects();
65
- var { toggleDrawer } = useSidebar();
66
-
67
- useEffect(function () {
68
- if (!tabSessionId || !tabProjectSlug) return;
69
- if (activeSessionId === tabSessionId) return;
70
- activateSession(tabProjectSlug, tabSessionId);
71
- }, [tabSessionId, tabProjectSlug]);
72
- var online = useOnline();
73
- var ws = useWebSocket();
74
- var spinnerVerb = useSpinnerVerb(isProcessing);
75
- var { bookmarks, requestSessionBookmarks } = useBookmarks();
76
- var [showBookmarkDropdown, setShowBookmarkDropdown] = useState<boolean>(false);
77
- var bookmarkDropdownRef = useRef<HTMLDivElement>(null);
78
- var bookmarkBtnRef = useRef<HTMLButtonElement>(null);
79
- var scrollParentRef = useRef<HTMLDivElement>(null);
80
- var prevLengthRef = useRef<number>(0);
81
- var isLiveChatRef = useRef<boolean>(false);
82
- var [isNearBottom, setIsNearBottom] = useState<boolean>(true);
83
- var isNearBottomRef = useRef<boolean>(true);
84
- var [isMobile, setIsMobile] = useState<boolean>(function () { return typeof window !== "undefined" && window.innerWidth < 640; });
85
- var [selectedModel, setSelectedModel] = useState<string>("default");
86
- var [selectedEffort, setSelectedEffort] = useState<string>("medium");
87
- var [showInfo, setShowInfo] = useState<boolean>(false);
88
- var [prefillText, setPrefillText] = useState<string | null>(null);
89
-
90
- useEffect(function () {
91
- if (pendingPrefill && !historyLoading) {
92
- setPrefillText(pendingPrefill);
93
- setPendingPrefill(null);
94
- }
95
- }, [pendingPrefill, historyLoading]);
96
-
97
- useEffect(function () {
98
- if (activeSessionId && !historyLoading) {
99
- requestSessionBookmarks();
100
- }
101
- }, [activeSessionId, historyLoading]);
102
-
103
- useEffect(function () {
104
- if (!showBookmarkDropdown) return;
105
- function handleClick(e: MouseEvent) {
106
- var target = e.target as Node;
107
- if (bookmarkDropdownRef.current && bookmarkDropdownRef.current.contains(target)) return;
108
- if (bookmarkBtnRef.current && bookmarkBtnRef.current.contains(target)) return;
109
- setShowBookmarkDropdown(false);
110
- }
111
- document.addEventListener("mousedown", handleClick);
112
- return function () { document.removeEventListener("mousedown", handleClick); };
113
- }, [showBookmarkDropdown]);
114
-
115
- var [copiedField, setCopiedField] = useState<string | null>(null);
116
- var [isRenaming, setIsRenaming] = useState<boolean>(false);
117
- var [renameValue, setRenameValue] = useState<string>("");
118
- var [showContext, setShowContext] = useState<boolean>(false);
119
- var infoRef = useRef<HTMLButtonElement>(null);
120
- var infoPanelRef = useRef<HTMLDivElement>(null);
121
- var contextBarRef = useRef<HTMLButtonElement>(null);
122
- var contextBarMobileRef = useRef<HTMLButtonElement>(null);
123
- var contextPanelRef = useRef<HTMLDivElement>(null);
124
- var renameInputRef = useRef<HTMLInputElement>(null);
125
-
126
- useEffect(function () {
127
- var el = scrollParentRef.current;
128
- if (!el) return;
129
- var scrollEl = el;
130
- function handleScroll() {
131
- var near = (scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight) < 200;
132
- if (near !== isNearBottomRef.current) {
133
- isNearBottomRef.current = near;
134
- setIsNearBottom(near);
135
- }
136
- }
137
- scrollEl.addEventListener("scroll", handleScroll, { passive: true });
138
- return function () { scrollEl.removeEventListener("scroll", handleScroll); };
139
- }, []);
140
-
141
- useEffect(function () {
142
- function handleResize() {
143
- setIsMobile(window.innerWidth < 640);
144
- }
145
- window.addEventListener("resize", handleResize);
146
- return function () { window.removeEventListener("resize", handleResize); };
147
- }, []);
148
-
149
- var virtualizer = useVirtualizer({
150
- count: messages.length,
151
- getScrollElement: function () {
152
- return scrollParentRef.current;
153
- },
154
- estimateSize: function (index) {
155
- var msg = messages[index];
156
- if (!msg) return 120;
157
- if (msg.type === "tool_start") return 52;
158
- if (msg.type === "user") return 100;
159
- return 200;
160
- },
161
- overscan: isMobile ? 10 : 20,
162
- });
163
-
164
- var scrollToBottom = useCallback(function () {
165
- if (messages.length === 0) return;
166
- if (isMobile) {
167
- var el = scrollParentRef.current;
168
- if (el) el.scrollTop = el.scrollHeight;
169
- } else {
170
- virtualizer.scrollToIndex(messages.length - 1, { align: "end", behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth" });
171
- }
172
- }, [messages.length, virtualizer, isMobile]);
173
-
174
- useEffect(
175
- function () {
176
- if (messages.length === 0) {
177
- prevLengthRef.current = 0;
178
- isLiveChatRef.current = false;
179
- return;
180
- }
181
- var prevLen = prevLengthRef.current;
182
- var delta = messages.length - prevLen;
183
- prevLengthRef.current = messages.length;
184
-
185
- if (prevLen === 0 && delta > 1) {
186
- isLiveChatRef.current = false;
187
- if (isMobile) {
188
- requestAnimationFrame(function () {
189
- var el = scrollParentRef.current;
190
- if (el) el.scrollTop = el.scrollHeight;
191
- });
192
- } else {
193
- var count = messages.length;
194
- var virt = virtualizer;
195
- requestAnimationFrame(function () {
196
- virt.scrollToIndex(count - 1, { align: "end" });
197
- });
198
- }
199
- return;
200
- }
201
-
202
- isLiveChatRef.current = true;
203
- scrollToBottom();
204
- },
205
- [messages.length, scrollToBottom]
206
- );
207
-
208
- useEffect(
209
- function () {
210
- if (isProcessing && isLiveChatRef.current) {
211
- scrollToBottom();
212
- }
213
- },
214
- [isProcessing, scrollToBottom]
215
- );
216
-
217
- useEffect(function () {
218
- if (!showInfo) return;
219
- function handleClick(e: MouseEvent) {
220
- if (
221
- infoPanelRef.current && !infoPanelRef.current.contains(e.target as Node) &&
222
- infoRef.current && !infoRef.current.contains(e.target as Node)
223
- ) {
224
- setShowInfo(false);
225
- }
226
- }
227
- document.addEventListener("mousedown", handleClick);
228
- return function () { document.removeEventListener("mousedown", handleClick); };
229
- }, [showInfo]);
230
-
231
- useEffect(function () {
232
- if (!showContext) return;
233
- function handleClick(e: MouseEvent) {
234
- var target = e.target as Node;
235
- if (contextPanelRef.current && contextPanelRef.current.contains(target)) return;
236
- if (contextBarRef.current && contextBarRef.current.contains(target)) return;
237
- if (contextBarMobileRef.current && contextBarMobileRef.current.contains(target)) return;
238
- setShowContext(false);
239
- }
240
- document.addEventListener("mousedown", handleClick);
241
- return function () { document.removeEventListener("mousedown", handleClick); };
242
- }, [showContext]);
243
-
244
- useEffect(function () {
245
- if (isRenaming && renameInputRef.current) {
246
- renameInputRef.current.focus();
247
- renameInputRef.current.select();
248
- }
249
- }, [isRenaming]);
250
-
251
- function handleCopy(text: string, field: string) {
252
- navigator.clipboard.writeText(text);
253
- setCopiedField(field);
254
- setTimeout(function () { setCopiedField(null); }, 1500);
255
- }
256
-
257
- function handleRenameStart() {
258
- setRenameValue(activeSessionTitle || "");
259
- setIsRenaming(true);
260
- }
261
-
262
- function handleRenameCommit() {
263
- if (renameValue.trim() && activeSessionId) {
264
- if (renameValue.trim() !== activeSessionTitle) {
265
- ws.send({ type: "session:rename", sessionId: activeSessionId, title: renameValue.trim() });
266
- setSessionTitle(renameValue.trim());
267
- updateSessionTabTitle(activeSessionId, renameValue.trim());
268
- }
269
- }
270
- setIsRenaming(false);
271
- setRenameValue("");
272
- }
273
-
274
- function handleRenameKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
275
- if (e.key === "Enter") {
276
- handleRenameCommit();
277
- } else if (e.key === "Escape") {
278
- setIsRenaming(false);
279
- setRenameValue("");
280
- }
281
- }
282
-
283
- function formatTokens(n: number): string {
284
- if (n >= 1000000) return (n / 1000000).toFixed(1).replace(/\.0$/, "") + "M";
285
- if (n >= 1000) return (n / 1000).toFixed(1).replace(/\.0$/, "") + "k";
286
- return String(n);
287
- }
288
-
289
- var SEGMENT_COLORS: Record<string, string> = {
290
- system: "var(--color-neutral)",
291
- builtin_tools: "var(--color-info)",
292
- instructions: "var(--color-success)",
293
- memory: "var(--color-warning)",
294
- user: "var(--color-primary)",
295
- assistant: "var(--color-secondary)",
296
- tool_results: "var(--color-accent)",
297
- };
298
- var MCP_HUES = [180, 160, 140, 200, 220];
299
- function getSegmentColor(id: string): string {
300
- if (SEGMENT_COLORS[id]) return SEGMENT_COLORS[id];
301
- if (id.startsWith("mcp_")) {
302
- var idx = 0;
303
- for (var c = 0; c < id.length; c++) idx += id.charCodeAt(c);
304
- return "oklch(0.65 0.15 " + MCP_HUES[idx % MCP_HUES.length] + ")";
305
- }
306
- return "oklch(0.5 0.1 250)";
307
- }
308
-
309
- var contextInfo = useMemo(function () {
310
- var percent = 0;
311
- var filled = 0;
312
- if (contextUsage && contextUsage.contextWindow > 0) {
313
- filled = contextUsage.inputTokens + contextUsage.cacheReadTokens + contextUsage.cacheCreationTokens;
314
- percent = Math.min(100, Math.round((filled / contextUsage.contextWindow) * 100));
315
- }
316
- var autocompact = contextBreakdown && contextBreakdown.contextWindow > 0 ? Math.round((contextBreakdown.autocompactAt / contextBreakdown.contextWindow) * 100) : 90;
317
- return {
318
- contextPercent: percent,
319
- contextFilled: filled,
320
- autocompactPercent: autocompact,
321
- isApproachingCompact: percent >= autocompact - 10,
322
- isCritical: percent >= autocompact,
323
- };
324
- }, [contextUsage, contextBreakdown]);
325
-
326
- var contextPercent = contextInfo.contextPercent;
327
- var contextFilled = contextInfo.contextFilled;
328
- var autocompactPercent = contextInfo.autocompactPercent;
329
- var isApproachingCompact = contextInfo.isApproachingCompact;
330
- var isCritical = contextInfo.isCritical;
331
-
332
- var resumeCommand = activeSessionId && activeProject
333
- ? "cd " + activeProject.path + " && claude --resume " + activeSessionId
334
- : activeSessionId
335
- ? "claude --resume " + activeSessionId
336
- : "";
337
-
338
- function handleClientCommand(name: string, args: string): boolean {
339
- switch (name) {
340
- case "clear":
341
- case "reset":
342
- case "new":
343
- if (activeProject?.slug) {
344
- ws.send({ type: "session:create", projectSlug: activeProject.slug });
345
- }
346
- return true;
347
- case "copy": {
348
- var lastAssistant: typeof messages[0] | undefined;
349
- for (var ci = messages.length - 1; ci >= 0; ci--) {
350
- if (messages[ci].type === "assistant") { lastAssistant = messages[ci]; break; }
351
- }
352
- if (lastAssistant?.text) {
353
- navigator.clipboard.writeText(lastAssistant.text);
354
- }
355
- return true;
356
- }
357
- case "export": {
358
- var lines = messages.map(function (m) {
359
- var role = m.type === "user" ? "User" : m.type === "assistant" ? "Assistant" : m.type;
360
- return role + ": " + (m.text || m.content || "");
361
- });
362
- var blob = new Blob([lines.join("\n\n")], { type: "text/plain" });
363
- var url = URL.createObjectURL(blob);
364
- var a = document.createElement("a");
365
- a.href = url;
366
- a.download = (activeSessionTitle || "conversation") + ".txt";
367
- a.click();
368
- URL.revokeObjectURL(url);
369
- return true;
370
- }
371
- case "rename": {
372
- if (args && activeSessionId) {
373
- ws.send({ type: "session:rename", sessionId: activeSessionId, title: args });
374
- setSessionTitle(args);
375
- updateSessionTabTitle(activeSessionId, args);
376
- } else {
377
- handleRenameStart();
378
- }
379
- return true;
380
- }
381
- case "theme":
382
- openSettings("appearance");
383
- return true;
384
- case "config":
385
- case "settings":
386
- openSettings("appearance");
387
- return true;
388
- case "permissions":
389
- case "allowed-tools":
390
- if (activeProject?.slug) openProjectSettings("permissions");
391
- return true;
392
- case "memory":
393
- if (activeProject?.slug) openProjectSettings("memory");
394
- return true;
395
- case "skills":
396
- if (activeProject?.slug) openProjectSettings("skills");
397
- return true;
398
- case "plan":
399
- ws.send({ type: "chat:set_permission_mode", mode: "plan" });
400
- return true;
401
- case "cost":
402
- case "context":
403
- setShowInfo(true);
404
- return true;
405
- default:
406
- return false;
407
- }
408
- }
409
-
410
- function handleCancel() {
411
- ws.send({ type: "chat:cancel" });
412
- setIsProcessing(false);
413
- setCurrentStatus(null);
414
- setWasInterrupted(true);
415
- }
416
-
417
- function handleSend(text: string, attachmentIds: string[]) {
418
- if (text.startsWith("/")) {
419
- var parts = text.split(/\s+/);
420
- var cmdName = parts[0].slice(1).toLowerCase();
421
- var cmdArgs = parts.slice(1).join(" ");
422
-
423
- var isBuiltin = false;
424
- for (var i = 0; i < builtinCommands.length; i++) {
425
- var cmd = builtinCommands[i];
426
- if (cmd.name === cmdName || (cmd.aliases && cmd.aliases.indexOf(cmdName) !== -1)) {
427
- isBuiltin = true;
428
- break;
429
- }
430
- }
431
-
432
- if (isBuiltin && handleClientCommand(cmdName, cmdArgs)) return;
433
- }
434
-
435
- if (isProcessing) {
436
- enqueueMessage(text);
437
- return;
438
- }
439
- sendMessage(text, attachmentIds, selectedModel, selectedEffort);
440
- }
441
-
442
- var virtualItems = virtualizer.getVirtualItems();
443
-
444
- var lastAssistantIndex = useMemo(function () {
445
- if (isProcessing || lastResponseCost == null) return -1;
446
- for (var i = messages.length - 1; i >= 0; i--) {
447
- if (messages[i].type === "assistant") return i;
448
- }
449
- return -1;
450
- }, [messages, isProcessing, lastResponseCost]);
451
-
452
- return (
453
- <div className="flex flex-col h-full w-full bg-base-100 overflow-hidden relative">
454
- <div className="bg-base-100 border-b border-base-300 flex-shrink-0 px-2 sm:px-4">
455
- <div className="flex items-center h-11 gap-1.5">
456
- <button
457
- className="btn btn-ghost btn-sm btn-square lg:hidden"
458
- aria-label="Toggle sidebar"
459
- onClick={toggleDrawer}
460
- >
461
- <Menu size={18} />
462
- </button>
463
- <div className="flex-1 min-w-0 flex items-center gap-1.5">
464
- {isRenaming ? (
465
- <input
466
- ref={renameInputRef}
467
- value={renameValue}
468
- onChange={function (e) { setRenameValue(e.target.value); }}
469
- onBlur={handleRenameCommit}
470
- onKeyDown={handleRenameKeyDown}
471
- className="input input-sm input-bordered text-sm font-semibold w-full max-w-[280px] bg-base-300 border-base-content/15"
472
- />
473
- ) : (
474
- <>
475
- <span className="text-sm font-semibold text-base-content truncate">
476
- {formatSessionTitle(activeSessionTitle) || (activeSessionId ? "Session" : "New Session")}
477
- </span>
478
- {activeSessionId && (
479
- <button
480
- onClick={handleRenameStart}
481
- aria-label="Rename session"
482
- className="btn btn-ghost btn-xs btn-square text-base-content/30 hover:text-base-content/70 transition-colors"
483
- >
484
- <Pencil size={12} />
485
- </button>
486
- )}
487
- {activeSessionId && bookmarks.length > 0 && (
488
- <div className="relative">
489
- <button
490
- ref={bookmarkBtnRef}
491
- onClick={function () { setShowBookmarkDropdown(!showBookmarkDropdown); }}
492
- aria-label="View bookmarks"
493
- className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-mono text-warning/70 hover:text-warning hover:bg-warning/10 transition-colors"
494
- >
495
- <Bookmark size={11} />
496
- <span>{bookmarks.length}</span>
497
- </button>
498
- {showBookmarkDropdown && (
499
- <div
500
- ref={bookmarkDropdownRef}
501
- className="absolute top-full left-0 mt-1 z-50 bg-base-300 border border-base-content/15 rounded-lg shadow-xl py-1 w-[280px] max-h-[300px] overflow-y-auto"
502
- >
503
- <div className="px-2.5 py-1.5 text-[10px] uppercase tracking-widest text-base-content/40 font-mono font-bold">
504
- Bookmarks
505
- </div>
506
- {bookmarks.map(function (bm) {
507
- return (
508
- <button
509
- key={bm.id}
510
- type="button"
511
- onClick={function () {
512
- setShowBookmarkDropdown(false);
513
- var el = document.getElementById("msg-" + bm.messageUuid);
514
- if (el) {
515
- el.scrollIntoView({ behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth", block: "center" });
516
- el.classList.add("ring-2", "ring-warning/40");
517
- setTimeout(function () { el!.classList.remove("ring-2", "ring-warning/40"); }, 2000);
518
- }
519
- }}
520
- className="flex items-start gap-2 w-full px-2.5 py-1.5 hover:bg-base-content/5 transition-colors text-left"
521
- >
522
- <Bookmark size={10} className="text-warning/60 mt-0.5 flex-shrink-0" />
523
- <div className="min-w-0 flex-1">
524
- <div className="text-[11px] text-base-content/60 truncate">{bm.messageText}</div>
525
- <div className="text-[9px] text-base-content/30 font-mono">{bm.messageType}</div>
526
- </div>
527
- </button>
528
- );
529
- })}
530
- </div>
531
- )}
532
- </div>
533
- )}
534
- </>
535
- )}
536
- </div>
537
- <div className="flex gap-1.5 items-center relative">
538
- {activeSessionId && (
539
- <button
540
- ref={contextBarRef}
541
- onClick={function () { setShowContext(!showContext); }}
542
- aria-label="Context usage"
543
- className={"hidden sm:flex items-center gap-1.5 px-1.5 py-1 rounded transition-colors " + (showContext ? "bg-base-300" : "hover:bg-base-300/50")}
544
- >
545
- <div className="w-16 h-1.5 rounded-full bg-base-300 overflow-hidden relative">
546
- <div
547
- className={"h-full rounded-full transition-all duration-300 " + (isCritical ? "bg-error animate-pulse" : isApproachingCompact ? "bg-warning" : "bg-primary/60")}
548
- style={{ width: Math.max(contextPercent, 1) + "%" }}
549
- />
550
- </div>
551
- <span className={"text-[10px] font-mono tabular-nums " + (isCritical ? "text-error" : isApproachingCompact ? "text-warning" : "text-base-content/40")}>
552
- {contextPercent}%
553
- </span>
554
- </button>
555
- )}
556
- {activeSessionId && (
557
- <button
558
- onClick={function () {
559
- var state = getSessionStore().state;
560
- if (state.activeProjectSlug && state.activeSessionId) {
561
- invalidateSessionMessageCache(state.activeSessionId);
562
- getSessionStore().setState(function (s) { return { ...s, historyLoading: true, messages: [] }; });
563
- ws.send({ type: "session:activate", projectSlug: state.activeProjectSlug, sessionId: state.activeSessionId, refresh: true } as any);
564
- }
565
- }}
566
- aria-label="Refresh conversation"
567
- className={"btn btn-ghost btn-sm btn-square text-base-content/50 hover:text-base-content/70 transition-colors" + (historyLoading ? " animate-spin" : "")}
568
- >
569
- {historyLoading ? <Loader2 size={14} /> : <RefreshCw size={14} />}
570
- </button>
571
- )}
572
- {activeSessionId && (
573
- <button
574
- ref={infoRef}
575
- onClick={function () { setShowInfo(!showInfo); }}
576
- aria-label="Session info"
577
- className={"btn btn-ghost btn-sm btn-square transition-colors " + (showInfo ? "text-primary" : "text-base-content/50 hover:text-base-content/70")}
578
- >
579
- <Info size={15} />
580
- </button>
581
- )}
582
- {!activeSessionId && (
583
- <button
584
- aria-label="Session info"
585
- disabled
586
- className="btn btn-ghost btn-sm btn-square text-base-content/30 opacity-40 cursor-not-allowed"
587
- >
588
- <Info size={15} />
589
- </button>
590
- )}
591
- <button
592
- aria-label="Open terminal"
593
- onClick={function () { openTab("terminal"); }}
594
- className="btn btn-ghost btn-sm btn-square text-base-content/50 hover:text-base-content/70"
595
- >
596
- <Terminal size={15} />
597
- </button>
598
-
599
- {showContext && activeSessionId && (
600
- <div
601
- ref={contextPanelRef}
602
- className="absolute top-full right-0 mt-1 z-50 bg-base-300 border border-base-content/15 rounded-lg shadow-xl p-3 w-[calc(100vw-24px)] sm:min-w-[300px] sm:w-auto max-w-[340px]"
603
- >
604
- <div className="flex flex-col gap-3">
605
- <div className="flex items-center justify-between">
606
- <div className="text-[10px] uppercase tracking-widest text-base-content/40 font-mono font-bold">Context Window</div>
607
- <span className={"text-xs font-mono font-bold tabular-nums " + (isCritical ? "text-error" : isApproachingCompact ? "text-warning" : "text-primary")}>
608
- {contextPercent}%
609
- </span>
610
- </div>
611
-
612
- {contextBreakdown ? (
613
- <>
614
- <div className="relative">
615
- <div className="w-full h-3 rounded bg-base-200 overflow-hidden flex">
616
- {contextBreakdown.segments.filter(function (seg) {
617
- return seg.tokens > 0;
618
- }).map(function (seg) {
619
- var pct = (seg.tokens / contextBreakdown!.contextWindow) * 100;
620
- if (pct < 0.2) return null;
621
- return (
622
- <div
623
- key={seg.id}
624
- className="h-full transition-all duration-300"
625
- style={{ width: pct + "%", backgroundColor: getSegmentColor(seg.id) }}
626
- />
627
- );
628
- })}
629
- </div>
630
- <div
631
- className="absolute top-0 w-px h-full bg-base-content/25"
632
- style={{ left: autocompactPercent + "%" }}
633
- title={"Auto-compact at " + autocompactPercent + "%"}
634
- />
635
- </div>
636
-
637
- <div className="flex justify-between text-[11px] font-mono tabular-nums text-base-content/50">
638
- <span>{formatTokens(contextFilled)} used</span>
639
- <span>{formatTokens(contextBreakdown.contextWindow)} total</span>
640
- </div>
641
-
642
- <div className="border-t border-base-content/10 pt-2.5 flex flex-col gap-0.5">
643
- {contextBreakdown.segments.filter(function (seg) {
644
- return seg.tokens > 0;
645
- }).map(function (seg) {
646
- var pct = contextBreakdown!.contextWindow > 0 ? ((seg.tokens / contextBreakdown!.contextWindow) * 100) : 0;
647
- return (
648
- <div key={seg.id} className="flex items-center justify-between py-0.5">
649
- <div className="flex items-center gap-2 min-w-0">
650
- <div className="w-2 h-2 rounded-sm flex-shrink-0" style={{ backgroundColor: getSegmentColor(seg.id) }} />
651
- <span className="text-[11px] text-base-content/50 truncate">{seg.label}</span>
652
- </div>
653
- <div className="flex items-center gap-2 flex-shrink-0 ml-3">
654
- <span className="text-[11px] font-mono tabular-nums text-base-content/70">
655
- {seg.estimated ? "~" : ""}{formatTokens(seg.tokens)}
656
- </span>
657
- <span className="text-[10px] font-mono tabular-nums text-base-content/30 w-9 text-right">{pct < 1 ? "<1" : Math.round(pct)}%</span>
658
- </div>
659
- </div>
660
- );
661
- })}
662
- </div>
663
-
664
- {(function () {
665
- var totalUsed = contextBreakdown!.segments.reduce(function (sum, seg) { return sum + seg.tokens; }, 0);
666
- var available = contextBreakdown!.contextWindow - totalUsed;
667
- if (available > 0) {
668
- return (
669
- <div className="border-t border-base-content/10 pt-2 flex items-center justify-between">
670
- <span className="text-[11px] text-base-content/30">Available</span>
671
- <span className="text-[11px] font-mono tabular-nums text-base-content/40">{formatTokens(available)}</span>
672
- </div>
673
- );
674
- }
675
- return null;
676
- })()}
677
- </>
678
- ) : (
679
- <>
680
- <div className="w-full h-3 rounded bg-base-200 overflow-hidden">
681
- <div
682
- className={"h-full transition-all duration-300 " + (isCritical ? "bg-error" : isApproachingCompact ? "bg-warning" : "bg-primary/60")}
683
- style={{ width: contextPercent + "%" }}
684
- />
685
- </div>
686
-
687
- <div className="flex justify-between text-[11px] font-mono tabular-nums text-base-content/50">
688
- <span>{formatTokens(contextFilled)} used</span>
689
- <span>{formatTokens(contextUsage?.contextWindow || 0)} total</span>
690
- </div>
691
-
692
- <div className="text-[11px] text-base-content/30 text-center py-1">
693
- Computing breakdown...
694
- </div>
695
- </>
696
- )}
697
-
698
- {isCritical && (
699
- <div className="text-[10px] font-mono px-2 py-1.5 rounded bg-error/10 text-error">
700
- Context nearly full — session will auto-compact soon
701
- </div>
702
- )}
703
- {isApproachingCompact && !isCritical && (
704
- <div className="text-[10px] font-mono px-2 py-1.5 rounded bg-warning/10 text-warning">
705
- Approaching auto-compact threshold
706
- </div>
707
- )}
708
- </div>
709
- </div>
710
- )}
711
-
712
- {showInfo && activeSessionId && (
713
- <div
714
- ref={infoPanelRef}
715
- className="absolute top-full right-0 mt-1 z-50 bg-base-300 border border-base-content/15 rounded-lg shadow-xl p-3 w-[calc(100vw-24px)] sm:min-w-[340px] sm:w-auto max-w-[380px]"
716
- >
717
- <div className="flex flex-col gap-2.5">
718
- <div>
719
- <div className="text-[10px] uppercase tracking-widest text-base-content/40 mb-1 font-mono font-bold">Session ID</div>
720
- <div className="flex items-center gap-1.5">
721
- <code className="text-[12px] font-mono text-base-content/70 bg-base-200 px-2 py-1 rounded flex-1 truncate select-all">
722
- {activeSessionId}
723
- </code>
724
- <button
725
- onClick={function () { handleCopy(activeSessionId!, "sessionId"); }}
726
- aria-label="Copy session ID"
727
- className="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content/70 flex-shrink-0"
728
- >
729
- {copiedField === "sessionId" ? <Check size={13} className="text-success" /> : <Copy size={13} />}
730
- </button>
731
- </div>
732
- </div>
733
- <div>
734
- <div className="text-[10px] uppercase tracking-widest text-base-content/40 mb-1 font-mono font-bold">Resume Command</div>
735
- <div className="flex items-center gap-1.5">
736
- <code className="text-[12px] font-mono text-base-content/70 bg-base-200 px-2 py-1 rounded flex-1 truncate select-all">
737
- {resumeCommand}
738
- </code>
739
- <button
740
- onClick={function () { handleCopy(resumeCommand, "resume"); }}
741
- aria-label="Copy resume command"
742
- className="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content/70 flex-shrink-0"
743
- >
744
- {copiedField === "resume" ? <Check size={13} className="text-success" /> : <Copy size={13} />}
745
- </button>
746
- </div>
747
- </div>
748
- </div>
749
- </div>
750
- )}
751
- </div>
752
- </div>
753
- {activeSessionId && (
754
- <button
755
- ref={contextBarMobileRef}
756
- onClick={function () { setShowContext(!showContext); }}
757
- aria-label="Context usage"
758
- className={"sm:hidden flex items-center gap-1.5 px-1.5 pb-1 rounded transition-colors w-full " + (showContext ? "bg-base-300" : "hover:bg-base-300/50")}
759
- >
760
- <div className="flex-1 h-1.5 rounded-full bg-base-300 overflow-hidden relative">
761
- <div
762
- className={"h-full rounded-full transition-all duration-300 " + (isCritical ? "bg-error animate-pulse" : isApproachingCompact ? "bg-warning" : "bg-primary/60")}
763
- style={{ width: Math.max(contextPercent, 1) + "%" }}
764
- />
765
- </div>
766
- <span className={"text-[10px] font-mono tabular-nums " + (isCritical ? "text-error" : isApproachingCompact ? "text-warning" : "text-base-content/40")}>
767
- {contextPercent}%
768
- </span>
769
- </button>
770
- )}
771
- </div>
772
-
773
- {isPlanMode && (
774
- <div className="flex items-center gap-2 px-4 py-1.5 bg-primary/8 border-b border-primary/15">
775
- <div className="w-2 h-2 rounded-full bg-primary animate-pulse" />
776
- <span className="text-[11px] font-mono font-medium text-primary/60 uppercase tracking-wider">Plan Mode</span>
777
- </div>
778
- )}
779
-
780
- <div
781
- ref={scrollParentRef}
782
- className="flex-1 overflow-y-auto overflow-x-hidden min-h-0 bg-lattice-grid"
783
- aria-live="polite"
784
- aria-relevant="additions"
785
- style={{ WebkitOverflowScrolling: "touch", touchAction: "pan-y" }}
786
- >
787
- {historyHasMore && messages.length > 0 && (
788
- <div className="flex justify-center py-3">
789
- <button
790
- onClick={loadMoreHistory}
791
- className="text-[11px] text-base-content/30 hover:text-base-content/50 font-mono transition-colors"
792
- >
793
- Load older messages
794
- </button>
795
- </div>
796
- )}
797
- {messages.length === 0 && historyLoading ? (
798
- <SessionLoadingState fileSize={historyLoadingFileSize} />
799
- ) : messages.length === 0 ? (
800
- <div className="flex items-center justify-center p-10 h-full">
801
- <div className="text-center max-w-[360px]">
802
- <div className="text-base-content/10 mb-4 flex justify-center">
803
- <LatticeLogomark size={40} />
804
- </div>
805
- <p className="text-[15px] font-mono font-semibold text-base-content/60 mb-1.5">
806
- {activeSessionId
807
- ? "What are you working on?"
808
- : activeProject
809
- ? "Ready when you are"
810
- : "Select a project"}
811
- </p>
812
- <p className="text-[12px] text-base-content/30 leading-relaxed">
813
- {activeSessionId
814
- ? "Type a message below or press / for commands."
815
- : activeProject
816
- ? "Start a new session from the sidebar to chat with Claude."
817
- : "Choose a project from the rail to get started."}
818
- </p>
819
- </div>
820
- </div>
821
- ) : isMobile ? (
822
- <div className="pt-4">
823
- {messages.map(function (msg, idx) {
824
- if (msg.type === "tool_start") {
825
- var groupStart = idx;
826
- while (groupStart > 0 && messages[groupStart - 1].type === "tool_start") {
827
- groupStart--;
828
- }
829
- var groupEnd = idx;
830
- while (groupEnd < messages.length - 1 && messages[groupEnd + 1].type === "tool_start") {
831
- groupEnd++;
832
- }
833
- var groupSize = groupEnd - groupStart + 1;
834
- if (groupSize >= 2) {
835
- if (idx === groupStart) {
836
- var groupTools = messages.slice(groupStart, groupEnd + 1);
837
- return <ToolGroup key={"tg-" + (groupTools[0].uuid || idx)} tools={groupTools} />;
838
- }
839
- return null;
840
- }
841
- }
842
- var isLastAssistant = idx === lastAssistantIndex;
843
- return (
844
- <Message
845
- key={msg.uuid || ("msg-" + idx)}
846
- message={msg}
847
- responseCost={isLastAssistant ? lastResponseCost : undefined}
848
- responseDuration={isLastAssistant ? lastResponseDuration : undefined}
849
- />
850
- );
851
- })}
852
- </div>
853
- ) : (
854
- <div
855
- className="relative w-full"
856
- style={{ height: virtualizer.getTotalSize() + "px", paddingBottom: "24px" }}
857
- >
858
- <div
859
- className="absolute top-0 left-0 w-full will-change-transform"
860
- style={{
861
- transform: "translateY(" + (virtualItems.length > 0 ? virtualItems[0].start : 0) + "px)",
862
- }}
863
- >
864
- {virtualItems.map(function (virtualItem) {
865
- var msg = messages[virtualItem.index];
866
- var idx = virtualItem.index;
867
-
868
- if (msg.type === "tool_start") {
869
- var groupStart = idx;
870
- while (groupStart > 0 && messages[groupStart - 1].type === "tool_start") {
871
- groupStart--;
872
- }
873
- var groupEnd = idx;
874
- while (groupEnd < messages.length - 1 && messages[groupEnd + 1].type === "tool_start") {
875
- groupEnd++;
876
- }
877
- var groupSize = groupEnd - groupStart + 1;
878
-
879
- if (groupSize >= 2) {
880
- if (idx === groupStart) {
881
- var groupTools = messages.slice(groupStart, groupEnd + 1);
882
- return (
883
- <div
884
- key={virtualItem.key}
885
- data-index={virtualItem.index}
886
- ref={virtualizer.measureElement}
887
- className={virtualItem.index === 0 ? "pt-4" : ""}
888
- >
889
- <ToolGroup tools={groupTools} />
890
- </div>
891
- );
892
- }
893
- return (
894
- <div
895
- key={virtualItem.key}
896
- data-index={virtualItem.index}
897
- ref={virtualizer.measureElement}
898
- />
899
- );
900
- }
901
- }
902
-
903
- var isLastAssistant = virtualItem.index === lastAssistantIndex;
904
- return (
905
- <div
906
- key={virtualItem.key}
907
- data-index={virtualItem.index}
908
- ref={virtualizer.measureElement}
909
- className={virtualItem.index === 0 ? "pt-4" : ""}
910
- >
911
- <Message
912
- message={msg}
913
- responseCost={isLastAssistant ? lastResponseCost : undefined}
914
- responseDuration={isLastAssistant ? lastResponseDuration : undefined}
915
- />
916
- </div>
917
- );
918
- })}
919
- </div>
920
- </div>
921
- )}
922
-
923
- {isProcessing && (
924
- <div className="flex px-5 py-3 gap-3 items-center">
925
- <div className="w-7 h-7 rounded-full bg-primary/10 border border-primary/20 flex items-center justify-center flex-shrink-0">
926
- <div className="w-3 h-3 rounded-full bg-primary" />
927
- </div>
928
- <div className="flex gap-1.5 items-center">
929
- {[0, 1, 2].map(function (i) {
930
- return (
931
- <div
932
- key={i}
933
- className="w-1.5 h-1.5 rounded-full bg-base-content/30"
934
- style={{
935
- animation: "pulse 1.2s ease-in-out infinite",
936
- animationDelay: i * 0.2 + "s",
937
- }}
938
- />
939
- );
940
- })}
941
- </div>
942
- </div>
943
- )}
944
- </div>
945
-
946
- {messages.length > 0 && !isNearBottom && (
947
- <div className="absolute bottom-32 left-1/2 -translate-x-1/2 z-10">
948
- <button
949
- onClick={scrollToBottom}
950
- className="btn btn-sm btn-circle bg-base-300 border border-base-content/15 shadow-lg hover:bg-base-200 text-base-content/60 hover:text-base-content transition-all duration-150"
951
- aria-label="Scroll to bottom"
952
- >
953
- <ArrowDown size={14} />
954
- </button>
955
- </div>
956
- )}
957
-
958
- <StatusBar status={currentStatus} />
959
-
960
- {isProcessing && (
961
- <div className="flex-shrink-0 flex items-center justify-center gap-4 my-4 pointer-events-none relative z-10">
962
- <div className="flex items-center gap-4 pointer-events-auto bg-base-200/90 backdrop-blur-sm border border-base-content/10 rounded-full px-5 py-2 shadow-lg">
963
- <span className="text-[14px] text-base-content/40 font-mono animate-pulse">{spinnerVerb}...</span>
964
- <button
965
- onClick={handleCancel}
966
- className="btn btn-ghost btn-sm text-error/70 hover:text-error gap-1.5"
967
- >
968
- <Square size={12} className="fill-current" />
969
- Stop
970
- </button>
971
- </div>
972
- </div>
973
- )}
974
-
975
- {messageQueue.length > 0 && (
976
- <div className="flex-shrink-0 px-2 sm:px-4 py-2 space-y-1.5">
977
- <div className="text-[10px] font-semibold uppercase tracking-wider text-base-content/30">Queued</div>
978
- {messageQueue.map(function (msg, i) {
979
- return (
980
- <div key={i} className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-base-300/50 border border-base-content/10">
981
- <input
982
- type="text"
983
- value={msg}
984
- onChange={function (e) { updateQueuedMessage(i, e.target.value); }}
985
- className="flex-1 bg-transparent text-[12px] text-base-content outline-none"
986
- />
987
- <button
988
- onClick={function () { removeQueuedMessage(i); }}
989
- className="text-base-content/30 hover:text-base-content/60"
990
- >
991
- <X size={12} />
992
- </button>
993
- </div>
994
- );
995
- })}
996
- </div>
997
- )}
998
-
999
- {wasInterrupted && !isProcessing && (
1000
- <div className="flex items-center gap-2 px-3 sm:px-5 py-2 bg-warning/10 border-t border-warning/20">
1001
- <AlertTriangle size={13} className="text-warning flex-shrink-0" />
1002
- <span className="text-[12px] text-warning">Session was interrupted — send a message to continue</span>
1003
- </div>
1004
- )}
1005
-
1006
- {promptSuggestion && !isProcessing && (
1007
- <div className="flex-shrink-0 px-2 sm:px-4 py-2">
1008
- <div className="flex items-center gap-1.5 max-w-full">
1009
- <button
1010
- onClick={function () { if (promptSuggestion) handleSend(promptSuggestion, []); }}
1011
- className="flex items-center gap-2 px-3 py-2 rounded-lg bg-primary/10 border border-primary/20 text-[12px] text-primary/80 hover:bg-primary/15 hover:text-primary transition-colors min-w-0"
1012
- >
1013
- <Zap size={12} className="flex-shrink-0" />
1014
- <span className="truncate">{promptSuggestion}</span>
1015
- </button>
1016
- <button
1017
- onClick={function () { if (promptSuggestion) setPrefillText(promptSuggestion); }}
1018
- aria-label="Edit suggestion"
1019
- className="btn btn-ghost btn-xs btn-square text-primary/40 hover:text-primary/80 flex-shrink-0"
1020
- >
1021
- <Pencil size={12} />
1022
- </button>
1023
- </div>
1024
- </div>
1025
- )}
1026
-
1027
- {budgetExceeded && budgetStatus && (
1028
- <div className="flex-shrink-0 border-t border-base-300 bg-warning/10 px-4 py-3 flex items-center gap-3">
1029
- <AlertTriangle size={16} className="text-warning flex-shrink-0" />
1030
- <div className="flex-1 min-w-0">
1031
- <div className="text-[13px] text-base-content font-medium">
1032
- Daily budget exceeded (${budgetStatus.dailySpend.toFixed(2)} / ${budgetStatus.dailyLimit.toFixed(2)})
1033
- </div>
1034
- <div className="text-[11px] text-base-content/50">Send anyway?</div>
1035
- </div>
1036
- <button
1037
- onClick={sendBudgetOverride}
1038
- className="px-3 py-1.5 rounded-lg bg-warning text-warning-content text-[12px] font-semibold hover:bg-warning/80 transition-colors"
1039
- >
1040
- Continue
1041
- </button>
1042
- <button
1043
- onClick={dismissBudgetExceeded}
1044
- className="px-3 py-1.5 rounded-lg border border-base-content/15 text-base-content/60 text-[12px] hover:bg-base-content/5 transition-colors"
1045
- >
1046
- Cancel
1047
- </button>
1048
- </div>
1049
- )}
1050
-
1051
- <div className="flex-shrink-0 border-t border-base-300 bg-base-200 px-2 sm:px-4 pb-3 pt-2">
1052
- <ChatInput
1053
- sessionId={activeSessionId}
1054
- onSend={handleSend}
1055
- disabled={!activeSessionId || !online || (budgetStatus !== null && budgetStatus.enforcement === "hard-block" && budgetStatus.dailySpend >= budgetStatus.dailyLimit)}
1056
- disabledPlaceholder={
1057
- (budgetStatus !== null && budgetStatus.enforcement === "hard-block" && budgetStatus.dailySpend >= budgetStatus.dailyLimit)
1058
- ? "Daily budget exceeded ($" + budgetStatus.dailySpend.toFixed(2) + " / $" + budgetStatus.dailyLimit.toFixed(2) + ")"
1059
- : undefined
1060
- }
1061
- failedInput={failedInput}
1062
- onFailedInputConsumed={clearFailedInput}
1063
- prefillText={prefillText}
1064
- onPrefillConsumed={function () { setPrefillText(null); }}
1065
- toolbarContent={
1066
- <>
1067
- <PermissionModeSelector />
1068
- <span className="text-base-content/15 hidden sm:inline">·</span>
1069
- <ModelSelector onChange={function (state) { setSelectedModel(state.model); setSelectedEffort(state.effort); }} />
1070
- </>
1071
- }
1072
- />
1073
- </div>
1074
- </div>
1075
- );
1076
- }