nastechai-desktop 18.1.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/.prettierrc +11 -0
- package/DESIGN.md +167 -0
- package/README.md +141 -0
- package/assets/icon.icns +0 -0
- package/assets/icon.ico +0 -0
- package/assets/icon.png +0 -0
- package/components.json +21 -0
- package/electron/backend-env.cjs +112 -0
- package/electron/backend-env.test.cjs +111 -0
- package/electron/backend-probes.cjs +106 -0
- package/electron/backend-probes.test.cjs +82 -0
- package/electron/backend-ready.cjs +66 -0
- package/electron/bootstrap-platform.cjs +91 -0
- package/electron/bootstrap-platform.test.cjs +111 -0
- package/electron/bootstrap-runner.cjs +720 -0
- package/electron/bootstrap-runner.test.cjs +138 -0
- package/electron/connection-config.cjs +254 -0
- package/electron/connection-config.test.cjs +329 -0
- package/electron/dashboard-token.cjs +99 -0
- package/electron/dashboard-token.test.cjs +142 -0
- package/electron/desktop-uninstall.cjs +232 -0
- package/electron/desktop-uninstall.test.cjs +246 -0
- package/electron/entitlements.mac.inherit.plist +14 -0
- package/electron/entitlements.mac.plist +14 -0
- package/electron/fs-read-dir.cjs +109 -0
- package/electron/fs-read-dir.test.cjs +364 -0
- package/electron/gateway-ws-probe.cjs +188 -0
- package/electron/gateway-ws-probe.test.cjs +122 -0
- package/electron/git-root.cjs +54 -0
- package/electron/git-root.test.cjs +40 -0
- package/electron/git-worktrees.cjs +174 -0
- package/electron/hardening.cjs +184 -0
- package/electron/hardening.test.cjs +116 -0
- package/electron/main.cjs +5762 -0
- package/electron/oauth-net-request.cjs +20 -0
- package/electron/oauth-net-request.test.cjs +34 -0
- package/electron/preload.cjs +135 -0
- package/electron/session-windows.cjs +99 -0
- package/electron/session-windows.test.cjs +177 -0
- package/electron/update-remote.cjs +56 -0
- package/electron/update-remote.test.cjs +78 -0
- package/electron/vscode-marketplace.cjs +331 -0
- package/electron/vscode-marketplace.test.cjs +113 -0
- package/electron/windows-child-process.test.cjs +57 -0
- package/electron/windows-user-env.cjs +76 -0
- package/electron/windows-user-env.test.cjs +90 -0
- package/electron/workspace-cwd.cjs +38 -0
- package/electron/workspace-cwd.test.cjs +45 -0
- package/eslint.config.mjs +122 -0
- package/index.html +17 -0
- package/package.json +254 -0
- package/pr-assets/session-source-folders.png +0 -0
- package/preview-demo.html +65 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/ds-assets/filler-bg0.jpg +0 -0
- package/public/nastech-frames/nastech-frame-0.png +0 -0
- package/public/nastech-frames/nastech-frame-1.png +0 -0
- package/public/nastech-frames/nastech-frame-2.png +0 -0
- package/public/nastech-frames/nastech-frame-3.png +0 -0
- package/public/nastech-frames/nastech-frame-4.png +0 -0
- package/public/nastech-frames/nastech-frame-5.png +0 -0
- package/public/nastech-frames/nastech-frame-6.png +0 -0
- package/public/nastech-frames/nastech-frame-7.png +0 -0
- package/public/nastech-girl.jpg +0 -0
- package/public/nastech-sprite.png +0 -0
- package/public/nastech.png +0 -0
- package/scripts/after-pack.cjs +41 -0
- package/scripts/assert-dist-built.cjs +70 -0
- package/scripts/assert-dist-built.test.cjs +84 -0
- package/scripts/assert-root-install.cjs +13 -0
- package/scripts/before-build.cjs +11 -0
- package/scripts/before-pack.cjs +78 -0
- package/scripts/before-pack.test.cjs +53 -0
- package/scripts/click-session.mjs +51 -0
- package/scripts/dev-no-hmr.mjs +22 -0
- package/scripts/diag-jump.mjs +115 -0
- package/scripts/diag-scroll-reset.mjs +229 -0
- package/scripts/eval.mjs +21 -0
- package/scripts/leak-typing.mjs +222 -0
- package/scripts/measure-jump.mjs +108 -0
- package/scripts/measure-latency.mjs +184 -0
- package/scripts/measure-real-stream.mjs +252 -0
- package/scripts/measure-submit.mjs +179 -0
- package/scripts/measure-synthetic-stream.mjs +322 -0
- package/scripts/notarize-artifact.cjs +77 -0
- package/scripts/notarize.cjs +100 -0
- package/scripts/patch-electron-builder-mac-binary.cjs +59 -0
- package/scripts/probe-renderer.mjs +38 -0
- package/scripts/probe-thread.mjs +40 -0
- package/scripts/profile-long-stream.mjs +191 -0
- package/scripts/profile-real-stream.mjs +137 -0
- package/scripts/profile-synth-stream.mjs +103 -0
- package/scripts/profile-typing-lag.md +381 -0
- package/scripts/profile-typing.mjs +260 -0
- package/scripts/reload-renderer.mjs +25 -0
- package/scripts/reload.mjs +36 -0
- package/scripts/set-exe-identity.cjs +94 -0
- package/scripts/stage-native-deps.cjs +159 -0
- package/scripts/test-desktop.mjs +425 -0
- package/scripts/write-build-stamp.cjs +126 -0
- package/src/app/agents/index.tsx +398 -0
- package/src/app/artifacts/index.test.ts +62 -0
- package/src/app/artifacts/index.tsx +906 -0
- package/src/app/chat/chat-drop-overlay.tsx +48 -0
- package/src/app/chat/chat-swap-overlay.tsx +47 -0
- package/src/app/chat/composer/attachments.tsx +114 -0
- package/src/app/chat/composer/completion-drawer.tsx +63 -0
- package/src/app/chat/composer/context-menu.tsx +172 -0
- package/src/app/chat/composer/controls.tsx +289 -0
- package/src/app/chat/composer/drop-affordance.ts +2 -0
- package/src/app/chat/composer/enter-submit-dom-race.test.tsx +218 -0
- package/src/app/chat/composer/focus.ts +134 -0
- package/src/app/chat/composer/help-hint.tsx +59 -0
- package/src/app/chat/composer/hooks/use-at-completions.ts +141 -0
- package/src/app/chat/composer/hooks/use-live-completion-adapter.ts +119 -0
- package/src/app/chat/composer/hooks/use-mic-recorder.ts +291 -0
- package/src/app/chat/composer/hooks/use-slash-completions.ts +114 -0
- package/src/app/chat/composer/hooks/use-voice-conversation.ts +390 -0
- package/src/app/chat/composer/hooks/use-voice-recorder.ts +116 -0
- package/src/app/chat/composer/ime-composition-dom-repro.test.tsx +108 -0
- package/src/app/chat/composer/index.tsx +1611 -0
- package/src/app/chat/composer/inline-refs.ts +138 -0
- package/src/app/chat/composer/model-pill.tsx +86 -0
- package/src/app/chat/composer/queue-panel.tsx +130 -0
- package/src/app/chat/composer/rich-editor.test.ts +18 -0
- package/src/app/chat/composer/rich-editor.ts +165 -0
- package/src/app/chat/composer/skin-slash-popover.tsx +61 -0
- package/src/app/chat/composer/slash-nav-dom-repro.test.tsx +186 -0
- package/src/app/chat/composer/status-stack/index.tsx +202 -0
- package/src/app/chat/composer/status-stack/status-row.tsx +155 -0
- package/src/app/chat/composer/text-utils.test.ts +77 -0
- package/src/app/chat/composer/text-utils.ts +107 -0
- package/src/app/chat/composer/trigger-popover.test.tsx +42 -0
- package/src/app/chat/composer/trigger-popover.tsx +116 -0
- package/src/app/chat/composer/types.ts +64 -0
- package/src/app/chat/composer/url-dialog.tsx +82 -0
- package/src/app/chat/composer/voice-activity.tsx +252 -0
- package/src/app/chat/hooks/use-composer-actions.test.ts +57 -0
- package/src/app/chat/hooks/use-composer-actions.ts +525 -0
- package/src/app/chat/hooks/use-file-drop-zone.ts +118 -0
- package/src/app/chat/index.tsx +390 -0
- package/src/app/chat/perf-probe.tsx +269 -0
- package/src/app/chat/right-rail/index.ts +1 -0
- package/src/app/chat/right-rail/preview-console-state.ts +82 -0
- package/src/app/chat/right-rail/preview-console.tsx +290 -0
- package/src/app/chat/right-rail/preview-file.tsx +559 -0
- package/src/app/chat/right-rail/preview-pane.test.tsx +43 -0
- package/src/app/chat/right-rail/preview-pane.tsx +657 -0
- package/src/app/chat/right-rail/preview.tsx +171 -0
- package/src/app/chat/scroll-to-bottom-button.test.tsx +67 -0
- package/src/app/chat/scroll-to-bottom-button.tsx +74 -0
- package/src/app/chat/sidebar/cron-jobs-section.tsx +325 -0
- package/src/app/chat/sidebar/index.tsx +1219 -0
- package/src/app/chat/sidebar/load-more-row.tsx +30 -0
- package/src/app/chat/sidebar/order.test.ts +21 -0
- package/src/app/chat/sidebar/order.ts +17 -0
- package/src/app/chat/sidebar/profile-switcher.tsx +516 -0
- package/src/app/chat/sidebar/session-actions-menu.tsx +264 -0
- package/src/app/chat/sidebar/session-row.tsx +257 -0
- package/src/app/chat/sidebar/virtual-session-list.tsx +154 -0
- package/src/app/chat/sidebar/workspace-groups.test.ts +149 -0
- package/src/app/chat/sidebar/workspace-groups.ts +326 -0
- package/src/app/chat/thread-loading.test.ts +34 -0
- package/src/app/chat/thread-loading.ts +26 -0
- package/src/app/command-center/index.tsx +654 -0
- package/src/app/command-palette/index.tsx +513 -0
- package/src/app/command-palette/marketplace-theme-page.tsx +157 -0
- package/src/app/cron/index.tsx +942 -0
- package/src/app/cron/job-state.ts +29 -0
- package/src/app/desktop-controller.tsx +938 -0
- package/src/app/floating-hud.ts +22 -0
- package/src/app/gateway/hooks/use-gateway-boot.test.tsx +265 -0
- package/src/app/gateway/hooks/use-gateway-boot.ts +387 -0
- package/src/app/gateway/hooks/use-gateway-request.ts +138 -0
- package/src/app/hooks/use-keybinds.ts +186 -0
- package/src/app/hooks/use-refresh-hotkey.ts +45 -0
- package/src/app/hooks/use-route-enum-param.ts +38 -0
- package/src/app/index.tsx +1 -0
- package/src/app/layout-constants.ts +13 -0
- package/src/app/messaging/index.tsx +648 -0
- package/src/app/messaging/platform-icon.tsx +93 -0
- package/src/app/model-picker-overlay.tsx +42 -0
- package/src/app/model-visibility-overlay.tsx +31 -0
- package/src/app/overlays/overlay-chrome.tsx +66 -0
- package/src/app/overlays/overlay-search-input.tsx +33 -0
- package/src/app/overlays/overlay-split-layout.tsx +130 -0
- package/src/app/overlays/overlay-view.tsx +91 -0
- package/src/app/page-search-shell.tsx +75 -0
- package/src/app/profiles/create-profile-dialog.tsx +154 -0
- package/src/app/profiles/delete-profile-dialog.tsx +65 -0
- package/src/app/profiles/index.tsx +671 -0
- package/src/app/profiles/rename-profile-dialog.tsx +125 -0
- package/src/app/right-sidebar/files/dnd-manager.ts +27 -0
- package/src/app/right-sidebar/files/ipc.test.ts +100 -0
- package/src/app/right-sidebar/files/ipc.ts +161 -0
- package/src/app/right-sidebar/files/remote-picker.tsx +177 -0
- package/src/app/right-sidebar/files/tree.tsx +224 -0
- package/src/app/right-sidebar/files/use-project-tree.test.ts +190 -0
- package/src/app/right-sidebar/files/use-project-tree.ts +268 -0
- package/src/app/right-sidebar/index.test.tsx +75 -0
- package/src/app/right-sidebar/index.tsx +395 -0
- package/src/app/right-sidebar/store.ts +15 -0
- package/src/app/right-sidebar/terminal/buffer.ts +65 -0
- package/src/app/right-sidebar/terminal/index.tsx +98 -0
- package/src/app/right-sidebar/terminal/persistent.tsx +122 -0
- package/src/app/right-sidebar/terminal/selection.ts +75 -0
- package/src/app/right-sidebar/terminal/use-terminal-session.ts +504 -0
- package/src/app/routes.ts +88 -0
- package/src/app/session/hooks/use-context-suggestions.ts +58 -0
- package/src/app/session/hooks/use-cwd-actions.ts +109 -0
- package/src/app/session/hooks/use-message-stream.ts +957 -0
- package/src/app/session/hooks/use-model-controls.test.tsx +198 -0
- package/src/app/session/hooks/use-model-controls.ts +106 -0
- package/src/app/session/hooks/use-nastech-config.ts +74 -0
- package/src/app/session/hooks/use-preview-routing.test.tsx +168 -0
- package/src/app/session/hooks/use-preview-routing.ts +223 -0
- package/src/app/session/hooks/use-prompt-actions.test.tsx +316 -0
- package/src/app/session/hooks/use-prompt-actions.ts +1030 -0
- package/src/app/session/hooks/use-route-resume.test.tsx +136 -0
- package/src/app/session/hooks/use-route-resume.ts +115 -0
- package/src/app/session/hooks/use-session-actions.test.tsx +119 -0
- package/src/app/session/hooks/use-session-actions.ts +885 -0
- package/src/app/session/hooks/use-session-state-cache.test.tsx +118 -0
- package/src/app/session/hooks/use-session-state-cache.ts +191 -0
- package/src/app/session-picker-overlay.tsx +32 -0
- package/src/app/session-switcher.tsx +107 -0
- package/src/app/settings/about-settings.tsx +173 -0
- package/src/app/settings/appearance-settings.tsx +162 -0
- package/src/app/settings/config-settings.tsx +384 -0
- package/src/app/settings/constants.ts +545 -0
- package/src/app/settings/credential-key-ui.tsx +373 -0
- package/src/app/settings/env-credentials.tsx +198 -0
- package/src/app/settings/env-var-actions-menu.tsx +136 -0
- package/src/app/settings/field-copy.ts +56 -0
- package/src/app/settings/gateway-settings.tsx +620 -0
- package/src/app/settings/helpers.test.ts +138 -0
- package/src/app/settings/helpers.ts +151 -0
- package/src/app/settings/index.tsx +237 -0
- package/src/app/settings/keys-settings.tsx +96 -0
- package/src/app/settings/mcp-settings.tsx +271 -0
- package/src/app/settings/model-settings.test.tsx +157 -0
- package/src/app/settings/model-settings.tsx +559 -0
- package/src/app/settings/notifications-settings.tsx +150 -0
- package/src/app/settings/primitives.tsx +115 -0
- package/src/app/settings/providers-settings.test.tsx +100 -0
- package/src/app/settings/providers-settings.tsx +258 -0
- package/src/app/settings/sessions-settings.tsx +276 -0
- package/src/app/settings/toolset-config-panel.test.tsx +289 -0
- package/src/app/settings/toolset-config-panel.tsx +449 -0
- package/src/app/settings/types.ts +42 -0
- package/src/app/settings/uninstall-section.tsx +185 -0
- package/src/app/settings/use-deep-link-highlight.ts +60 -0
- package/src/app/shell/app-shell.tsx +167 -0
- package/src/app/shell/gateway-menu-panel.tsx +150 -0
- package/src/app/shell/hooks/use-overlay-routing.ts +71 -0
- package/src/app/shell/hooks/use-status-snapshot.ts +57 -0
- package/src/app/shell/hooks/use-statusbar-items.tsx +403 -0
- package/src/app/shell/keybind-panel.tsx +220 -0
- package/src/app/shell/model-edit-submenu.test.tsx +84 -0
- package/src/app/shell/model-edit-submenu.tsx +245 -0
- package/src/app/shell/model-menu-panel.tsx +295 -0
- package/src/app/shell/sidebar-label.tsx +22 -0
- package/src/app/shell/statusbar-controls.tsx +185 -0
- package/src/app/shell/titlebar-controls.tsx +244 -0
- package/src/app/shell/titlebar.test.ts +26 -0
- package/src/app/shell/titlebar.ts +45 -0
- package/src/app/shell/use-group-registry.ts +39 -0
- package/src/app/skills/index.test.tsx +103 -0
- package/src/app/skills/index.tsx +371 -0
- package/src/app/types.ts +99 -0
- package/src/app/updates-overlay.tsx +369 -0
- package/src/components/Backdrop.tsx +114 -0
- package/src/components/assistant-ui/ansi-text.tsx +34 -0
- package/src/components/assistant-ui/clarify-tool.tsx +281 -0
- package/src/components/assistant-ui/directive-text.test.ts +39 -0
- package/src/components/assistant-ui/directive-text.tsx +389 -0
- package/src/components/assistant-ui/markdown-text.test.ts +204 -0
- package/src/components/assistant-ui/markdown-text.tsx +497 -0
- package/src/components/assistant-ui/message-render-boundary.test.tsx +80 -0
- package/src/components/assistant-ui/message-render-boundary.tsx +48 -0
- package/src/components/assistant-ui/streaming.test.tsx +739 -0
- package/src/components/assistant-ui/thread-list.tsx +307 -0
- package/src/components/assistant-ui/thread-virtualizer.tsx +512 -0
- package/src/components/assistant-ui/thread.tsx +1474 -0
- package/src/components/assistant-ui/todo-tool.tsx +109 -0
- package/src/components/assistant-ui/tool-approval-group.test.tsx +158 -0
- package/src/components/assistant-ui/tool-approval.test.tsx +81 -0
- package/src/components/assistant-ui/tool-approval.tsx +209 -0
- package/src/components/assistant-ui/tool-fallback-model.test.ts +66 -0
- package/src/components/assistant-ui/tool-fallback-model.ts +1368 -0
- package/src/components/assistant-ui/tool-fallback.tsx +466 -0
- package/src/components/assistant-ui/tooltip-icon-button.tsx +33 -0
- package/src/components/assistant-ui/user-message-edit.test.tsx +141 -0
- package/src/components/assistant-ui/user-message-text.tsx +150 -0
- package/src/components/boot-failure-overlay.tsx +246 -0
- package/src/components/boot-failure-reauth.test.ts +100 -0
- package/src/components/boot-failure-reauth.ts +81 -0
- package/src/components/brand-mark.tsx +19 -0
- package/src/components/chat/activity-timer-text.tsx +24 -0
- package/src/components/chat/activity-timer.test.tsx +43 -0
- package/src/components/chat/activity-timer.ts +64 -0
- package/src/components/chat/code-card.tsx +78 -0
- package/src/components/chat/compact-markdown.tsx +113 -0
- package/src/components/chat/composer-dock.ts +31 -0
- package/src/components/chat/diff-lines.tsx +54 -0
- package/src/components/chat/disclosure-row.tsx +63 -0
- package/src/components/chat/generated-image-context.tsx +19 -0
- package/src/components/chat/generated-image-result.tsx +174 -0
- package/src/components/chat/image-generation-placeholder.tsx +279 -0
- package/src/components/chat/intro-copy.jsonl +75 -0
- package/src/components/chat/intro.tsx +182 -0
- package/src/components/chat/preview-attachment.tsx +125 -0
- package/src/components/chat/shiki-highlighter.tsx +107 -0
- package/src/components/chat/status-row.tsx +70 -0
- package/src/components/chat/status-section.tsx +42 -0
- package/src/components/chat/terminal-output.tsx +50 -0
- package/src/components/chat/zoomable-image.tsx +177 -0
- package/src/components/desktop-install-overlay.tsx +595 -0
- package/src/components/desktop-onboarding-overlay.test.tsx +100 -0
- package/src/components/desktop-onboarding-overlay.tsx +1286 -0
- package/src/components/error-boundary.tsx +77 -0
- package/src/components/gateway-connecting-overlay.test.tsx +143 -0
- package/src/components/gateway-connecting-overlay.tsx +183 -0
- package/src/components/haptics-provider.tsx +19 -0
- package/src/components/language-switcher.test.tsx +53 -0
- package/src/components/language-switcher.tsx +175 -0
- package/src/components/model-picker.tsx +340 -0
- package/src/components/model-visibility-dialog.tsx +155 -0
- package/src/components/notifications.tsx +196 -0
- package/src/components/page-loader.tsx +34 -0
- package/src/components/pane-shell/context.ts +14 -0
- package/src/components/pane-shell/index.ts +4 -0
- package/src/components/pane-shell/pane-shell.test.tsx +333 -0
- package/src/components/pane-shell/pane-shell.tsx +330 -0
- package/src/components/prompt-overlays.tsx +234 -0
- package/src/components/session-picker.tsx +108 -0
- package/src/components/status-dot.tsx +26 -0
- package/src/components/ui/action-status.tsx +25 -0
- package/src/components/ui/alert.tsx +53 -0
- package/src/components/ui/badge.tsx +35 -0
- package/src/components/ui/braille-spinner.tsx +61 -0
- package/src/components/ui/button.tsx +81 -0
- package/src/components/ui/checkbox.tsx +27 -0
- package/src/components/ui/codicon.tsx +20 -0
- package/src/components/ui/command.tsx +111 -0
- package/src/components/ui/confirm-dialog.tsx +109 -0
- package/src/components/ui/context-menu.tsx +141 -0
- package/src/components/ui/control.ts +25 -0
- package/src/components/ui/copy-button.test.tsx +36 -0
- package/src/components/ui/copy-button.tsx +229 -0
- package/src/components/ui/dialog.tsx +152 -0
- package/src/components/ui/disclosure-caret.tsx +20 -0
- package/src/components/ui/dropdown-menu.tsx +291 -0
- package/src/components/ui/error-state.tsx +50 -0
- package/src/components/ui/fade-text.tsx +110 -0
- package/src/components/ui/glyph-spinner.tsx +63 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/kbd.tsx +37 -0
- package/src/components/ui/loader.tsx +558 -0
- package/src/components/ui/log-view.tsx +17 -0
- package/src/components/ui/pagination.tsx +114 -0
- package/src/components/ui/popover.tsx +44 -0
- package/src/components/ui/scroll-area.tsx +43 -0
- package/src/components/ui/search-field.tsx +80 -0
- package/src/components/ui/segmented-control.tsx +51 -0
- package/src/components/ui/select.tsx +92 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +116 -0
- package/src/components/ui/sidebar.tsx +674 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/switch.tsx +49 -0
- package/src/components/ui/tabs.tsx +36 -0
- package/src/components/ui/text-tab.tsx +43 -0
- package/src/components/ui/textarea.tsx +11 -0
- package/src/components/ui/tool-icon.tsx +65 -0
- package/src/components/ui/tooltip.tsx +69 -0
- package/src/fonts/JetBrainsMono-Bold.woff2 +0 -0
- package/src/fonts/JetBrainsMono-Italic.woff2 +0 -0
- package/src/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/src/global.d.ts +457 -0
- package/src/hooks/use-image-download.ts +85 -0
- package/src/hooks/use-media-query.ts +24 -0
- package/src/hooks/use-mobile.ts +3 -0
- package/src/hooks/use-resize-observer.ts +38 -0
- package/src/hooks/use-worktree-info.ts +68 -0
- package/src/i18n/catalog.ts +12 -0
- package/src/i18n/context.test.tsx +232 -0
- package/src/i18n/context.tsx +183 -0
- package/src/i18n/define-locale.ts +41 -0
- package/src/i18n/en.ts +1779 -0
- package/src/i18n/index.ts +20 -0
- package/src/i18n/ja.ts +1890 -0
- package/src/i18n/languages.test.ts +43 -0
- package/src/i18n/languages.ts +86 -0
- package/src/i18n/runtime.test.ts +75 -0
- package/src/i18n/runtime.ts +53 -0
- package/src/i18n/types.ts +1452 -0
- package/src/i18n/zh-hant.ts +1849 -0
- package/src/i18n/zh.ts +1923 -0
- package/src/lib/ansi.test.ts +123 -0
- package/src/lib/ansi.ts +175 -0
- package/src/lib/chat-messages.test.ts +708 -0
- package/src/lib/chat-messages.ts +885 -0
- package/src/lib/chat-runtime.test.ts +18 -0
- package/src/lib/chat-runtime.ts +335 -0
- package/src/lib/clipboard.ts +28 -0
- package/src/lib/commit-changelog.test.ts +114 -0
- package/src/lib/commit-changelog.ts +177 -0
- package/src/lib/completion-sound.ts +519 -0
- package/src/lib/desktop-fs.test.ts +116 -0
- package/src/lib/desktop-fs.ts +113 -0
- package/src/lib/desktop-slash-commands.test.ts +126 -0
- package/src/lib/desktop-slash-commands.ts +286 -0
- package/src/lib/embedded-images.test.ts +35 -0
- package/src/lib/embedded-images.ts +60 -0
- package/src/lib/external-link.test.tsx +168 -0
- package/src/lib/external-link.tsx +303 -0
- package/src/lib/gateway-events.test.ts +27 -0
- package/src/lib/gateway-events.ts +49 -0
- package/src/lib/gateway-ws-url.test.ts +78 -0
- package/src/lib/gateway-ws-url.ts +91 -0
- package/src/lib/generated-images.test.ts +97 -0
- package/src/lib/generated-images.ts +116 -0
- package/src/lib/haptics.ts +129 -0
- package/src/lib/icons.ts +203 -0
- package/src/lib/incremental-external-store-runtime.ts +188 -0
- package/src/lib/katex-memo.ts +260 -0
- package/src/lib/keybinds/actions.ts +125 -0
- package/src/lib/keybinds/combo.test.ts +86 -0
- package/src/lib/keybinds/combo.ts +169 -0
- package/src/lib/local-preview.ts +126 -0
- package/src/lib/markdown-code.test.ts +23 -0
- package/src/lib/markdown-code.ts +195 -0
- package/src/lib/markdown-preprocess.ts +386 -0
- package/src/lib/media.remote.test.ts +58 -0
- package/src/lib/media.ts +111 -0
- package/src/lib/model-status-label.test.ts +31 -0
- package/src/lib/model-status-label.ts +103 -0
- package/src/lib/mutable-ref.ts +6 -0
- package/src/lib/preview-targets.test.ts +27 -0
- package/src/lib/preview-targets.ts +63 -0
- package/src/lib/profile-color.ts +58 -0
- package/src/lib/provider-setup-errors.test.ts +26 -0
- package/src/lib/provider-setup-errors.ts +12 -0
- package/src/lib/query-client.ts +13 -0
- package/src/lib/remend-tail.test.ts +105 -0
- package/src/lib/remend-tail.ts +108 -0
- package/src/lib/runtime-readiness.test.ts +65 -0
- package/src/lib/runtime-readiness.ts +147 -0
- package/src/lib/session-export.ts +57 -0
- package/src/lib/session-search.test.ts +58 -0
- package/src/lib/session-search.ts +19 -0
- package/src/lib/session-source.ts +62 -0
- package/src/lib/speech-text.ts +35 -0
- package/src/lib/statusbar.ts +91 -0
- package/src/lib/storage.test.ts +25 -0
- package/src/lib/storage.ts +107 -0
- package/src/lib/todos.test.ts +35 -0
- package/src/lib/todos.ts +51 -0
- package/src/lib/tool-result-summary.test.ts +106 -0
- package/src/lib/tool-result-summary.ts +467 -0
- package/src/lib/update-copy.test.ts +38 -0
- package/src/lib/update-copy.ts +44 -0
- package/src/lib/use-enter-animation.ts +100 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/voice-playback.ts +128 -0
- package/src/lib/yolo-session.ts +26 -0
- package/src/main.tsx +43 -0
- package/src/nastech.test.ts +49 -0
- package/src/nastech.ts +718 -0
- package/src/store/activity.ts +100 -0
- package/src/store/boot.ts +91 -0
- package/src/store/clarify.test.ts +81 -0
- package/src/store/clarify.ts +69 -0
- package/src/store/command-palette.ts +20 -0
- package/src/store/compaction.test.ts +53 -0
- package/src/store/compaction.ts +38 -0
- package/src/store/completion-sound.ts +32 -0
- package/src/store/composer-input-history.test.ts +147 -0
- package/src/store/composer-input-history.ts +158 -0
- package/src/store/composer-queue.test.ts +148 -0
- package/src/store/composer-queue.ts +239 -0
- package/src/store/composer-status.test.ts +99 -0
- package/src/store/composer-status.ts +277 -0
- package/src/store/composer.test.ts +106 -0
- package/src/store/composer.ts +184 -0
- package/src/store/cron.ts +19 -0
- package/src/store/gateway.ts +290 -0
- package/src/store/haptics.ts +17 -0
- package/src/store/keybinds.ts +139 -0
- package/src/store/layout.ts +176 -0
- package/src/store/model-presets.test.ts +51 -0
- package/src/store/model-presets.ts +86 -0
- package/src/store/model-visibility.test.ts +37 -0
- package/src/store/model-visibility.ts +108 -0
- package/src/store/native-notifications.test.ts +192 -0
- package/src/store/native-notifications.ts +203 -0
- package/src/store/notifications.ts +165 -0
- package/src/store/onboarding.test.ts +372 -0
- package/src/store/onboarding.ts +866 -0
- package/src/store/panes.test.ts +146 -0
- package/src/store/panes.ts +145 -0
- package/src/store/preview.test.ts +135 -0
- package/src/store/preview.ts +466 -0
- package/src/store/profile.test.ts +89 -0
- package/src/store/profile.ts +365 -0
- package/src/store/prompts.test.ts +121 -0
- package/src/store/prompts.ts +115 -0
- package/src/store/session-switcher.test.ts +115 -0
- package/src/store/session-switcher.ts +128 -0
- package/src/store/session-sync.ts +25 -0
- package/src/store/session.test.ts +131 -0
- package/src/store/session.ts +255 -0
- package/src/store/subagents.test.ts +111 -0
- package/src/store/subagents.ts +260 -0
- package/src/store/thread-scroll.ts +46 -0
- package/src/store/todos.test.ts +47 -0
- package/src/store/todos.ts +64 -0
- package/src/store/tool-diffs.ts +23 -0
- package/src/store/tool-dismiss.ts +45 -0
- package/src/store/tool-view.ts +91 -0
- package/src/store/translucency.ts +38 -0
- package/src/store/updates.test.ts +77 -0
- package/src/store/updates.ts +315 -0
- package/src/store/voice-playback.ts +24 -0
- package/src/store/windows.test.ts +143 -0
- package/src/store/windows.ts +77 -0
- package/src/styles.css +1235 -0
- package/src/themes/color.ts +142 -0
- package/src/themes/context.tsx +339 -0
- package/src/themes/index.ts +3 -0
- package/src/themes/install.test.ts +119 -0
- package/src/themes/install.ts +95 -0
- package/src/themes/presets.test.ts +33 -0
- package/src/themes/presets.ts +293 -0
- package/src/themes/profile-theme.test.ts +41 -0
- package/src/themes/types.ts +66 -0
- package/src/themes/use-skin-command.ts +60 -0
- package/src/themes/user-themes.test.ts +63 -0
- package/src/themes/user-themes.ts +122 -0
- package/src/themes/vscode.test.ts +171 -0
- package/src/themes/vscode.ts +343 -0
- package/src/types/nastech.ts +646 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +25 -0
- package/vite.config.ts +56 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { useSearchParams } from 'react-router-dom'
|
|
3
|
+
|
|
4
|
+
interface DeepLinkHighlightOptions {
|
|
5
|
+
param: string
|
|
6
|
+
ready: (target: string) => boolean
|
|
7
|
+
elementId: (target: string) => string
|
|
8
|
+
onResolve?: (target: string) => void
|
|
9
|
+
block?: ScrollLogicalPosition
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Deep-link from the command palette (?<param>=<id>): once the target row is
|
|
13
|
+
// renderable, scroll it into view and flash it, then drop the param so it
|
|
14
|
+
// doesn't re-fire. Returns the pending target (null once consumed) so callers
|
|
15
|
+
// can force the row open before it mounts.
|
|
16
|
+
export function useDeepLinkHighlight({
|
|
17
|
+
param,
|
|
18
|
+
ready,
|
|
19
|
+
elementId,
|
|
20
|
+
onResolve,
|
|
21
|
+
block = 'center'
|
|
22
|
+
}: DeepLinkHighlightOptions): null | string {
|
|
23
|
+
const [searchParams, setSearchParams] = useSearchParams()
|
|
24
|
+
const target = searchParams.get(param)
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!target || !ready(target)) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onResolve?.(target)
|
|
32
|
+
|
|
33
|
+
// Defer a frame so async state (expansion, selection) mounts the row first.
|
|
34
|
+
const scrollTimeout = window.setTimeout(() => {
|
|
35
|
+
const element = document.getElementById(elementId(target))
|
|
36
|
+
|
|
37
|
+
if (!element) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
element.scrollIntoView({ behavior: 'smooth', block })
|
|
42
|
+
element.classList.add('setting-field-highlight')
|
|
43
|
+
window.setTimeout(() => element.classList.remove('setting-field-highlight'), 1600)
|
|
44
|
+
}, 80)
|
|
45
|
+
|
|
46
|
+
setSearchParams(
|
|
47
|
+
previous => {
|
|
48
|
+
const next = new URLSearchParams(previous)
|
|
49
|
+
next.delete(param)
|
|
50
|
+
|
|
51
|
+
return next
|
|
52
|
+
},
|
|
53
|
+
{ replace: true }
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return () => window.clearTimeout(scrollTimeout)
|
|
57
|
+
}, [block, elementId, onResolve, param, ready, setSearchParams, target])
|
|
58
|
+
|
|
59
|
+
return target
|
|
60
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { useStore } from '@nanostores/react'
|
|
2
|
+
import type { CSSProperties, ReactNode } from 'react'
|
|
3
|
+
import { useSyncExternalStore } from 'react'
|
|
4
|
+
|
|
5
|
+
import { NotificationStack } from '@/components/notifications'
|
|
6
|
+
import { PaneShell } from '@/components/pane-shell'
|
|
7
|
+
import { SidebarProvider } from '@/components/ui/sidebar'
|
|
8
|
+
import {
|
|
9
|
+
$fileBrowserOpen,
|
|
10
|
+
$panesFlipped,
|
|
11
|
+
$sidebarOpen,
|
|
12
|
+
FILE_BROWSER_DEFAULT_WIDTH,
|
|
13
|
+
FILE_BROWSER_PANE_ID,
|
|
14
|
+
setSidebarOpen
|
|
15
|
+
} from '@/store/layout'
|
|
16
|
+
import { $paneWidthOverride } from '@/store/panes'
|
|
17
|
+
import { $connection } from '@/store/session'
|
|
18
|
+
|
|
19
|
+
import { KeybindPanel } from './keybind-panel'
|
|
20
|
+
import { StatusbarControls, type StatusbarItem } from './statusbar-controls'
|
|
21
|
+
import { TITLEBAR_HEIGHT, titlebarControlsPosition } from './titlebar'
|
|
22
|
+
import { TitlebarControls, type TitlebarTool } from './titlebar-controls'
|
|
23
|
+
|
|
24
|
+
interface AppShellProps {
|
|
25
|
+
children: ReactNode
|
|
26
|
+
leftStatusbarItems?: readonly StatusbarItem[]
|
|
27
|
+
leftTitlebarTools?: readonly TitlebarTool[]
|
|
28
|
+
onOpenSettings: () => void
|
|
29
|
+
overlays?: ReactNode
|
|
30
|
+
statusbarItems?: readonly StatusbarItem[]
|
|
31
|
+
titlebarTools?: readonly TitlebarTool[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Renderer-side fallback so layout snaps even when the main-process fullscreen event
|
|
35
|
+
// hasn't landed yet (e.g. dev reloads, before the IPC bridge is wired).
|
|
36
|
+
function subscribeWindowSize(cb: () => void) {
|
|
37
|
+
window.addEventListener('resize', cb)
|
|
38
|
+
window.addEventListener('fullscreenchange', cb)
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
window.removeEventListener('resize', cb)
|
|
42
|
+
window.removeEventListener('fullscreenchange', cb)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const viewportIsFullscreen = () =>
|
|
47
|
+
window.innerWidth >= window.screen.width && window.innerHeight >= window.screen.height
|
|
48
|
+
|
|
49
|
+
export function AppShell({
|
|
50
|
+
children,
|
|
51
|
+
leftStatusbarItems,
|
|
52
|
+
leftTitlebarTools,
|
|
53
|
+
onOpenSettings,
|
|
54
|
+
overlays,
|
|
55
|
+
statusbarItems,
|
|
56
|
+
titlebarTools
|
|
57
|
+
}: AppShellProps) {
|
|
58
|
+
const sidebarOpen = useStore($sidebarOpen)
|
|
59
|
+
const fileBrowserOpen = useStore($fileBrowserOpen)
|
|
60
|
+
const panesFlipped = useStore($panesFlipped)
|
|
61
|
+
const fileBrowserWidthOverride = useStore($paneWidthOverride(FILE_BROWSER_PANE_ID))
|
|
62
|
+
const connection = useStore($connection)
|
|
63
|
+
const viewportFullscreen = useSyncExternalStore(subscribeWindowSize, viewportIsFullscreen, () => false)
|
|
64
|
+
const isFullscreen = Boolean(connection?.isFullscreen) || viewportFullscreen
|
|
65
|
+
const titlebarControls = titlebarControlsPosition(connection?.windowButtonPosition, isFullscreen)
|
|
66
|
+
// Width Windows/Linux reserve for the OS-painted min/max/close overlay (zero
|
|
67
|
+
// on macOS, where window controls sit on the left and are reported via
|
|
68
|
+
// windowButtonPosition instead). The right tool cluster has to clear them.
|
|
69
|
+
const nativeOverlayWidth = connection?.nativeOverlayWidth ?? 0
|
|
70
|
+
const titlebarToolsRight = nativeOverlayWidth > 0 ? `${nativeOverlayWidth}px` : '0.75rem'
|
|
71
|
+
|
|
72
|
+
// The inset clears the top-left titlebar buttons when nothing covers the
|
|
73
|
+
// window's left edge. Default layout: the sessions sidebar sits there.
|
|
74
|
+
// Flipped layout: the file browser does instead.
|
|
75
|
+
const leftEdgePaneOpen = panesFlipped ? fileBrowserOpen : sidebarOpen
|
|
76
|
+
|
|
77
|
+
const titlebarContentInset = leftEdgePaneOpen
|
|
78
|
+
? 0
|
|
79
|
+
: titlebarControls.left + TITLEBAR_HEIGHT + Math.round(TITLEBAR_HEIGHT / 2)
|
|
80
|
+
|
|
81
|
+
// The static system cluster (haptics, profiles, settings, right-sidebar) is
|
|
82
|
+
// hardcoded in TitlebarControls. Pane-supplied tools (preview's group) render
|
|
83
|
+
// in a separate cluster anchored further left.
|
|
84
|
+
//
|
|
85
|
+
// Width math has to include the `gap-x-1` (0.25rem) between buttons:
|
|
86
|
+
// N buttons + (N - 1) inner gaps, plus one extra 0.25rem of breathing room
|
|
87
|
+
// between the pane-tool cluster and the system cluster so they don't sit
|
|
88
|
+
// flush against each other. Modeled as N gaps (N - 1 inner + 1 trailing)
|
|
89
|
+
// to keep the formula generic for any pane-tool count.
|
|
90
|
+
const SYSTEM_TOOL_COUNT = 4
|
|
91
|
+
const paneToolCount = titlebarTools?.filter(tool => !tool.hidden).length ?? 0
|
|
92
|
+
const systemToolsWidth = `calc(${SYSTEM_TOOL_COUNT} * (var(--titlebar-control-size) + 0.25rem))`
|
|
93
|
+
|
|
94
|
+
const fileBrowserWidth =
|
|
95
|
+
fileBrowserWidthOverride !== undefined ? `${fileBrowserWidthOverride}px` : FILE_BROWSER_DEFAULT_WIDTH
|
|
96
|
+
|
|
97
|
+
// Where the pane-tool cluster's right edge sits, measured from the inner
|
|
98
|
+
// titlebar padding (--titlebar-tools-right). Two anchors:
|
|
99
|
+
// - file-browser closed → flush against static cluster's left edge
|
|
100
|
+
// - file-browser open → flush against the file-browser pane's left edge
|
|
101
|
+
// (= preview pane's right edge)
|
|
102
|
+
const previewToolbarGap = fileBrowserOpen ? fileBrowserWidth : systemToolsWidth
|
|
103
|
+
|
|
104
|
+
// Used by the drag region to know where the rightmost interactive element
|
|
105
|
+
// ends. When pane tools are present, that's `gap + paneCount * controlSize
|
|
106
|
+
// + paneCount * 0.25rem` (the leftmost button is at `tools-right + gap +
|
|
107
|
+
// paneCount * (size + gap-x-1)`). Otherwise the static cluster's footprint
|
|
108
|
+
// is enough.
|
|
109
|
+
const titlebarToolsWidth =
|
|
110
|
+
paneToolCount > 0
|
|
111
|
+
? `calc(${previewToolbarGap} + ${paneToolCount} * (var(--titlebar-control-size) + 0.25rem))`
|
|
112
|
+
: systemToolsWidth
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<SidebarProvider
|
|
116
|
+
className="h-screen min-h-0 flex-col bg-background"
|
|
117
|
+
onOpenChange={setSidebarOpen}
|
|
118
|
+
open={sidebarOpen}
|
|
119
|
+
style={
|
|
120
|
+
{
|
|
121
|
+
// Alias for shadcn <Sidebar> descendants. Resolves to the chat-sidebar
|
|
122
|
+
// pane track via PaneShell's emitted --pane-chat-sidebar-width.
|
|
123
|
+
'--sidebar-width': 'var(--pane-chat-sidebar-width)',
|
|
124
|
+
'--titlebar-height': `${TITLEBAR_HEIGHT}px`,
|
|
125
|
+
'--titlebar-content-inset': `${titlebarContentInset}px`,
|
|
126
|
+
'--titlebar-controls-left': `${titlebarControls.left}px`,
|
|
127
|
+
'--titlebar-controls-top': `${titlebarControls.top}px`,
|
|
128
|
+
'--titlebar-tools-right': titlebarToolsRight,
|
|
129
|
+
'--titlebar-tools-width': titlebarToolsWidth,
|
|
130
|
+
// Anchor for the pane-tool cluster's right edge in TitlebarControls.
|
|
131
|
+
// Sourced from the layout store rather than the PaneShell-emitted
|
|
132
|
+
// --pane-*-width vars because the titlebar is a sibling of PaneShell
|
|
133
|
+
// and CSS variables resolve at the consumer's scope.
|
|
134
|
+
'--shell-preview-toolbar-gap': previewToolbarGap
|
|
135
|
+
} as CSSProperties
|
|
136
|
+
}
|
|
137
|
+
>
|
|
138
|
+
<TitlebarControls leftTools={leftTitlebarTools} onOpenSettings={onOpenSettings} tools={titlebarTools} />
|
|
139
|
+
|
|
140
|
+
<main className="relative z-3 flex min-h-0 w-full flex-1 flex-col overflow-hidden transition-none">
|
|
141
|
+
<PaneShell className="min-h-0 flex-1">
|
|
142
|
+
<div
|
|
143
|
+
aria-hidden="true"
|
|
144
|
+
className="pointer-events-none absolute left-0 top-0 z-1 h-(--titlebar-height) w-(--titlebar-controls-left) [-webkit-app-region:drag]"
|
|
145
|
+
/>
|
|
146
|
+
<div
|
|
147
|
+
aria-hidden="true"
|
|
148
|
+
className="pointer-events-none absolute top-0 z-1 h-(--titlebar-height) left-[calc(var(--titlebar-controls-left)+(var(--titlebar-control-size)*2)+0.75rem)] right-[calc(var(--titlebar-tools-right)+var(--titlebar-tools-width)+0.75rem)] [-webkit-app-region:drag]"
|
|
149
|
+
/>
|
|
150
|
+
|
|
151
|
+
{children}
|
|
152
|
+
</PaneShell>
|
|
153
|
+
|
|
154
|
+
<StatusbarControls items={statusbarItems} leftItems={leftStatusbarItems} />
|
|
155
|
+
</main>
|
|
156
|
+
|
|
157
|
+
{overlays}
|
|
158
|
+
|
|
159
|
+
{/* Keybind map dialog (titlebar ⌨ button / ⌘/). */}
|
|
160
|
+
<KeybindPanel />
|
|
161
|
+
|
|
162
|
+
{/* Mounted at the shell root (after overlays) so success/error toasts
|
|
163
|
+
surface above every route and overlay — not just the chat view. */}
|
|
164
|
+
<NotificationStack />
|
|
165
|
+
</SidebarProvider>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { IconLayoutDashboard } from '@tabler/icons-react'
|
|
2
|
+
|
|
3
|
+
import { StatusDot, type StatusTone } from '@/components/status-dot'
|
|
4
|
+
import { Button } from '@/components/ui/button'
|
|
5
|
+
import { Tip } from '@/components/ui/tooltip'
|
|
6
|
+
import { useI18n } from '@/i18n'
|
|
7
|
+
import { Activity, AlertCircle } from '@/lib/icons'
|
|
8
|
+
import type { RuntimeReadinessResult } from '@/lib/runtime-readiness'
|
|
9
|
+
import { cn } from '@/lib/utils'
|
|
10
|
+
import type { StatusResponse } from '@/types/nastech'
|
|
11
|
+
|
|
12
|
+
interface GatewayMenuPanelProps {
|
|
13
|
+
gatewayState: string
|
|
14
|
+
inferenceStatus: RuntimeReadinessResult | null
|
|
15
|
+
logLines: readonly string[]
|
|
16
|
+
onOpenSystem: () => void
|
|
17
|
+
statusSnapshot: StatusResponse | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const PLATFORM_TONE: Record<string, StatusTone> = {
|
|
21
|
+
connected: 'good',
|
|
22
|
+
connecting: 'warn',
|
|
23
|
+
retrying: 'warn',
|
|
24
|
+
pending_restart: 'warn',
|
|
25
|
+
startup_failed: 'bad',
|
|
26
|
+
fatal: 'bad'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const prettyState = (state: string) => state.replace(/_/g, ' ').replace(/^./, c => c.toUpperCase())
|
|
30
|
+
|
|
31
|
+
// Strip leading "YYYY-MM-DD HH:MM:SS,mmm " and "[runtime_id] " prefixes from
|
|
32
|
+
// log lines so they don't dominate the display. Full text preserved on hover.
|
|
33
|
+
const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}[,.\d]*\s+/
|
|
34
|
+
const RUNTIME_BRACKET_RE = /^\[[^\]]+]\s+/
|
|
35
|
+
const trimLogLine = (raw: string) => raw.trim().replace(TIMESTAMP_RE, '').replace(RUNTIME_BRACKET_RE, '')
|
|
36
|
+
|
|
37
|
+
export function GatewayMenuPanel({
|
|
38
|
+
gatewayState,
|
|
39
|
+
inferenceStatus,
|
|
40
|
+
logLines,
|
|
41
|
+
onOpenSystem,
|
|
42
|
+
statusSnapshot
|
|
43
|
+
}: GatewayMenuPanelProps) {
|
|
44
|
+
const { t } = useI18n()
|
|
45
|
+
const copy = t.shell.gatewayMenu
|
|
46
|
+
const gatewayOpen = gatewayState === 'open'
|
|
47
|
+
const gatewayConnecting = gatewayState === 'connecting'
|
|
48
|
+
const inferenceReady = gatewayOpen && inferenceStatus?.ready === true
|
|
49
|
+
|
|
50
|
+
const connectionLabel = gatewayOpen
|
|
51
|
+
? copy.connected
|
|
52
|
+
: gatewayConnecting
|
|
53
|
+
? copy.connecting
|
|
54
|
+
: prettyState(gatewayState || copy.offline)
|
|
55
|
+
|
|
56
|
+
const inferenceLabel = gatewayOpen
|
|
57
|
+
? inferenceStatus?.ready
|
|
58
|
+
? copy.inferenceReady
|
|
59
|
+
: inferenceStatus
|
|
60
|
+
? copy.inferenceNotReady
|
|
61
|
+
: copy.checkingInference
|
|
62
|
+
: copy.disconnected
|
|
63
|
+
|
|
64
|
+
const platforms = Object.entries(statusSnapshot?.gateway_platforms || {}).sort(([l], [r]) => l.localeCompare(r))
|
|
65
|
+
const recentLogs = logLines.slice(-5)
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="text-sm">
|
|
69
|
+
<div className="flex items-center justify-between gap-2 px-3 py-2.5">
|
|
70
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
71
|
+
{inferenceReady ? (
|
|
72
|
+
<Activity className="size-3.5 text-primary" />
|
|
73
|
+
) : (
|
|
74
|
+
<AlertCircle className={cn('size-3.5', gatewayOpen ? 'text-amber-600' : 'text-destructive')} />
|
|
75
|
+
)}
|
|
76
|
+
<span className="font-medium">{copy.gateway}</span>
|
|
77
|
+
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
78
|
+
<StatusDot tone={inferenceReady ? 'good' : gatewayOpen ? 'warn' : 'bad'} />
|
|
79
|
+
{inferenceLabel}
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
<div className="flex items-center">
|
|
83
|
+
<Tip label={copy.openSystem}>
|
|
84
|
+
<Button
|
|
85
|
+
aria-label={copy.openSystem}
|
|
86
|
+
className="text-muted-foreground hover:text-foreground"
|
|
87
|
+
onClick={onOpenSystem}
|
|
88
|
+
size="icon-sm"
|
|
89
|
+
variant="ghost"
|
|
90
|
+
>
|
|
91
|
+
<IconLayoutDashboard />
|
|
92
|
+
</Button>
|
|
93
|
+
</Tip>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div className="border-t border-border/50 px-3 py-2 text-xs text-muted-foreground">
|
|
98
|
+
<div>{copy.connection(connectionLabel)}</div>
|
|
99
|
+
{inferenceStatus?.reason && <div className="mt-1 line-clamp-3">{inferenceStatus.reason}</div>}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{recentLogs.length > 0 && (
|
|
103
|
+
<div className="border-t border-border/50 px-3 py-2">
|
|
104
|
+
<SectionLabel>{copy.recentActivity}</SectionLabel>
|
|
105
|
+
<ul className="mt-1.5 space-y-0.5">
|
|
106
|
+
{recentLogs.map((line, index) => (
|
|
107
|
+
<Tip key={`${index}:${line}`} label={line.trim()}>
|
|
108
|
+
<li className="truncate font-mono text-[0.68rem] text-muted-foreground/85">
|
|
109
|
+
{trimLogLine(line) || '\u00A0'}
|
|
110
|
+
</li>
|
|
111
|
+
</Tip>
|
|
112
|
+
))}
|
|
113
|
+
</ul>
|
|
114
|
+
<Button
|
|
115
|
+
className="-ml-2 mt-1.5 font-medium text-muted-foreground"
|
|
116
|
+
onClick={onOpenSystem}
|
|
117
|
+
size="xs"
|
|
118
|
+
type="button"
|
|
119
|
+
variant="text"
|
|
120
|
+
>
|
|
121
|
+
{copy.viewAllLogs}
|
|
122
|
+
</Button>
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
{platforms.length > 0 && (
|
|
127
|
+
<div className="border-t border-border/50 px-3 py-2">
|
|
128
|
+
<SectionLabel>{copy.messagingPlatforms}</SectionLabel>
|
|
129
|
+
<ul className="mt-1.5 space-y-1">
|
|
130
|
+
{platforms.map(([name, platform]) => (
|
|
131
|
+
<li className="flex items-center justify-between gap-2 text-xs" key={name}>
|
|
132
|
+
<span className="truncate capitalize">{name}</span>
|
|
133
|
+
<span className="flex items-center gap-1.5 text-[0.66rem] text-muted-foreground">
|
|
134
|
+
<StatusDot tone={PLATFORM_TONE[platform.state] || 'muted'} />
|
|
135
|
+
{prettyState(platform.state)}
|
|
136
|
+
</span>
|
|
137
|
+
</li>
|
|
138
|
+
))}
|
|
139
|
+
</ul>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function SectionLabel({ children }: { children: string }) {
|
|
147
|
+
return (
|
|
148
|
+
<div className="text-[0.62rem] font-semibold uppercase tracking-[0.14em] text-muted-foreground/80">{children}</div>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
2
|
+
import { useLocation, useNavigate } from 'react-router-dom'
|
|
3
|
+
|
|
4
|
+
import { type CommandCenterSection } from '@/app/command-center'
|
|
5
|
+
import { AGENTS_ROUTE, appViewForPath, COMMAND_CENTER_ROUTE, isOverlayView, NEW_CHAT_ROUTE } from '@/app/routes'
|
|
6
|
+
|
|
7
|
+
const SECTIONS = ['sessions', 'system', 'usage'] as const
|
|
8
|
+
|
|
9
|
+
export function useOverlayRouting() {
|
|
10
|
+
const location = useLocation()
|
|
11
|
+
const navigate = useNavigate()
|
|
12
|
+
|
|
13
|
+
const currentView = appViewForPath(location.pathname)
|
|
14
|
+
const settingsOpen = currentView === 'settings'
|
|
15
|
+
const commandCenterOpen = currentView === 'command-center'
|
|
16
|
+
const agentsOpen = currentView === 'agents'
|
|
17
|
+
const cronOpen = currentView === 'cron'
|
|
18
|
+
const profilesOpen = currentView === 'profiles'
|
|
19
|
+
const chatOpen = currentView === 'chat'
|
|
20
|
+
const overlayOpen = isOverlayView(currentView)
|
|
21
|
+
|
|
22
|
+
// Overlay routes (settings/command-center/agents) stash the underlying path
|
|
23
|
+
// so closing them returns there instead of bouncing to /.
|
|
24
|
+
const returnPathRef = useRef(NEW_CHAT_ROUTE)
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!overlayOpen) {
|
|
28
|
+
returnPathRef.current = `${location.pathname}${location.search}${location.hash}`
|
|
29
|
+
}
|
|
30
|
+
}, [location.hash, location.pathname, location.search, overlayOpen])
|
|
31
|
+
|
|
32
|
+
const commandCenterInitialSection = useMemo<CommandCenterSection | undefined>(
|
|
33
|
+
() => SECTIONS.find(value => value === new URLSearchParams(location.search).get('section')),
|
|
34
|
+
[location.search]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const openCommandCenterSection = useCallback(
|
|
38
|
+
(section: CommandCenterSection) => navigate(`${COMMAND_CENTER_ROUTE}?section=${section}`),
|
|
39
|
+
[navigate]
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const closeOverlayToPreviousRoute = useCallback(
|
|
43
|
+
() => navigate(returnPathRef.current || NEW_CHAT_ROUTE, { replace: true }),
|
|
44
|
+
[navigate]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const toggleCommandCenter = useCallback(() => {
|
|
48
|
+
if (commandCenterOpen) {
|
|
49
|
+
closeOverlayToPreviousRoute()
|
|
50
|
+
} else {
|
|
51
|
+
navigate(COMMAND_CENTER_ROUTE)
|
|
52
|
+
}
|
|
53
|
+
}, [closeOverlayToPreviousRoute, commandCenterOpen, navigate])
|
|
54
|
+
|
|
55
|
+
const openAgents = useCallback(() => navigate(AGENTS_ROUTE), [navigate])
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
agentsOpen,
|
|
59
|
+
chatOpen,
|
|
60
|
+
closeOverlayToPreviousRoute,
|
|
61
|
+
commandCenterInitialSection,
|
|
62
|
+
commandCenterOpen,
|
|
63
|
+
cronOpen,
|
|
64
|
+
currentView,
|
|
65
|
+
openAgents,
|
|
66
|
+
openCommandCenterSection,
|
|
67
|
+
profilesOpen,
|
|
68
|
+
settingsOpen,
|
|
69
|
+
toggleCommandCenter
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { getLogs, getStatus } from '@/nastech'
|
|
4
|
+
import { evaluateRuntimeReadiness, type RuntimeReadinessResult } from '@/lib/runtime-readiness'
|
|
5
|
+
import type { StatusResponse } from '@/types/nastech'
|
|
6
|
+
|
|
7
|
+
const REFRESH_MS = 15_000
|
|
8
|
+
const LOG_TAIL = 12
|
|
9
|
+
|
|
10
|
+
type GatewayRequester = <T = unknown>(method: string, params?: Record<string, unknown>) => Promise<T>
|
|
11
|
+
|
|
12
|
+
export function useStatusSnapshot(gatewayState: string | undefined, requestGateway: GatewayRequester) {
|
|
13
|
+
const [statusSnapshot, setStatusSnapshot] = useState<StatusResponse | null>(null)
|
|
14
|
+
const [gatewayLogLines, setGatewayLogLines] = useState<string[]>([])
|
|
15
|
+
const [inferenceStatus, setInferenceStatus] = useState<RuntimeReadinessResult | null>(null)
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
let cancelled = false
|
|
19
|
+
|
|
20
|
+
const refresh = async () => {
|
|
21
|
+
try {
|
|
22
|
+
const [next, logs, inference] = await Promise.all([
|
|
23
|
+
getStatus(),
|
|
24
|
+
getLogs({ file: 'gui', lines: LOG_TAIL }).catch(() => ({ lines: [] })),
|
|
25
|
+
gatewayState === 'open'
|
|
26
|
+
? evaluateRuntimeReadiness(requestGateway).catch(error => ({
|
|
27
|
+
checksDisagree: false,
|
|
28
|
+
ready: false,
|
|
29
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
30
|
+
source: 'fallback' as const
|
|
31
|
+
}))
|
|
32
|
+
: Promise.resolve(null)
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
if (cancelled) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setStatusSnapshot(next)
|
|
40
|
+
setGatewayLogLines(logs.lines.map(line => line.trim()).filter(Boolean))
|
|
41
|
+
setInferenceStatus(inference)
|
|
42
|
+
} catch {
|
|
43
|
+
// Keep last snapshot through transient gateway flaps.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void refresh()
|
|
48
|
+
const timer = window.setInterval(() => void refresh(), REFRESH_MS)
|
|
49
|
+
|
|
50
|
+
return () => {
|
|
51
|
+
cancelled = true
|
|
52
|
+
window.clearInterval(timer)
|
|
53
|
+
}
|
|
54
|
+
}, [gatewayState, requestGateway])
|
|
55
|
+
|
|
56
|
+
return { gatewayLogLines, inferenceStatus, statusSnapshot }
|
|
57
|
+
}
|