@cryptiklemur/lattice 4.0.2 → 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.
- package/bin/lattice +1 -9
- package/dist/client/assets/{angular-html-N8PCEquT.js → angular-html-DKTL-XDO.js} +1 -1
- package/dist/client/assets/{angular-ts-CJ8RJIPD.js → angular-ts-tvBzOwQR.js} +1 -1
- package/dist/client/assets/{apl-BD6tCLWN.js → apl-CCzl5qFl.js} +1 -1
- package/dist/client/assets/{astro-CpIIfBs6.js → astro-DNQTpO2Y.js} +1 -1
- package/dist/client/assets/{blade-D3qgnjiV.js → blade-CyJoIMeJ.js} +1 -1
- package/dist/client/assets/{c-Dr6ADN_t.js → c-OEwk5KN8.js} +1 -1
- package/dist/client/assets/{cobol-BIfDE0Hr.js → cobol-DpHyJzz2.js} +1 -1
- package/dist/client/assets/{coffee-DHQ57vfY.js → coffee-BX5dbDzZ.js} +1 -1
- package/dist/client/assets/{cpp-CEBY6JOp.js → cpp-BTBjNg2U.js} +1 -1
- package/dist/client/assets/{crystal-D125CSmP.js → crystal-CNzZd6DW.js} +1 -1
- package/dist/client/assets/{css-CBmrkYSr.js → css-BuKsNmms.js} +1 -1
- package/dist/client/assets/{dist-A_mCRD1f.js → dist-CKpDHMy6.js} +2 -2
- package/dist/client/assets/{edge-Ccsz7cJW.js → edge-DzhnGgJE.js} +1 -1
- package/dist/client/assets/{elixir-Do6gk14X.js → elixir-Dqs0waqF.js} +1 -1
- package/dist/client/assets/{elm-Db22zT4C.js → elm-BtWwjxWn.js} +1 -1
- package/dist/client/assets/{erb-MXVqAAJD.js → erb-iPD89b4v.js} +1 -1
- package/dist/client/assets/{git-rebase-B-LLWBOA.js → git-rebase-BxVNXJL4.js} +1 -1
- package/dist/client/assets/{glimmer-js-eWszRU73.js → glimmer-js-BlyCupwF.js} +1 -1
- package/dist/client/assets/{glimmer-ts-VQmwGqUp.js → glimmer-ts-DjIxWOS9.js} +1 -1
- package/dist/client/assets/{glsl-B8ilOfAl.js → glsl-CGIL-65r.js} +1 -1
- package/dist/client/assets/{graphql-DnTqxeOc.js → graphql-DeOn6mNV.js} +1 -1
- package/dist/client/assets/{hack-XJsHYSQb.js → hack-DVppeCmS.js} +1 -1
- package/dist/client/assets/{haml-CQ7Vqzwp.js → haml-WDhua0Mp.js} +1 -1
- package/dist/client/assets/{handlebars-C4szooBf.js → handlebars-i2Fu_9HI.js} +1 -1
- package/dist/client/assets/{html-B6EgAiSd.js → html-B1e6oxzK.js} +1 -1
- package/dist/client/assets/{html-derivative-DdinogQX.js → html-derivative-MofKXIVd.js} +1 -1
- package/dist/client/assets/{http-BSLxCgRq.js → http-Dk6S5pRD.js} +1 -1
- package/dist/client/assets/{hurl-pOsTwNfp.js → hurl-CroFYYJG.js} +1 -1
- package/dist/client/assets/{index-BHQ_8mvl.js → index-CVu-S6Yk.js} +2 -2
- package/dist/client/assets/{java-DRQLiiST.js → java-B89FYjqS.js} +1 -1
- package/dist/client/assets/{javascript-DvEK2-47.js → javascript-m6CO1Uiy.js} +1 -1
- package/dist/client/assets/{jinja-D2NYJ25y.js → jinja-DC9Wi41X.js} +1 -1
- package/dist/client/assets/{jison-DDZaLNAp.js → jison-6xiegwDk.js} +1 -1
- package/dist/client/assets/{json-TGR0NIWd.js → json-HA-96-qr.js} +1 -1
- package/dist/client/assets/{jsx-BjUoPYga.js → jsx-Wt1a8i8U.js} +1 -1
- package/dist/client/assets/{julia-C4gjSpFu.js → julia-7M93VBON.js} +1 -1
- package/dist/client/assets/{just-H351x5u_.js → just-CjfDLYLv.js} +1 -1
- package/dist/client/assets/{latex-BiTmf6gf.js → latex-CitsJ46x.js} +1 -1
- package/dist/client/assets/{liquid-86ufjRy-.js → liquid-C8VIFin8.js} +1 -1
- package/dist/client/assets/{lua-BNxR0F_8.js → lua-Ba2N7esc.js} +1 -1
- package/dist/client/assets/{marko-CvRxpRjM.js → marko-lTLvb2wu.js} +1 -1
- package/dist/client/assets/{mdc-CYbAIy2C.js → mdc-D6IV-8FD.js} +1 -1
- package/dist/client/assets/{nginx-egdgMq-F.js → nginx-Ch5AjE6S.js} +1 -1
- package/dist/client/assets/{nim-CXBJVz_w.js → nim-WmDDC6LW.js} +1 -1
- package/dist/client/assets/{perl-XRfMobzg.js → perl-CQv0gYuq.js} +1 -1
- package/dist/client/assets/{php-Br7a8uil.js → php-BJmH0qOB.js} +1 -1
- package/dist/client/assets/{pug-BVbbUVvy.js → pug-CsHPkzc9.js} +1 -1
- package/dist/client/assets/{qml-ByKvrL1j.js → qml-B36ecArG.js} +1 -1
- package/dist/client/assets/{r-mVoV0Ni6.js → r-D5Yi5Z4y.js} +1 -1
- package/dist/client/assets/{razor-T5O-9UJL.js → razor-CHAxVq4R.js} +1 -1
- package/dist/client/assets/{regexp-CioRuhuN.js → regexp-gfs--3M7.js} +1 -1
- package/dist/client/assets/{rst-V__uTudD.js → rst-ugdlp-hl.js} +1 -1
- package/dist/client/assets/{ruby-C_PuKPTI.js → ruby-CDRRW37j.js} +1 -1
- package/dist/client/assets/{sas-D_DqqQH4.js → sas-DZaNQaIP.js} +1 -1
- package/dist/client/assets/{scss-D-TjzZ4c.js → scss-CzWQEplj.js} +1 -1
- package/dist/client/assets/{shellscript-E5759VHu.js → shellscript-fgYvpu9N.js} +1 -1
- package/dist/client/assets/{shellsession-AESTM-Pv.js → shellsession-BoAohHh7.js} +1 -1
- package/dist/client/assets/{soy-QrbrrcDv.js → soy-DBzVgv9x.js} +1 -1
- package/dist/client/assets/{sql-0M8VcDHD.js → sql-BYXpAYTs.js} +1 -1
- package/dist/client/assets/{stata-CgeIpGtc.js → stata-I71MMY3p.js} +1 -1
- package/dist/client/assets/{surrealql-DBGwnZbw.js → surrealql-C9U8_1VO.js} +1 -1
- package/dist/client/assets/{svelte-Cv0PvUc_.js → svelte-VOFrPnWT.js} +1 -1
- package/dist/client/assets/{templ-B9t7xRE4.js → templ-BIaxAEtC.js} +1 -1
- package/dist/client/assets/{tex-DhZZ8dr2.js → tex-D1dwnBE5.js} +1 -1
- package/dist/client/assets/{ts-tags-BFv8sbnd.js → ts-tags-C0L2Q0r5.js} +1 -1
- package/dist/client/assets/{tsx-CXC9KSbY.js → tsx-BqcycEv1.js} +1 -1
- package/dist/client/assets/{twig-CM_OO66r.js → twig-vyWqOhpM.js} +1 -1
- package/dist/client/assets/{typescript-BdgOTaoD.js → typescript-B2YbovqG.js} +1 -1
- package/dist/client/assets/{vue-BnQhjnCm.js → vue-CbXxGdjo.js} +1 -1
- package/dist/client/assets/{vue-html-CNnGecRI.js → vue-html-DDX4KXW7.js} +1 -1
- package/dist/client/assets/{vue-vine-DCuMkRhK.js → vue-vine-DsyY1LR5.js} +1 -1
- package/dist/client/assets/{xml-CbTD7cB8.js → xml-Ddi0-r0D.js} +1 -1
- package/dist/client/assets/{xsl-uOqqo7cf.js → xsl-CsFcZHFS.js} +1 -1
- package/dist/client/assets/{yaml-BNrLoH59.js → yaml-tGJWoH6Y.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/sw.js +1 -1
- package/dist/server/analytics/engine.js +832 -0
- package/dist/server/assets.js +39 -0
- package/dist/server/auth/passphrase.js +70 -0
- package/dist/server/config.js +47 -0
- package/dist/server/daemon.js +535 -0
- package/dist/server/features/ralph-loop.js +138 -0
- package/dist/server/features/scheduler.js +260 -0
- package/dist/server/features/sticky-notes.js +99 -0
- package/dist/server/handlers/analytics.js +28 -0
- package/dist/server/handlers/attachment.js +158 -0
- package/dist/server/handlers/bookmarks.js +41 -0
- package/dist/server/handlers/chat.js +350 -0
- package/dist/server/handlers/editor.js +72 -0
- package/dist/server/handlers/fs.js +234 -0
- package/dist/server/handlers/loop.js +33 -0
- package/dist/server/handlers/memory.js +181 -0
- package/dist/server/handlers/mesh.js +322 -0
- package/dist/server/handlers/notes.js +36 -0
- package/dist/server/handlers/plugins.js +593 -0
- package/dist/server/handlers/project-settings.js +166 -0
- package/dist/server/handlers/scheduler.js +52 -0
- package/dist/server/handlers/session.js +194 -0
- package/dist/server/handlers/settings.js +148 -0
- package/dist/server/handlers/skills.js +360 -0
- package/dist/server/handlers/terminal.js +75 -0
- package/dist/server/handlers/themes.js +102 -0
- package/dist/server/handlers/update.js +124 -0
- package/dist/server/identity.js +45 -0
- package/dist/server/index.js +435 -0
- package/dist/server/logger.js +20 -0
- package/dist/server/mesh/connector.js +355 -0
- package/dist/server/mesh/crypto.js +88 -0
- package/dist/server/mesh/discovery.js +95 -0
- package/dist/server/mesh/pairing.js +104 -0
- package/dist/server/mesh/peers.js +54 -0
- package/dist/server/mesh/proxy.js +86 -0
- package/dist/server/mesh/session-sync.js +85 -0
- package/dist/server/project/bookmarks.js +77 -0
- package/dist/server/project/context-breakdown.js +279 -0
- package/dist/server/project/file-browser.js +97 -0
- package/dist/server/project/project-files.js +274 -0
- package/dist/server/project/registry.js +51 -0
- package/dist/server/project/sdk-bridge.js +960 -0
- package/dist/server/project/session.js +696 -0
- package/dist/server/project/terminal.js +87 -0
- package/dist/server/project/warmup.js +242 -0
- package/dist/server/push.js +87 -0
- package/dist/server/tls.js +50 -0
- package/dist/server/tui.js +83 -0
- package/dist/server/update-checker.js +119 -0
- package/dist/server/ws/broadcast.js +50 -0
- package/dist/server/ws/router.js +105 -0
- package/dist/server/ws/server.js +2 -0
- package/dist/shared/analytics.js +1 -0
- package/dist/shared/messages.js +1 -0
- package/dist/shared/models.js +1 -0
- package/dist/shared/project-settings.js +1 -0
- package/package.json +5 -8
- package/themes/alabaster.json +9 -0
- package/themes/amoled.json +20 -0
- package/themes/ayu-light.json +9 -0
- package/themes/catppuccin-latte.json +9 -0
- package/themes/catppuccin-mocha.json +9 -0
- package/themes/clay-light.json +10 -0
- package/themes/clay.json +10 -0
- package/themes/dracula.json +9 -0
- package/themes/everforest-light.json +9 -0
- package/themes/everforest.json +9 -0
- package/themes/github-light.json +9 -0
- package/themes/gruvbox-dark.json +9 -0
- package/themes/gruvbox-light.json +9 -0
- package/themes/horizon-light.json +9 -0
- package/themes/kanagawa-lotus.json +9 -0
- package/themes/kanagawa.json +9 -0
- package/themes/modus-operandi.json +9 -0
- package/themes/monokai.json +9 -0
- package/themes/nightfox.json +9 -0
- package/themes/nord-light.json +9 -0
- package/themes/nord.json +9 -0
- package/themes/one-dark.json +9 -0
- package/themes/one-light.json +9 -0
- package/themes/palenight.json +9 -0
- package/themes/paper.json +9 -0
- package/themes/penumbra-light.json +9 -0
- package/themes/poimandres.json +9 -0
- package/themes/quiet-light.json +9 -0
- package/themes/rose-pine-dawn.json +9 -0
- package/themes/rose-pine.json +9 -0
- package/themes/solarized-dark.json +9 -0
- package/themes/solarized-light.json +9 -0
- package/themes/synthwave84.json +9 -0
- package/themes/tokyo-night-light.json +9 -0
- package/themes/tokyo-night.json +9 -0
- package/themes/vesper.json +9 -0
- package/index.html +0 -20
- package/public/icons/icon-192.svg +0 -11
- package/public/icons/icon-512.svg +0 -11
- package/public/sw-push.js +0 -53
- package/src/client/App.tsx +0 -42
- package/src/client/commands.ts +0 -36
- package/src/client/components/analytics/AnalyticsView.tsx +0 -244
- package/src/client/components/analytics/ChartCard.tsx +0 -194
- package/src/client/components/analytics/PeriodSelector.tsx +0 -42
- package/src/client/components/analytics/QuickStats.tsx +0 -122
- package/src/client/components/analytics/chartTokens.ts +0 -188
- package/src/client/components/analytics/charts/ActivityCalendar.tsx +0 -204
- package/src/client/components/analytics/charts/CacheEfficiencyChart.tsx +0 -56
- package/src/client/components/analytics/charts/ContextUtilizationChart.tsx +0 -106
- package/src/client/components/analytics/charts/CostAreaChart.tsx +0 -79
- package/src/client/components/analytics/charts/CostDistributionChart.tsx +0 -59
- package/src/client/components/analytics/charts/CostDonutChart.tsx +0 -84
- package/src/client/components/analytics/charts/CumulativeCostChart.tsx +0 -59
- package/src/client/components/analytics/charts/DailySummaryCards.tsx +0 -86
- package/src/client/components/analytics/charts/HourlyHeatmap.tsx +0 -133
- package/src/client/components/analytics/charts/NodeFleetOverview.tsx +0 -89
- package/src/client/components/analytics/charts/PermissionBreakdown.tsx +0 -98
- package/src/client/components/analytics/charts/ProjectRadar.tsx +0 -126
- package/src/client/components/analytics/charts/ResponseTimeScatter.tsx +0 -96
- package/src/client/components/analytics/charts/SessionBubbleChart.tsx +0 -114
- package/src/client/components/analytics/charts/SessionComplexityList.tsx +0 -65
- package/src/client/components/analytics/charts/SessionTimeline.tsx +0 -107
- package/src/client/components/analytics/charts/TokenFlowChart.tsx +0 -78
- package/src/client/components/analytics/charts/TokenSankeyChart.tsx +0 -93
- package/src/client/components/analytics/charts/ToolSunburst.tsx +0 -123
- package/src/client/components/analytics/charts/ToolTreemap.tsx +0 -110
- package/src/client/components/auth/PassphrasePrompt.tsx +0 -70
- package/src/client/components/chat/AttachmentChips.tsx +0 -116
- package/src/client/components/chat/ChatInput.tsx +0 -533
- package/src/client/components/chat/ChatView.tsx +0 -1076
- package/src/client/components/chat/CommandPalette.tsx +0 -162
- package/src/client/components/chat/ElicitationCard.tsx +0 -238
- package/src/client/components/chat/Message.tsx +0 -825
- package/src/client/components/chat/ModelSelector.tsx +0 -108
- package/src/client/components/chat/PermissionModeSelector.tsx +0 -41
- package/src/client/components/chat/PromptQuestion.tsx +0 -271
- package/src/client/components/chat/StatusBar.tsx +0 -50
- package/src/client/components/chat/TodoCard.tsx +0 -57
- package/src/client/components/chat/ToolGroup.tsx +0 -129
- package/src/client/components/chat/ToolResultRenderer.tsx +0 -348
- package/src/client/components/chat/VoiceRecorder.tsx +0 -85
- package/src/client/components/chat/toolSummary.ts +0 -41
- package/src/client/components/dashboard/DashboardView.tsx +0 -200
- package/src/client/components/dashboard/ProjectDashboardView.tsx +0 -179
- package/src/client/components/mesh/NodeBadge.tsx +0 -24
- package/src/client/components/mesh/PairingDialog.tsx +0 -340
- package/src/client/components/project-settings/ProjectClaude.tsx +0 -318
- package/src/client/components/project-settings/ProjectEnvironment.tsx +0 -235
- package/src/client/components/project-settings/ProjectGeneral.tsx +0 -76
- package/src/client/components/project-settings/ProjectMcp.tsx +0 -232
- package/src/client/components/project-settings/ProjectMemory.tsx +0 -488
- package/src/client/components/project-settings/ProjectNotifications.tsx +0 -48
- package/src/client/components/project-settings/ProjectPermissions.tsx +0 -209
- package/src/client/components/project-settings/ProjectPlugins.tsx +0 -117
- package/src/client/components/project-settings/ProjectRules.tsx +0 -286
- package/src/client/components/project-settings/ProjectSettingsView.tsx +0 -117
- package/src/client/components/project-settings/ProjectSkills.tsx +0 -91
- package/src/client/components/settings/Appearance.tsx +0 -275
- package/src/client/components/settings/BudgetSettings.tsx +0 -165
- package/src/client/components/settings/ClaudeSettings.tsx +0 -175
- package/src/client/components/settings/Editor.tsx +0 -123
- package/src/client/components/settings/Environment.tsx +0 -185
- package/src/client/components/settings/GlobalMcp.tsx +0 -216
- package/src/client/components/settings/GlobalMemory.tsx +0 -19
- package/src/client/components/settings/GlobalPlugins.tsx +0 -806
- package/src/client/components/settings/GlobalRules.tsx +0 -149
- package/src/client/components/settings/GlobalSkills.tsx +0 -140
- package/src/client/components/settings/MeshStatus.tsx +0 -183
- package/src/client/components/settings/Notifications.tsx +0 -123
- package/src/client/components/settings/SettingsView.tsx +0 -75
- package/src/client/components/settings/SkillMarketplace.tsx +0 -175
- package/src/client/components/settings/ThemePreview.tsx +0 -140
- package/src/client/components/settings/ThemeWizard.tsx +0 -405
- package/src/client/components/settings/mcp-shared.tsx +0 -194
- package/src/client/components/settings/skill-shared.tsx +0 -186
- package/src/client/components/setup/SetupWizard.tsx +0 -755
- package/src/client/components/sidebar/AddProjectModal.tsx +0 -438
- package/src/client/components/sidebar/NodeSettingsModal.tsx +0 -206
- package/src/client/components/sidebar/ProjectDropdown.tsx +0 -211
- package/src/client/components/sidebar/ProjectRail.tsx +0 -353
- package/src/client/components/sidebar/SearchFilter.tsx +0 -52
- package/src/client/components/sidebar/SessionList.tsx +0 -599
- package/src/client/components/sidebar/SettingsSidebar.tsx +0 -139
- package/src/client/components/sidebar/Sidebar.tsx +0 -469
- package/src/client/components/sidebar/UserIsland.tsx +0 -282
- package/src/client/components/sidebar/UserMenu.tsx +0 -107
- package/src/client/components/ui/CommandPalette.tsx +0 -321
- package/src/client/components/ui/ContextMenu.tsx +0 -153
- package/src/client/components/ui/ErrorBoundary.tsx +0 -56
- package/src/client/components/ui/IconPicker.tsx +0 -184
- package/src/client/components/ui/KeyboardShortcuts.tsx +0 -129
- package/src/client/components/ui/LatticeLogomark.tsx +0 -19
- package/src/client/components/ui/NodeDisconnectedOverlay.tsx +0 -35
- package/src/client/components/ui/PopupMenu.tsx +0 -120
- package/src/client/components/ui/SaveFooter.tsx +0 -63
- package/src/client/components/ui/Toast.tsx +0 -132
- package/src/client/components/ui/UpdateBanner.tsx +0 -110
- package/src/client/components/ui/UpdatePrompt.tsx +0 -47
- package/src/client/components/workspace/BookmarksView.tsx +0 -156
- package/src/client/components/workspace/FileBrowser.tsx +0 -174
- package/src/client/components/workspace/FileTree.tsx +0 -129
- package/src/client/components/workspace/FileViewer.tsx +0 -211
- package/src/client/components/workspace/NoteCard.tsx +0 -120
- package/src/client/components/workspace/NotesView.tsx +0 -102
- package/src/client/components/workspace/ScheduledTasksView.tsx +0 -117
- package/src/client/components/workspace/SplitPane.tsx +0 -81
- package/src/client/components/workspace/TabBar.tsx +0 -170
- package/src/client/components/workspace/TaskCard.tsx +0 -159
- package/src/client/components/workspace/TaskEditModal.tsx +0 -129
- package/src/client/components/workspace/TerminalInstance.tsx +0 -171
- package/src/client/components/workspace/TerminalView.tsx +0 -110
- package/src/client/components/workspace/WorkspaceView.tsx +0 -141
- package/src/client/hooks/useAnalytics.ts +0 -84
- package/src/client/hooks/useAttachments.ts +0 -313
- package/src/client/hooks/useBookmarks.ts +0 -57
- package/src/client/hooks/useEditorConfig.ts +0 -28
- package/src/client/hooks/useFocusTrap.ts +0 -74
- package/src/client/hooks/useIdleDetection.ts +0 -50
- package/src/client/hooks/useInstallPrompt.ts +0 -53
- package/src/client/hooks/useMesh.ts +0 -89
- package/src/client/hooks/useNotifications.ts +0 -54
- package/src/client/hooks/useOnline.ts +0 -6
- package/src/client/hooks/useProjectSettings.ts +0 -56
- package/src/client/hooks/useProjects.ts +0 -98
- package/src/client/hooks/usePushNotifications.ts +0 -92
- package/src/client/hooks/useSaveState.ts +0 -65
- package/src/client/hooks/useSession.ts +0 -580
- package/src/client/hooks/useSidebar.ts +0 -90
- package/src/client/hooks/useSkills.ts +0 -30
- package/src/client/hooks/useSpinnerVerb.ts +0 -36
- package/src/client/hooks/useSwipeDrawer.ts +0 -299
- package/src/client/hooks/useTheme.ts +0 -114
- package/src/client/hooks/useTimeTick.ts +0 -35
- package/src/client/hooks/useVoiceRecorder.ts +0 -169
- package/src/client/hooks/useWebSocket.ts +0 -27
- package/src/client/hooks/useWorkspace.ts +0 -57
- package/src/client/lib/theme-derive.ts +0 -196
- package/src/client/lib/workspace-url.ts +0 -219
- package/src/client/main.tsx +0 -10
- package/src/client/providers/WebSocketProvider.tsx +0 -186
- package/src/client/router.tsx +0 -578
- package/src/client/stores/analytics.ts +0 -68
- package/src/client/stores/bookmarks.ts +0 -45
- package/src/client/stores/mesh.ts +0 -78
- package/src/client/stores/session.ts +0 -569
- package/src/client/stores/sidebar.ts +0 -530
- package/src/client/stores/theme.ts +0 -44
- package/src/client/stores/workspace.ts +0 -518
- package/src/client/styles/global.css +0 -391
- package/src/client/styles/theme-vars.css +0 -18
- package/src/client/themes/index.ts +0 -105
- package/src/client/utils/editorUrl.ts +0 -55
- package/src/client/utils/findDuplicateKeys.ts +0 -12
- package/src/client/utils/formatSessionTitle.ts +0 -17
- package/src/client/vite-env.d.ts +0 -6
- package/src/server/analytics/engine.ts +0 -920
- package/src/server/assets.ts +0 -45
- package/src/server/auth/passphrase.ts +0 -78
- package/src/server/config.ts +0 -55
- package/src/server/daemon.ts +0 -567
- package/src/server/features/ralph-loop.ts +0 -173
- package/src/server/features/scheduler.ts +0 -304
- package/src/server/features/sticky-notes.ts +0 -104
- package/src/server/handlers/analytics.ts +0 -39
- package/src/server/handlers/attachment.ts +0 -189
- package/src/server/handlers/bookmarks.ts +0 -50
- package/src/server/handlers/chat.ts +0 -381
- package/src/server/handlers/editor.ts +0 -76
- package/src/server/handlers/fs.ts +0 -251
- package/src/server/handlers/loop.ts +0 -37
- package/src/server/handlers/memory.ts +0 -182
- package/src/server/handlers/mesh.ts +0 -362
- package/src/server/handlers/notes.ts +0 -47
- package/src/server/handlers/plugins.ts +0 -655
- package/src/server/handlers/project-settings.ts +0 -180
- package/src/server/handlers/scheduler.ts +0 -64
- package/src/server/handlers/session.ts +0 -226
- package/src/server/handlers/settings.ts +0 -157
- package/src/server/handlers/skills.ts +0 -378
- package/src/server/handlers/terminal.ts +0 -88
- package/src/server/handlers/themes.ts +0 -121
- package/src/server/handlers/update.ts +0 -133
- package/src/server/identity.ts +0 -56
- package/src/server/index.ts +0 -457
- package/src/server/logger.ts +0 -21
- package/src/server/mesh/connector.ts +0 -419
- package/src/server/mesh/crypto.ts +0 -106
- package/src/server/mesh/discovery.ts +0 -126
- package/src/server/mesh/pairing.ts +0 -123
- package/src/server/mesh/peers.ts +0 -60
- package/src/server/mesh/proxy.ts +0 -106
- package/src/server/mesh/session-sync.ts +0 -107
- package/src/server/project/bookmarks.ts +0 -83
- package/src/server/project/context-breakdown.ts +0 -307
- package/src/server/project/file-browser.ts +0 -106
- package/src/server/project/project-files.ts +0 -267
- package/src/server/project/pty-worker.cjs +0 -83
- package/src/server/project/registry.ts +0 -57
- package/src/server/project/sdk-bridge.ts +0 -1100
- package/src/server/project/session.ts +0 -723
- package/src/server/project/terminal.ts +0 -111
- package/src/server/project/warmup.ts +0 -285
- package/src/server/push.ts +0 -121
- package/src/server/tls.ts +0 -65
- package/src/server/tui.ts +0 -103
- package/src/server/update-checker.ts +0 -147
- package/src/server/ws/broadcast.ts +0 -61
- package/src/server/ws/router.ts +0 -123
- package/src/server/ws/server.ts +0 -2
- package/src/shared/analytics.ts +0 -40
- package/src/shared/messages.ts +0 -1302
- package/src/shared/models.ts +0 -255
- package/src/shared/project-settings.ts +0 -45
- package/tsconfig.json +0 -25
- package/vite.config.ts +0 -71
- /package/{src/server/runtime.ts → dist/server/runtime.js} +0 -0
- /package/{src/shared/constants.ts → dist/shared/constants.js} +0 -0
- /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
|
-
}
|