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.
Files changed (546) hide show
  1. package/.prettierrc +11 -0
  2. package/DESIGN.md +167 -0
  3. package/README.md +141 -0
  4. package/assets/icon.icns +0 -0
  5. package/assets/icon.ico +0 -0
  6. package/assets/icon.png +0 -0
  7. package/components.json +21 -0
  8. package/electron/backend-env.cjs +112 -0
  9. package/electron/backend-env.test.cjs +111 -0
  10. package/electron/backend-probes.cjs +106 -0
  11. package/electron/backend-probes.test.cjs +82 -0
  12. package/electron/backend-ready.cjs +66 -0
  13. package/electron/bootstrap-platform.cjs +91 -0
  14. package/electron/bootstrap-platform.test.cjs +111 -0
  15. package/electron/bootstrap-runner.cjs +720 -0
  16. package/electron/bootstrap-runner.test.cjs +138 -0
  17. package/electron/connection-config.cjs +254 -0
  18. package/electron/connection-config.test.cjs +329 -0
  19. package/electron/dashboard-token.cjs +99 -0
  20. package/electron/dashboard-token.test.cjs +142 -0
  21. package/electron/desktop-uninstall.cjs +232 -0
  22. package/electron/desktop-uninstall.test.cjs +246 -0
  23. package/electron/entitlements.mac.inherit.plist +14 -0
  24. package/electron/entitlements.mac.plist +14 -0
  25. package/electron/fs-read-dir.cjs +109 -0
  26. package/electron/fs-read-dir.test.cjs +364 -0
  27. package/electron/gateway-ws-probe.cjs +188 -0
  28. package/electron/gateway-ws-probe.test.cjs +122 -0
  29. package/electron/git-root.cjs +54 -0
  30. package/electron/git-root.test.cjs +40 -0
  31. package/electron/git-worktrees.cjs +174 -0
  32. package/electron/hardening.cjs +184 -0
  33. package/electron/hardening.test.cjs +116 -0
  34. package/electron/main.cjs +5762 -0
  35. package/electron/oauth-net-request.cjs +20 -0
  36. package/electron/oauth-net-request.test.cjs +34 -0
  37. package/electron/preload.cjs +135 -0
  38. package/electron/session-windows.cjs +99 -0
  39. package/electron/session-windows.test.cjs +177 -0
  40. package/electron/update-remote.cjs +56 -0
  41. package/electron/update-remote.test.cjs +78 -0
  42. package/electron/vscode-marketplace.cjs +331 -0
  43. package/electron/vscode-marketplace.test.cjs +113 -0
  44. package/electron/windows-child-process.test.cjs +57 -0
  45. package/electron/windows-user-env.cjs +76 -0
  46. package/electron/windows-user-env.test.cjs +90 -0
  47. package/electron/workspace-cwd.cjs +38 -0
  48. package/electron/workspace-cwd.test.cjs +45 -0
  49. package/eslint.config.mjs +122 -0
  50. package/index.html +17 -0
  51. package/package.json +254 -0
  52. package/pr-assets/session-source-folders.png +0 -0
  53. package/preview-demo.html +65 -0
  54. package/public/apple-touch-icon.png +0 -0
  55. package/public/ds-assets/filler-bg0.jpg +0 -0
  56. package/public/nastech-frames/nastech-frame-0.png +0 -0
  57. package/public/nastech-frames/nastech-frame-1.png +0 -0
  58. package/public/nastech-frames/nastech-frame-2.png +0 -0
  59. package/public/nastech-frames/nastech-frame-3.png +0 -0
  60. package/public/nastech-frames/nastech-frame-4.png +0 -0
  61. package/public/nastech-frames/nastech-frame-5.png +0 -0
  62. package/public/nastech-frames/nastech-frame-6.png +0 -0
  63. package/public/nastech-frames/nastech-frame-7.png +0 -0
  64. package/public/nastech-girl.jpg +0 -0
  65. package/public/nastech-sprite.png +0 -0
  66. package/public/nastech.png +0 -0
  67. package/scripts/after-pack.cjs +41 -0
  68. package/scripts/assert-dist-built.cjs +70 -0
  69. package/scripts/assert-dist-built.test.cjs +84 -0
  70. package/scripts/assert-root-install.cjs +13 -0
  71. package/scripts/before-build.cjs +11 -0
  72. package/scripts/before-pack.cjs +78 -0
  73. package/scripts/before-pack.test.cjs +53 -0
  74. package/scripts/click-session.mjs +51 -0
  75. package/scripts/dev-no-hmr.mjs +22 -0
  76. package/scripts/diag-jump.mjs +115 -0
  77. package/scripts/diag-scroll-reset.mjs +229 -0
  78. package/scripts/eval.mjs +21 -0
  79. package/scripts/leak-typing.mjs +222 -0
  80. package/scripts/measure-jump.mjs +108 -0
  81. package/scripts/measure-latency.mjs +184 -0
  82. package/scripts/measure-real-stream.mjs +252 -0
  83. package/scripts/measure-submit.mjs +179 -0
  84. package/scripts/measure-synthetic-stream.mjs +322 -0
  85. package/scripts/notarize-artifact.cjs +77 -0
  86. package/scripts/notarize.cjs +100 -0
  87. package/scripts/patch-electron-builder-mac-binary.cjs +59 -0
  88. package/scripts/probe-renderer.mjs +38 -0
  89. package/scripts/probe-thread.mjs +40 -0
  90. package/scripts/profile-long-stream.mjs +191 -0
  91. package/scripts/profile-real-stream.mjs +137 -0
  92. package/scripts/profile-synth-stream.mjs +103 -0
  93. package/scripts/profile-typing-lag.md +381 -0
  94. package/scripts/profile-typing.mjs +260 -0
  95. package/scripts/reload-renderer.mjs +25 -0
  96. package/scripts/reload.mjs +36 -0
  97. package/scripts/set-exe-identity.cjs +94 -0
  98. package/scripts/stage-native-deps.cjs +159 -0
  99. package/scripts/test-desktop.mjs +425 -0
  100. package/scripts/write-build-stamp.cjs +126 -0
  101. package/src/app/agents/index.tsx +398 -0
  102. package/src/app/artifacts/index.test.ts +62 -0
  103. package/src/app/artifacts/index.tsx +906 -0
  104. package/src/app/chat/chat-drop-overlay.tsx +48 -0
  105. package/src/app/chat/chat-swap-overlay.tsx +47 -0
  106. package/src/app/chat/composer/attachments.tsx +114 -0
  107. package/src/app/chat/composer/completion-drawer.tsx +63 -0
  108. package/src/app/chat/composer/context-menu.tsx +172 -0
  109. package/src/app/chat/composer/controls.tsx +289 -0
  110. package/src/app/chat/composer/drop-affordance.ts +2 -0
  111. package/src/app/chat/composer/enter-submit-dom-race.test.tsx +218 -0
  112. package/src/app/chat/composer/focus.ts +134 -0
  113. package/src/app/chat/composer/help-hint.tsx +59 -0
  114. package/src/app/chat/composer/hooks/use-at-completions.ts +141 -0
  115. package/src/app/chat/composer/hooks/use-live-completion-adapter.ts +119 -0
  116. package/src/app/chat/composer/hooks/use-mic-recorder.ts +291 -0
  117. package/src/app/chat/composer/hooks/use-slash-completions.ts +114 -0
  118. package/src/app/chat/composer/hooks/use-voice-conversation.ts +390 -0
  119. package/src/app/chat/composer/hooks/use-voice-recorder.ts +116 -0
  120. package/src/app/chat/composer/ime-composition-dom-repro.test.tsx +108 -0
  121. package/src/app/chat/composer/index.tsx +1611 -0
  122. package/src/app/chat/composer/inline-refs.ts +138 -0
  123. package/src/app/chat/composer/model-pill.tsx +86 -0
  124. package/src/app/chat/composer/queue-panel.tsx +130 -0
  125. package/src/app/chat/composer/rich-editor.test.ts +18 -0
  126. package/src/app/chat/composer/rich-editor.ts +165 -0
  127. package/src/app/chat/composer/skin-slash-popover.tsx +61 -0
  128. package/src/app/chat/composer/slash-nav-dom-repro.test.tsx +186 -0
  129. package/src/app/chat/composer/status-stack/index.tsx +202 -0
  130. package/src/app/chat/composer/status-stack/status-row.tsx +155 -0
  131. package/src/app/chat/composer/text-utils.test.ts +77 -0
  132. package/src/app/chat/composer/text-utils.ts +107 -0
  133. package/src/app/chat/composer/trigger-popover.test.tsx +42 -0
  134. package/src/app/chat/composer/trigger-popover.tsx +116 -0
  135. package/src/app/chat/composer/types.ts +64 -0
  136. package/src/app/chat/composer/url-dialog.tsx +82 -0
  137. package/src/app/chat/composer/voice-activity.tsx +252 -0
  138. package/src/app/chat/hooks/use-composer-actions.test.ts +57 -0
  139. package/src/app/chat/hooks/use-composer-actions.ts +525 -0
  140. package/src/app/chat/hooks/use-file-drop-zone.ts +118 -0
  141. package/src/app/chat/index.tsx +390 -0
  142. package/src/app/chat/perf-probe.tsx +269 -0
  143. package/src/app/chat/right-rail/index.ts +1 -0
  144. package/src/app/chat/right-rail/preview-console-state.ts +82 -0
  145. package/src/app/chat/right-rail/preview-console.tsx +290 -0
  146. package/src/app/chat/right-rail/preview-file.tsx +559 -0
  147. package/src/app/chat/right-rail/preview-pane.test.tsx +43 -0
  148. package/src/app/chat/right-rail/preview-pane.tsx +657 -0
  149. package/src/app/chat/right-rail/preview.tsx +171 -0
  150. package/src/app/chat/scroll-to-bottom-button.test.tsx +67 -0
  151. package/src/app/chat/scroll-to-bottom-button.tsx +74 -0
  152. package/src/app/chat/sidebar/cron-jobs-section.tsx +325 -0
  153. package/src/app/chat/sidebar/index.tsx +1219 -0
  154. package/src/app/chat/sidebar/load-more-row.tsx +30 -0
  155. package/src/app/chat/sidebar/order.test.ts +21 -0
  156. package/src/app/chat/sidebar/order.ts +17 -0
  157. package/src/app/chat/sidebar/profile-switcher.tsx +516 -0
  158. package/src/app/chat/sidebar/session-actions-menu.tsx +264 -0
  159. package/src/app/chat/sidebar/session-row.tsx +257 -0
  160. package/src/app/chat/sidebar/virtual-session-list.tsx +154 -0
  161. package/src/app/chat/sidebar/workspace-groups.test.ts +149 -0
  162. package/src/app/chat/sidebar/workspace-groups.ts +326 -0
  163. package/src/app/chat/thread-loading.test.ts +34 -0
  164. package/src/app/chat/thread-loading.ts +26 -0
  165. package/src/app/command-center/index.tsx +654 -0
  166. package/src/app/command-palette/index.tsx +513 -0
  167. package/src/app/command-palette/marketplace-theme-page.tsx +157 -0
  168. package/src/app/cron/index.tsx +942 -0
  169. package/src/app/cron/job-state.ts +29 -0
  170. package/src/app/desktop-controller.tsx +938 -0
  171. package/src/app/floating-hud.ts +22 -0
  172. package/src/app/gateway/hooks/use-gateway-boot.test.tsx +265 -0
  173. package/src/app/gateway/hooks/use-gateway-boot.ts +387 -0
  174. package/src/app/gateway/hooks/use-gateway-request.ts +138 -0
  175. package/src/app/hooks/use-keybinds.ts +186 -0
  176. package/src/app/hooks/use-refresh-hotkey.ts +45 -0
  177. package/src/app/hooks/use-route-enum-param.ts +38 -0
  178. package/src/app/index.tsx +1 -0
  179. package/src/app/layout-constants.ts +13 -0
  180. package/src/app/messaging/index.tsx +648 -0
  181. package/src/app/messaging/platform-icon.tsx +93 -0
  182. package/src/app/model-picker-overlay.tsx +42 -0
  183. package/src/app/model-visibility-overlay.tsx +31 -0
  184. package/src/app/overlays/overlay-chrome.tsx +66 -0
  185. package/src/app/overlays/overlay-search-input.tsx +33 -0
  186. package/src/app/overlays/overlay-split-layout.tsx +130 -0
  187. package/src/app/overlays/overlay-view.tsx +91 -0
  188. package/src/app/page-search-shell.tsx +75 -0
  189. package/src/app/profiles/create-profile-dialog.tsx +154 -0
  190. package/src/app/profiles/delete-profile-dialog.tsx +65 -0
  191. package/src/app/profiles/index.tsx +671 -0
  192. package/src/app/profiles/rename-profile-dialog.tsx +125 -0
  193. package/src/app/right-sidebar/files/dnd-manager.ts +27 -0
  194. package/src/app/right-sidebar/files/ipc.test.ts +100 -0
  195. package/src/app/right-sidebar/files/ipc.ts +161 -0
  196. package/src/app/right-sidebar/files/remote-picker.tsx +177 -0
  197. package/src/app/right-sidebar/files/tree.tsx +224 -0
  198. package/src/app/right-sidebar/files/use-project-tree.test.ts +190 -0
  199. package/src/app/right-sidebar/files/use-project-tree.ts +268 -0
  200. package/src/app/right-sidebar/index.test.tsx +75 -0
  201. package/src/app/right-sidebar/index.tsx +395 -0
  202. package/src/app/right-sidebar/store.ts +15 -0
  203. package/src/app/right-sidebar/terminal/buffer.ts +65 -0
  204. package/src/app/right-sidebar/terminal/index.tsx +98 -0
  205. package/src/app/right-sidebar/terminal/persistent.tsx +122 -0
  206. package/src/app/right-sidebar/terminal/selection.ts +75 -0
  207. package/src/app/right-sidebar/terminal/use-terminal-session.ts +504 -0
  208. package/src/app/routes.ts +88 -0
  209. package/src/app/session/hooks/use-context-suggestions.ts +58 -0
  210. package/src/app/session/hooks/use-cwd-actions.ts +109 -0
  211. package/src/app/session/hooks/use-message-stream.ts +957 -0
  212. package/src/app/session/hooks/use-model-controls.test.tsx +198 -0
  213. package/src/app/session/hooks/use-model-controls.ts +106 -0
  214. package/src/app/session/hooks/use-nastech-config.ts +74 -0
  215. package/src/app/session/hooks/use-preview-routing.test.tsx +168 -0
  216. package/src/app/session/hooks/use-preview-routing.ts +223 -0
  217. package/src/app/session/hooks/use-prompt-actions.test.tsx +316 -0
  218. package/src/app/session/hooks/use-prompt-actions.ts +1030 -0
  219. package/src/app/session/hooks/use-route-resume.test.tsx +136 -0
  220. package/src/app/session/hooks/use-route-resume.ts +115 -0
  221. package/src/app/session/hooks/use-session-actions.test.tsx +119 -0
  222. package/src/app/session/hooks/use-session-actions.ts +885 -0
  223. package/src/app/session/hooks/use-session-state-cache.test.tsx +118 -0
  224. package/src/app/session/hooks/use-session-state-cache.ts +191 -0
  225. package/src/app/session-picker-overlay.tsx +32 -0
  226. package/src/app/session-switcher.tsx +107 -0
  227. package/src/app/settings/about-settings.tsx +173 -0
  228. package/src/app/settings/appearance-settings.tsx +162 -0
  229. package/src/app/settings/config-settings.tsx +384 -0
  230. package/src/app/settings/constants.ts +545 -0
  231. package/src/app/settings/credential-key-ui.tsx +373 -0
  232. package/src/app/settings/env-credentials.tsx +198 -0
  233. package/src/app/settings/env-var-actions-menu.tsx +136 -0
  234. package/src/app/settings/field-copy.ts +56 -0
  235. package/src/app/settings/gateway-settings.tsx +620 -0
  236. package/src/app/settings/helpers.test.ts +138 -0
  237. package/src/app/settings/helpers.ts +151 -0
  238. package/src/app/settings/index.tsx +237 -0
  239. package/src/app/settings/keys-settings.tsx +96 -0
  240. package/src/app/settings/mcp-settings.tsx +271 -0
  241. package/src/app/settings/model-settings.test.tsx +157 -0
  242. package/src/app/settings/model-settings.tsx +559 -0
  243. package/src/app/settings/notifications-settings.tsx +150 -0
  244. package/src/app/settings/primitives.tsx +115 -0
  245. package/src/app/settings/providers-settings.test.tsx +100 -0
  246. package/src/app/settings/providers-settings.tsx +258 -0
  247. package/src/app/settings/sessions-settings.tsx +276 -0
  248. package/src/app/settings/toolset-config-panel.test.tsx +289 -0
  249. package/src/app/settings/toolset-config-panel.tsx +449 -0
  250. package/src/app/settings/types.ts +42 -0
  251. package/src/app/settings/uninstall-section.tsx +185 -0
  252. package/src/app/settings/use-deep-link-highlight.ts +60 -0
  253. package/src/app/shell/app-shell.tsx +167 -0
  254. package/src/app/shell/gateway-menu-panel.tsx +150 -0
  255. package/src/app/shell/hooks/use-overlay-routing.ts +71 -0
  256. package/src/app/shell/hooks/use-status-snapshot.ts +57 -0
  257. package/src/app/shell/hooks/use-statusbar-items.tsx +403 -0
  258. package/src/app/shell/keybind-panel.tsx +220 -0
  259. package/src/app/shell/model-edit-submenu.test.tsx +84 -0
  260. package/src/app/shell/model-edit-submenu.tsx +245 -0
  261. package/src/app/shell/model-menu-panel.tsx +295 -0
  262. package/src/app/shell/sidebar-label.tsx +22 -0
  263. package/src/app/shell/statusbar-controls.tsx +185 -0
  264. package/src/app/shell/titlebar-controls.tsx +244 -0
  265. package/src/app/shell/titlebar.test.ts +26 -0
  266. package/src/app/shell/titlebar.ts +45 -0
  267. package/src/app/shell/use-group-registry.ts +39 -0
  268. package/src/app/skills/index.test.tsx +103 -0
  269. package/src/app/skills/index.tsx +371 -0
  270. package/src/app/types.ts +99 -0
  271. package/src/app/updates-overlay.tsx +369 -0
  272. package/src/components/Backdrop.tsx +114 -0
  273. package/src/components/assistant-ui/ansi-text.tsx +34 -0
  274. package/src/components/assistant-ui/clarify-tool.tsx +281 -0
  275. package/src/components/assistant-ui/directive-text.test.ts +39 -0
  276. package/src/components/assistant-ui/directive-text.tsx +389 -0
  277. package/src/components/assistant-ui/markdown-text.test.ts +204 -0
  278. package/src/components/assistant-ui/markdown-text.tsx +497 -0
  279. package/src/components/assistant-ui/message-render-boundary.test.tsx +80 -0
  280. package/src/components/assistant-ui/message-render-boundary.tsx +48 -0
  281. package/src/components/assistant-ui/streaming.test.tsx +739 -0
  282. package/src/components/assistant-ui/thread-list.tsx +307 -0
  283. package/src/components/assistant-ui/thread-virtualizer.tsx +512 -0
  284. package/src/components/assistant-ui/thread.tsx +1474 -0
  285. package/src/components/assistant-ui/todo-tool.tsx +109 -0
  286. package/src/components/assistant-ui/tool-approval-group.test.tsx +158 -0
  287. package/src/components/assistant-ui/tool-approval.test.tsx +81 -0
  288. package/src/components/assistant-ui/tool-approval.tsx +209 -0
  289. package/src/components/assistant-ui/tool-fallback-model.test.ts +66 -0
  290. package/src/components/assistant-ui/tool-fallback-model.ts +1368 -0
  291. package/src/components/assistant-ui/tool-fallback.tsx +466 -0
  292. package/src/components/assistant-ui/tooltip-icon-button.tsx +33 -0
  293. package/src/components/assistant-ui/user-message-edit.test.tsx +141 -0
  294. package/src/components/assistant-ui/user-message-text.tsx +150 -0
  295. package/src/components/boot-failure-overlay.tsx +246 -0
  296. package/src/components/boot-failure-reauth.test.ts +100 -0
  297. package/src/components/boot-failure-reauth.ts +81 -0
  298. package/src/components/brand-mark.tsx +19 -0
  299. package/src/components/chat/activity-timer-text.tsx +24 -0
  300. package/src/components/chat/activity-timer.test.tsx +43 -0
  301. package/src/components/chat/activity-timer.ts +64 -0
  302. package/src/components/chat/code-card.tsx +78 -0
  303. package/src/components/chat/compact-markdown.tsx +113 -0
  304. package/src/components/chat/composer-dock.ts +31 -0
  305. package/src/components/chat/diff-lines.tsx +54 -0
  306. package/src/components/chat/disclosure-row.tsx +63 -0
  307. package/src/components/chat/generated-image-context.tsx +19 -0
  308. package/src/components/chat/generated-image-result.tsx +174 -0
  309. package/src/components/chat/image-generation-placeholder.tsx +279 -0
  310. package/src/components/chat/intro-copy.jsonl +75 -0
  311. package/src/components/chat/intro.tsx +182 -0
  312. package/src/components/chat/preview-attachment.tsx +125 -0
  313. package/src/components/chat/shiki-highlighter.tsx +107 -0
  314. package/src/components/chat/status-row.tsx +70 -0
  315. package/src/components/chat/status-section.tsx +42 -0
  316. package/src/components/chat/terminal-output.tsx +50 -0
  317. package/src/components/chat/zoomable-image.tsx +177 -0
  318. package/src/components/desktop-install-overlay.tsx +595 -0
  319. package/src/components/desktop-onboarding-overlay.test.tsx +100 -0
  320. package/src/components/desktop-onboarding-overlay.tsx +1286 -0
  321. package/src/components/error-boundary.tsx +77 -0
  322. package/src/components/gateway-connecting-overlay.test.tsx +143 -0
  323. package/src/components/gateway-connecting-overlay.tsx +183 -0
  324. package/src/components/haptics-provider.tsx +19 -0
  325. package/src/components/language-switcher.test.tsx +53 -0
  326. package/src/components/language-switcher.tsx +175 -0
  327. package/src/components/model-picker.tsx +340 -0
  328. package/src/components/model-visibility-dialog.tsx +155 -0
  329. package/src/components/notifications.tsx +196 -0
  330. package/src/components/page-loader.tsx +34 -0
  331. package/src/components/pane-shell/context.ts +14 -0
  332. package/src/components/pane-shell/index.ts +4 -0
  333. package/src/components/pane-shell/pane-shell.test.tsx +333 -0
  334. package/src/components/pane-shell/pane-shell.tsx +330 -0
  335. package/src/components/prompt-overlays.tsx +234 -0
  336. package/src/components/session-picker.tsx +108 -0
  337. package/src/components/status-dot.tsx +26 -0
  338. package/src/components/ui/action-status.tsx +25 -0
  339. package/src/components/ui/alert.tsx +53 -0
  340. package/src/components/ui/badge.tsx +35 -0
  341. package/src/components/ui/braille-spinner.tsx +61 -0
  342. package/src/components/ui/button.tsx +81 -0
  343. package/src/components/ui/checkbox.tsx +27 -0
  344. package/src/components/ui/codicon.tsx +20 -0
  345. package/src/components/ui/command.tsx +111 -0
  346. package/src/components/ui/confirm-dialog.tsx +109 -0
  347. package/src/components/ui/context-menu.tsx +141 -0
  348. package/src/components/ui/control.ts +25 -0
  349. package/src/components/ui/copy-button.test.tsx +36 -0
  350. package/src/components/ui/copy-button.tsx +229 -0
  351. package/src/components/ui/dialog.tsx +152 -0
  352. package/src/components/ui/disclosure-caret.tsx +20 -0
  353. package/src/components/ui/dropdown-menu.tsx +291 -0
  354. package/src/components/ui/error-state.tsx +50 -0
  355. package/src/components/ui/fade-text.tsx +110 -0
  356. package/src/components/ui/glyph-spinner.tsx +63 -0
  357. package/src/components/ui/input.tsx +22 -0
  358. package/src/components/ui/kbd.tsx +37 -0
  359. package/src/components/ui/loader.tsx +558 -0
  360. package/src/components/ui/log-view.tsx +17 -0
  361. package/src/components/ui/pagination.tsx +114 -0
  362. package/src/components/ui/popover.tsx +44 -0
  363. package/src/components/ui/scroll-area.tsx +43 -0
  364. package/src/components/ui/search-field.tsx +80 -0
  365. package/src/components/ui/segmented-control.tsx +51 -0
  366. package/src/components/ui/select.tsx +92 -0
  367. package/src/components/ui/separator.tsx +26 -0
  368. package/src/components/ui/sheet.tsx +116 -0
  369. package/src/components/ui/sidebar.tsx +674 -0
  370. package/src/components/ui/skeleton.tsx +7 -0
  371. package/src/components/ui/switch.tsx +49 -0
  372. package/src/components/ui/tabs.tsx +36 -0
  373. package/src/components/ui/text-tab.tsx +43 -0
  374. package/src/components/ui/textarea.tsx +11 -0
  375. package/src/components/ui/tool-icon.tsx +65 -0
  376. package/src/components/ui/tooltip.tsx +69 -0
  377. package/src/fonts/JetBrainsMono-Bold.woff2 +0 -0
  378. package/src/fonts/JetBrainsMono-Italic.woff2 +0 -0
  379. package/src/fonts/JetBrainsMono-Regular.woff2 +0 -0
  380. package/src/global.d.ts +457 -0
  381. package/src/hooks/use-image-download.ts +85 -0
  382. package/src/hooks/use-media-query.ts +24 -0
  383. package/src/hooks/use-mobile.ts +3 -0
  384. package/src/hooks/use-resize-observer.ts +38 -0
  385. package/src/hooks/use-worktree-info.ts +68 -0
  386. package/src/i18n/catalog.ts +12 -0
  387. package/src/i18n/context.test.tsx +232 -0
  388. package/src/i18n/context.tsx +183 -0
  389. package/src/i18n/define-locale.ts +41 -0
  390. package/src/i18n/en.ts +1779 -0
  391. package/src/i18n/index.ts +20 -0
  392. package/src/i18n/ja.ts +1890 -0
  393. package/src/i18n/languages.test.ts +43 -0
  394. package/src/i18n/languages.ts +86 -0
  395. package/src/i18n/runtime.test.ts +75 -0
  396. package/src/i18n/runtime.ts +53 -0
  397. package/src/i18n/types.ts +1452 -0
  398. package/src/i18n/zh-hant.ts +1849 -0
  399. package/src/i18n/zh.ts +1923 -0
  400. package/src/lib/ansi.test.ts +123 -0
  401. package/src/lib/ansi.ts +175 -0
  402. package/src/lib/chat-messages.test.ts +708 -0
  403. package/src/lib/chat-messages.ts +885 -0
  404. package/src/lib/chat-runtime.test.ts +18 -0
  405. package/src/lib/chat-runtime.ts +335 -0
  406. package/src/lib/clipboard.ts +28 -0
  407. package/src/lib/commit-changelog.test.ts +114 -0
  408. package/src/lib/commit-changelog.ts +177 -0
  409. package/src/lib/completion-sound.ts +519 -0
  410. package/src/lib/desktop-fs.test.ts +116 -0
  411. package/src/lib/desktop-fs.ts +113 -0
  412. package/src/lib/desktop-slash-commands.test.ts +126 -0
  413. package/src/lib/desktop-slash-commands.ts +286 -0
  414. package/src/lib/embedded-images.test.ts +35 -0
  415. package/src/lib/embedded-images.ts +60 -0
  416. package/src/lib/external-link.test.tsx +168 -0
  417. package/src/lib/external-link.tsx +303 -0
  418. package/src/lib/gateway-events.test.ts +27 -0
  419. package/src/lib/gateway-events.ts +49 -0
  420. package/src/lib/gateway-ws-url.test.ts +78 -0
  421. package/src/lib/gateway-ws-url.ts +91 -0
  422. package/src/lib/generated-images.test.ts +97 -0
  423. package/src/lib/generated-images.ts +116 -0
  424. package/src/lib/haptics.ts +129 -0
  425. package/src/lib/icons.ts +203 -0
  426. package/src/lib/incremental-external-store-runtime.ts +188 -0
  427. package/src/lib/katex-memo.ts +260 -0
  428. package/src/lib/keybinds/actions.ts +125 -0
  429. package/src/lib/keybinds/combo.test.ts +86 -0
  430. package/src/lib/keybinds/combo.ts +169 -0
  431. package/src/lib/local-preview.ts +126 -0
  432. package/src/lib/markdown-code.test.ts +23 -0
  433. package/src/lib/markdown-code.ts +195 -0
  434. package/src/lib/markdown-preprocess.ts +386 -0
  435. package/src/lib/media.remote.test.ts +58 -0
  436. package/src/lib/media.ts +111 -0
  437. package/src/lib/model-status-label.test.ts +31 -0
  438. package/src/lib/model-status-label.ts +103 -0
  439. package/src/lib/mutable-ref.ts +6 -0
  440. package/src/lib/preview-targets.test.ts +27 -0
  441. package/src/lib/preview-targets.ts +63 -0
  442. package/src/lib/profile-color.ts +58 -0
  443. package/src/lib/provider-setup-errors.test.ts +26 -0
  444. package/src/lib/provider-setup-errors.ts +12 -0
  445. package/src/lib/query-client.ts +13 -0
  446. package/src/lib/remend-tail.test.ts +105 -0
  447. package/src/lib/remend-tail.ts +108 -0
  448. package/src/lib/runtime-readiness.test.ts +65 -0
  449. package/src/lib/runtime-readiness.ts +147 -0
  450. package/src/lib/session-export.ts +57 -0
  451. package/src/lib/session-search.test.ts +58 -0
  452. package/src/lib/session-search.ts +19 -0
  453. package/src/lib/session-source.ts +62 -0
  454. package/src/lib/speech-text.ts +35 -0
  455. package/src/lib/statusbar.ts +91 -0
  456. package/src/lib/storage.test.ts +25 -0
  457. package/src/lib/storage.ts +107 -0
  458. package/src/lib/todos.test.ts +35 -0
  459. package/src/lib/todos.ts +51 -0
  460. package/src/lib/tool-result-summary.test.ts +106 -0
  461. package/src/lib/tool-result-summary.ts +467 -0
  462. package/src/lib/update-copy.test.ts +38 -0
  463. package/src/lib/update-copy.ts +44 -0
  464. package/src/lib/use-enter-animation.ts +100 -0
  465. package/src/lib/utils.ts +6 -0
  466. package/src/lib/voice-playback.ts +128 -0
  467. package/src/lib/yolo-session.ts +26 -0
  468. package/src/main.tsx +43 -0
  469. package/src/nastech.test.ts +49 -0
  470. package/src/nastech.ts +718 -0
  471. package/src/store/activity.ts +100 -0
  472. package/src/store/boot.ts +91 -0
  473. package/src/store/clarify.test.ts +81 -0
  474. package/src/store/clarify.ts +69 -0
  475. package/src/store/command-palette.ts +20 -0
  476. package/src/store/compaction.test.ts +53 -0
  477. package/src/store/compaction.ts +38 -0
  478. package/src/store/completion-sound.ts +32 -0
  479. package/src/store/composer-input-history.test.ts +147 -0
  480. package/src/store/composer-input-history.ts +158 -0
  481. package/src/store/composer-queue.test.ts +148 -0
  482. package/src/store/composer-queue.ts +239 -0
  483. package/src/store/composer-status.test.ts +99 -0
  484. package/src/store/composer-status.ts +277 -0
  485. package/src/store/composer.test.ts +106 -0
  486. package/src/store/composer.ts +184 -0
  487. package/src/store/cron.ts +19 -0
  488. package/src/store/gateway.ts +290 -0
  489. package/src/store/haptics.ts +17 -0
  490. package/src/store/keybinds.ts +139 -0
  491. package/src/store/layout.ts +176 -0
  492. package/src/store/model-presets.test.ts +51 -0
  493. package/src/store/model-presets.ts +86 -0
  494. package/src/store/model-visibility.test.ts +37 -0
  495. package/src/store/model-visibility.ts +108 -0
  496. package/src/store/native-notifications.test.ts +192 -0
  497. package/src/store/native-notifications.ts +203 -0
  498. package/src/store/notifications.ts +165 -0
  499. package/src/store/onboarding.test.ts +372 -0
  500. package/src/store/onboarding.ts +866 -0
  501. package/src/store/panes.test.ts +146 -0
  502. package/src/store/panes.ts +145 -0
  503. package/src/store/preview.test.ts +135 -0
  504. package/src/store/preview.ts +466 -0
  505. package/src/store/profile.test.ts +89 -0
  506. package/src/store/profile.ts +365 -0
  507. package/src/store/prompts.test.ts +121 -0
  508. package/src/store/prompts.ts +115 -0
  509. package/src/store/session-switcher.test.ts +115 -0
  510. package/src/store/session-switcher.ts +128 -0
  511. package/src/store/session-sync.ts +25 -0
  512. package/src/store/session.test.ts +131 -0
  513. package/src/store/session.ts +255 -0
  514. package/src/store/subagents.test.ts +111 -0
  515. package/src/store/subagents.ts +260 -0
  516. package/src/store/thread-scroll.ts +46 -0
  517. package/src/store/todos.test.ts +47 -0
  518. package/src/store/todos.ts +64 -0
  519. package/src/store/tool-diffs.ts +23 -0
  520. package/src/store/tool-dismiss.ts +45 -0
  521. package/src/store/tool-view.ts +91 -0
  522. package/src/store/translucency.ts +38 -0
  523. package/src/store/updates.test.ts +77 -0
  524. package/src/store/updates.ts +315 -0
  525. package/src/store/voice-playback.ts +24 -0
  526. package/src/store/windows.test.ts +143 -0
  527. package/src/store/windows.ts +77 -0
  528. package/src/styles.css +1235 -0
  529. package/src/themes/color.ts +142 -0
  530. package/src/themes/context.tsx +339 -0
  531. package/src/themes/index.ts +3 -0
  532. package/src/themes/install.test.ts +119 -0
  533. package/src/themes/install.ts +95 -0
  534. package/src/themes/presets.test.ts +33 -0
  535. package/src/themes/presets.ts +293 -0
  536. package/src/themes/profile-theme.test.ts +41 -0
  537. package/src/themes/types.ts +66 -0
  538. package/src/themes/use-skin-command.ts +60 -0
  539. package/src/themes/user-themes.test.ts +63 -0
  540. package/src/themes/user-themes.ts +122 -0
  541. package/src/themes/vscode.test.ts +171 -0
  542. package/src/themes/vscode.ts +343 -0
  543. package/src/types/nastech.ts +646 -0
  544. package/src/vite-env.d.ts +1 -0
  545. package/tsconfig.json +25 -0
  546. package/vite.config.ts +56 -0
@@ -0,0 +1,457 @@
1
+ export {}
2
+
3
+ declare global {
4
+ interface Window {
5
+ NASTECHDesktop: {
6
+ // Resolve a backend connection. Omit `profile` (or pass the primary) for
7
+ // the window's backend; pass a named profile to lazily spawn/reuse that
8
+ // profile's backend from the pool.
9
+ getConnection: (profile?: string | null) => Promise<NasTechConnection>
10
+ // Keepalive: mark a pool profile backend as recently used so the idle
11
+ // reaper spares it while its chat is active.
12
+ touchBackend: (profile?: string | null) => Promise<{ ok: boolean }>
13
+ getGatewayWsUrl: (profile?: null | string) => Promise<string>
14
+ getBootProgress: () => Promise<DesktopBootProgress>
15
+ getConnectionConfig: (profile?: null | string) => Promise<DesktopConnectionConfig>
16
+ saveConnectionConfig: (payload: DesktopConnectionConfigInput) => Promise<DesktopConnectionConfig>
17
+ applyConnectionConfig: (payload: DesktopConnectionConfigInput) => Promise<DesktopConnectionConfig>
18
+ testConnectionConfig: (payload: DesktopConnectionConfigInput) => Promise<DesktopConnectionTestResult>
19
+ probeConnectionConfig: (remoteUrl: string) => Promise<DesktopConnectionProbeResult>
20
+ oauthLoginConnectionConfig: (remoteUrl: string) => Promise<DesktopOauthLoginResult>
21
+ oauthLogoutConnectionConfig: (remoteUrl?: string) => Promise<DesktopOauthLogoutResult>
22
+ profile: {
23
+ get: () => Promise<DesktopActiveProfile>
24
+ // Persists the desktop's profile choice and relaunches the local
25
+ // backend under the new NASTECH_HOME (reloads the window). Pass null to
26
+ // clear the preference.
27
+ set: (name: string | null) => Promise<DesktopActiveProfile>
28
+ }
29
+ api: <T>(request: NasTechApiRequest) => Promise<T>
30
+ notify: (payload: NasTechNotification) => Promise<boolean>
31
+ requestMicrophoneAccess: () => Promise<boolean>
32
+ readFileDataUrl: (filePath: string) => Promise<string>
33
+ readFileText: (filePath: string) => Promise<NasTechReadFileTextResult>
34
+ selectPaths: (options?: NasTechSelectPathsOptions) => Promise<string[]>
35
+ writeClipboard: (text: string) => Promise<boolean>
36
+ saveImageFromUrl: (url: string) => Promise<boolean>
37
+ saveImageBuffer: (data: ArrayBuffer | Uint8Array, ext: string) => Promise<string>
38
+ saveClipboardImage: () => Promise<string>
39
+ getPathForFile: (file: File) => string
40
+ normalizePreviewTarget: (target: string, baseDir?: string) => Promise<NasTechPreviewTarget | null>
41
+ watchPreviewFile: (url: string) => Promise<NasTechPreviewWatch>
42
+ stopPreviewFileWatch: (id: string) => Promise<boolean>
43
+ setTitleBarTheme?: (payload: NasTechTitleBarTheme) => void
44
+ setPreviewShortcutActive?: (active: boolean) => void
45
+ openExternal: (url: string) => Promise<void>
46
+ fetchLinkTitle: (url: string) => Promise<string>
47
+ settings: {
48
+ getDefaultProjectDir: () => Promise<{ defaultLabel: string; dir: null | string }>
49
+ pickDefaultProjectDir: () => Promise<{ canceled: boolean; dir: null | string }>
50
+ setDefaultProjectDir: (dir: null | string) => Promise<{ dir: null | string }>
51
+ }
52
+ revealLogs: () => Promise<{ ok: boolean; path: string; error?: string }>
53
+ getRecentLogs: () => Promise<{ path: string; lines: string[] }>
54
+ readDir: (path: string) => Promise<NasTechReadDirResult>
55
+ gitRoot?: (path: string) => Promise<string | null>
56
+ terminal: {
57
+ dispose: (id: string) => Promise<boolean>
58
+ onData: (id: string, callback: (payload: string) => void) => () => void
59
+ onExit: (id: string, callback: (payload: NasTechTerminalExit) => void) => () => void
60
+ resize: (id: string, size: { cols: number; rows: number }) => Promise<boolean>
61
+ start: (options?: { cols?: number; cwd?: string; rows?: number }) => Promise<NasTechTerminalSession>
62
+ write: (id: string, data: string) => Promise<boolean>
63
+ }
64
+ onClosePreviewRequested?: (callback: () => void) => () => void
65
+ onOpenUpdatesRequested?: (callback: () => void) => () => void
66
+ onWindowStateChanged?: (callback: (payload: NasTechWindowState) => void) => () => void
67
+ onPreviewFileChanged: (callback: (payload: NasTechPreviewFileChanged) => void) => () => void
68
+ onBackendExit: (callback: (payload: BackendExit) => void) => () => void
69
+ onPowerResume?: (callback: () => void) => () => void
70
+ onBootProgress: (callback: (payload: DesktopBootProgress) => void) => () => void
71
+ getBootstrapState: () => Promise<DesktopBootstrapState>
72
+ resetBootstrap: () => Promise<{ ok: boolean }>
73
+ repairBootstrap: () => Promise<{ ok: boolean }>
74
+ cancelBootstrap: () => Promise<{ ok: boolean; cancelled: boolean }>
75
+ onBootstrapEvent: (callback: (payload: DesktopBootstrapEvent) => void) => () => void
76
+ getVersion: () => Promise<DesktopVersionInfo>
77
+ updates: {
78
+ check: () => Promise<DesktopUpdateStatus>
79
+ apply: (opts?: DesktopUpdateApplyOptions) => Promise<DesktopUpdateApplyResult>
80
+ getBranch: () => Promise<{ branch: string }>
81
+ setBranch: (name: string) => Promise<{ branch: string }>
82
+ onProgress: (callback: (payload: DesktopUpdateProgress) => void) => () => void
83
+ }
84
+ uninstall: {
85
+ summary: () => Promise<DesktopUninstallSummary>
86
+ run: (mode: DesktopUninstallMode) => Promise<DesktopUninstallResult>
87
+ }
88
+ openSessionWindow: (sessionId: string, opts?: { watch?: boolean }) => Promise<{ ok: boolean; error?: string }>
89
+ setTranslucency?: (payload: { intensity: number }) => void
90
+ sanitizeWorkspaceCwd?: (cwd?: null | string) => Promise<{ cwd: string; sanitized: boolean }>
91
+ worktrees?: (cwds: string[]) => Promise<Record<string, NasTechWorktreeInfo | null>>
92
+ themes?: {
93
+ fetchMarketplace: (id: string) => Promise<DesktopMarketplaceThemeResult>
94
+ searchMarketplace: (query: string) => Promise<DesktopMarketplaceSearchItem[]>
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ export interface NasTechTerminalSession {
101
+ cwd: string
102
+ id: string
103
+ shell: string
104
+ }
105
+
106
+ export interface NasTechTerminalExit {
107
+ code: number | null
108
+ signal: string | null
109
+ }
110
+
111
+ export interface DesktopVersionInfo {
112
+ appVersion: string
113
+ electronVersion: string
114
+ nodeVersion: string
115
+ platform: string
116
+ NASTECHRoot: string
117
+ }
118
+
119
+ export type DesktopUninstallMode = 'full' | 'gui' | 'lite'
120
+
121
+ export interface DesktopUninstallSummary {
122
+ NASTECH_HOME: string
123
+ agent_installed: boolean
124
+ gui_installed: boolean
125
+ source_built_artifacts: string[]
126
+ packaged_app_paths: string[]
127
+ userdata_dir: string
128
+ userdata_exists: boolean
129
+ platform: string
130
+ running_app_path?: null | string
131
+ probe?: string
132
+ }
133
+
134
+ export interface DesktopUninstallResult {
135
+ ok: boolean
136
+ mode?: DesktopUninstallMode
137
+ willRemoveAppBundle?: boolean
138
+ scriptPath?: string
139
+ error?: string
140
+ message?: string
141
+ }
142
+
143
+ export interface DesktopUpdateCommit {
144
+ sha: string
145
+ summary: string
146
+ author: string
147
+ at: number
148
+ }
149
+
150
+ export interface DesktopUpdateStatus {
151
+ supported: boolean
152
+ branch?: string
153
+ currentBranch?: string
154
+ reason?: string
155
+ message?: string
156
+ error?: string
157
+ behind?: number
158
+ currentSha?: string
159
+ targetSha?: string
160
+ commits?: DesktopUpdateCommit[]
161
+ dirty?: boolean
162
+ fetchedAt?: number
163
+ }
164
+
165
+ export type DesktopUpdateDirtyStrategy = 'abort' | 'stash' | 'force'
166
+
167
+ export interface DesktopUpdateApplyOptions {
168
+ dirtyStrategy?: DesktopUpdateDirtyStrategy
169
+ }
170
+
171
+ export interface DesktopUpdateApplyResult {
172
+ ok: boolean
173
+ branch?: string
174
+ error?: string
175
+ message?: string
176
+ /** True when no staged updater exists (CLI install) and the user should run
177
+ * `NASTECH update` themselves. `command` is the exact line to run. */
178
+ manual?: boolean
179
+ command?: string
180
+ NASTECHRoot?: string
181
+ }
182
+
183
+ export type DesktopUpdateStage = 'idle' | 'prepare' | 'fetch' | 'pull' | 'pydeps' | 'restart' | 'manual' | 'error'
184
+
185
+ export interface DesktopUpdateProgress {
186
+ stage: DesktopUpdateStage
187
+ message: string
188
+ percent: number | null
189
+ error: string | null
190
+ at: number
191
+ }
192
+
193
+ export interface NasTechConnection {
194
+ baseUrl: string
195
+ isFullscreen: boolean
196
+ mode?: 'local' | 'remote'
197
+ authMode?: 'oauth' | 'token'
198
+ nativeOverlayWidth: number
199
+ source?: 'env' | 'local' | 'settings'
200
+ token: string
201
+ wsUrl: string
202
+ logs: string[]
203
+ // Set for pool (non-primary) backends so the renderer knows which profile a
204
+ // connection belongs to.
205
+ profile?: string
206
+ windowButtonPosition: { x: number; y: number } | null
207
+ }
208
+
209
+ export interface NasTechTitleBarTheme {
210
+ background: string
211
+ foreground: string
212
+ }
213
+
214
+ export interface NasTechWindowState {
215
+ isFullscreen: boolean
216
+ nativeOverlayWidth: number
217
+ windowButtonPosition: { x: number; y: number } | null
218
+ }
219
+
220
+ export interface DesktopActiveProfile {
221
+ // The desktop's stored profile preference, or null when unset (legacy launch
222
+ // that defers to the sticky active_profile / default).
223
+ profile: string | null
224
+ }
225
+
226
+ export interface DesktopConnectionConfig {
227
+ envOverride: boolean
228
+ mode: 'local' | 'remote'
229
+ // The profile this config describes, or null for the global/default
230
+ // connection. Per-profile entries let a profile point at its own backend.
231
+ profile: null | string
232
+ remoteAuthMode: 'oauth' | 'token'
233
+ remoteOauthConnected: boolean
234
+ remoteTokenPreview: string | null
235
+ remoteTokenSet: boolean
236
+ remoteUrl: string
237
+ }
238
+
239
+ export interface DesktopConnectionConfigInput {
240
+ mode: 'local' | 'remote'
241
+ // When set, the save/apply/test targets this profile's per-profile remote
242
+ // override instead of the global connection.
243
+ profile?: null | string
244
+ remoteAuthMode?: 'oauth' | 'token'
245
+ remoteToken?: string
246
+ remoteUrl?: string
247
+ }
248
+
249
+ export interface DesktopConnectionTestResult {
250
+ baseUrl: string
251
+ ok: boolean
252
+ version: string | null
253
+ }
254
+
255
+ export interface DesktopAuthProvider {
256
+ name: string
257
+ displayName: string
258
+ // True when this provider authenticates with a username + password
259
+ // (the gateway's /login page renders a credential form) rather than an
260
+ // OAuth redirect. The session/cookie/ws-ticket machinery is identical;
261
+ // only the login-page form and the desktop's button copy differ.
262
+ supportsPassword?: boolean
263
+ }
264
+
265
+ export interface DesktopConnectionProbeResult {
266
+ baseUrl: string
267
+ reachable: boolean
268
+ authMode: 'oauth' | 'token' | 'unknown'
269
+ providers: DesktopAuthProvider[]
270
+ version: string | null
271
+ error: string | null
272
+ }
273
+
274
+ export interface DesktopOauthLoginResult {
275
+ ok: boolean
276
+ baseUrl: string
277
+ connected: boolean
278
+ }
279
+
280
+ export interface DesktopOauthLogoutResult {
281
+ ok: boolean
282
+ connected: boolean
283
+ }
284
+
285
+ export interface DesktopBootProgress {
286
+ error: string | null
287
+ fakeMode: boolean
288
+ message: string
289
+ phase: string
290
+ progress: number
291
+ running: boolean
292
+ timestamp: number
293
+ }
294
+
295
+ // First-launch install ("bootstrap") event types -- emitted by
296
+ // electron/bootstrap-runner.cjs and observed by the renderer install overlay.
297
+ // Mirrors the event shapes emitted by runBootstrap()'s onEvent callback.
298
+
299
+ export interface DesktopBootstrapStageDescriptor {
300
+ name: string
301
+ title?: string
302
+ category?: string
303
+ needs_user_input?: boolean
304
+ }
305
+
306
+ export type DesktopBootstrapStageState = 'pending' | 'running' | 'succeeded' | 'skipped' | 'failed'
307
+
308
+ export interface DesktopBootstrapStageResult {
309
+ state: DesktopBootstrapStageState
310
+ durationMs: number | null
311
+ startedAt: number | null
312
+ json: { ok: boolean; skipped?: boolean; reason?: string | null; stage: string } | null
313
+ error: string | null
314
+ }
315
+
316
+ export interface DesktopBootstrapUnsupportedPlatform {
317
+ platform: string
318
+ activeRoot: string
319
+ installCommand: string
320
+ docsUrl: string
321
+ }
322
+
323
+ export interface DesktopBootstrapState {
324
+ active: boolean
325
+ manifest: { type: 'manifest'; stages: DesktopBootstrapStageDescriptor[]; protocolVersion: number | null } | null
326
+ stages: Record<string, DesktopBootstrapStageResult>
327
+ error: string | null
328
+ log: Array<{ ts: number; stage: string | null; line: string; stream?: 'stdout' | 'stderr' }>
329
+ startedAt: number | null
330
+ completedAt: number | null
331
+ unsupportedPlatform: DesktopBootstrapUnsupportedPlatform | null
332
+ }
333
+
334
+ export type DesktopBootstrapEvent =
335
+ | { type: 'manifest'; stages: DesktopBootstrapStageDescriptor[]; protocolVersion: number | null }
336
+ | {
337
+ type: 'stage'
338
+ name: string
339
+ state: DesktopBootstrapStageState
340
+ durationMs?: number
341
+ json?: DesktopBootstrapStageResult['json']
342
+ error?: string | null
343
+ }
344
+ | { type: 'log'; stage?: string | null; line: string; stream?: 'stdout' | 'stderr' }
345
+ | { type: 'complete'; marker: Record<string, unknown> }
346
+ | { type: 'failed'; stage?: string | null; error: string }
347
+ | {
348
+ type: 'unsupported-platform'
349
+ platform: string
350
+ activeRoot: string
351
+ installCommand: string
352
+ docsUrl: string
353
+ }
354
+
355
+ export interface NasTechApiRequest {
356
+ path: string
357
+ method?: string
358
+ body?: unknown
359
+ timeoutMs?: number
360
+ // Route this REST call to a specific profile's backend. Omit for the primary
361
+ // (window) backend. Read-only cross-profile data is served by the primary, so
362
+ // this is only needed for profile-scoped live/settings calls.
363
+ profile?: string | null
364
+ }
365
+
366
+ export interface NasTechNotification {
367
+ title?: string
368
+ body?: string
369
+ silent?: boolean
370
+ }
371
+
372
+ export interface NasTechPreviewTarget {
373
+ binary?: boolean
374
+ byteSize?: number
375
+ kind: 'file' | 'url'
376
+ label: string
377
+ large?: boolean
378
+ language?: string
379
+ mimeType?: string
380
+ path?: string
381
+ previewKind?: 'binary' | 'html' | 'image' | 'text'
382
+ renderMode?: 'preview' | 'source'
383
+ source: string
384
+ url: string
385
+ }
386
+
387
+ export interface NasTechReadFileTextResult {
388
+ binary?: boolean
389
+ byteSize?: number
390
+ language?: string
391
+ mimeType?: string
392
+ path: string
393
+ text: string
394
+ truncated?: boolean
395
+ }
396
+
397
+ export interface NasTechPreviewWatch {
398
+ id: string
399
+ path: string
400
+ }
401
+
402
+ export interface NasTechReadDirEntry {
403
+ name: string
404
+ path: string
405
+ isDirectory: boolean
406
+ }
407
+
408
+ export interface NasTechReadDirResult {
409
+ entries: NasTechReadDirEntry[]
410
+ error?: string
411
+ }
412
+
413
+ export interface NasTechPreviewFileChanged {
414
+ id: string
415
+ path: string
416
+ url: string
417
+ }
418
+
419
+ export interface NasTechSelectPathsOptions {
420
+ title?: string
421
+ defaultPath?: string
422
+ directories?: boolean
423
+ multiple?: boolean
424
+ filters?: Array<{ name: string; extensions: string[] }>
425
+ }
426
+
427
+ export interface BackendExit {
428
+ code: number | null
429
+ signal: string | null
430
+ }
431
+
432
+ export interface NasTechWorktreeInfo {
433
+ repoRoot: string
434
+ worktreeRoot: string
435
+ isMainWorktree: boolean
436
+ branch: null | string
437
+ }
438
+
439
+ export interface DesktopMarketplaceSearchItem {
440
+ extensionId: string
441
+ displayName: string
442
+ publisher: string
443
+ description: string
444
+ installs: number
445
+ }
446
+
447
+ export interface DesktopMarketplaceThemeFile {
448
+ label: string
449
+ uiTheme?: string
450
+ contents: string
451
+ }
452
+
453
+ export interface DesktopMarketplaceThemeResult {
454
+ extensionId: string
455
+ displayName: string
456
+ themes: DesktopMarketplaceThemeFile[]
457
+ }
@@ -0,0 +1,85 @@
1
+ import { useCallback, useState } from 'react'
2
+
3
+ import { useI18n } from '@/i18n'
4
+ import { notify, notifyError } from '@/store/notifications'
5
+
6
+ export function imageFilename(src?: string): string {
7
+ if (!src) {
8
+ return 'image'
9
+ }
10
+
11
+ try {
12
+ return new URL(src, window.location.href).pathname.split('/').filter(Boolean).pop() || 'image'
13
+ } catch {
14
+ return src.split(/[\\/]/).filter(Boolean).pop() || 'image'
15
+ }
16
+ }
17
+
18
+ function isMissingIpcHandler(error: unknown): boolean {
19
+ const message = error instanceof Error ? error.message : typeof error === 'string' ? error : ''
20
+
21
+ return message.includes("No handler registered for 'nastech:saveImageFromUrl'")
22
+ }
23
+
24
+ async function startBrowserDownload(src: string) {
25
+ const response = await fetch(src)
26
+
27
+ if (!response.ok) {
28
+ throw new Error(`Could not fetch image: ${response.status}`)
29
+ }
30
+
31
+ const blobUrl = URL.createObjectURL(await response.blob())
32
+ const link = document.createElement('a')
33
+ link.href = blobUrl
34
+ link.download = imageFilename(src)
35
+ link.rel = 'noopener noreferrer'
36
+ document.body.appendChild(link)
37
+ link.click()
38
+ link.remove()
39
+ window.setTimeout(() => URL.revokeObjectURL(blobUrl), 30_000)
40
+ }
41
+
42
+ /** Save an image to disk via the desktop IPC bridge, falling back to a browser
43
+ * download when the handler is unavailable (older shell / web preview). */
44
+ export function useImageDownload(src?: string) {
45
+ const { t } = useI18n()
46
+ const copy = t.desktop
47
+ const [saving, setSaving] = useState(false)
48
+
49
+ const download = useCallback(async () => {
50
+ if (!src || saving) {
51
+ return
52
+ }
53
+
54
+ setSaving(true)
55
+
56
+ try {
57
+ if (window.NASTECHDesktop?.saveImageFromUrl) {
58
+ if (await window.NASTECHDesktop.saveImageFromUrl(src)) {
59
+ notify({ kind: 'success', title: copy.imageSaved, message: imageFilename(src) })
60
+ }
61
+
62
+ return
63
+ }
64
+
65
+ await startBrowserDownload(src)
66
+ } catch (error) {
67
+ if (isMissingIpcHandler(error)) {
68
+ try {
69
+ await startBrowserDownload(src)
70
+ notify({ kind: 'info', title: copy.downloadStarted, message: copy.restartToUseSaveImage })
71
+ } catch (fallbackError) {
72
+ notifyError(fallbackError, copy.restartToSaveImages)
73
+ }
74
+
75
+ return
76
+ }
77
+
78
+ notifyError(error, copy.imageDownloadFailed)
79
+ } finally {
80
+ setSaving(false)
81
+ }
82
+ }, [copy, saving, src])
83
+
84
+ return { download, saving }
85
+ }
@@ -0,0 +1,24 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ export const matchesQuery = (query: string) =>
4
+ typeof window !== 'undefined' && !!window.matchMedia && window.matchMedia(query).matches
5
+
6
+ export function useMediaQuery(query: string): boolean {
7
+ const [matches, setMatches] = useState(() => matchesQuery(query))
8
+
9
+ useEffect(() => {
10
+ if (typeof window === 'undefined' || !window.matchMedia) {
11
+ return
12
+ }
13
+
14
+ const mql = window.matchMedia(query)
15
+ const onChange = () => setMatches(mql.matches)
16
+
17
+ setMatches(mql.matches)
18
+ mql.addEventListener('change', onChange)
19
+
20
+ return () => mql.removeEventListener('change', onChange)
21
+ }, [query])
22
+
23
+ return matches
24
+ }
@@ -0,0 +1,3 @@
1
+ import { useMediaQuery } from './use-media-query'
2
+
3
+ export const useIsMobile = () => useMediaQuery(`(max-width: ${768 / 16 - 1 / 16}rem)`)
@@ -0,0 +1,38 @@
1
+ import { type RefObject, useLayoutEffect, useRef } from 'react'
2
+
3
+ export function useResizeObserver(onResize: () => void, ...refs: readonly RefObject<Element | null>[]) {
4
+ const refsRef = useRef(refs)
5
+ refsRef.current = refs
6
+
7
+ useLayoutEffect(() => {
8
+ if (typeof ResizeObserver === 'undefined') {
9
+ onResize()
10
+
11
+ return
12
+ }
13
+
14
+ const observer = new ResizeObserver(() => onResize())
15
+ let observed = false
16
+
17
+ for (const ref of refsRef.current) {
18
+ const element = ref.current
19
+
20
+ if (!element) {
21
+ continue
22
+ }
23
+
24
+ observer.observe(element)
25
+ observed = true
26
+ }
27
+
28
+ if (!observed) {
29
+ observer.disconnect()
30
+
31
+ return
32
+ }
33
+
34
+ onResize()
35
+
36
+ return () => observer.disconnect()
37
+ }, [onResize])
38
+ }
@@ -0,0 +1,68 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react'
2
+
3
+ import { uniqueCwds, type WorktreeResolver } from '@/app/chat/sidebar/workspace-groups'
4
+ import type { NasTechWorktreeInfo } from '@/global'
5
+ import type { SessionInfo } from '@/nastech'
6
+ import { desktopFsCacheKey, desktopWorktrees } from '@/lib/desktop-fs'
7
+
8
+ type WorktreeMap = Record<string, NasTechWorktreeInfo | null>
9
+
10
+ /**
11
+ * Probe the local filesystem for the git-worktree identity of each session cwd
12
+ * and return a resolver the grouping uses to build `parent → worktree`. Results
13
+ * are cached per cwd (and reset when the backend connection changes), so a probe
14
+ * runs once per directory. Unresolved cwds (probe pending, remote backend, or
15
+ * non-git dirs) fall back to the path-name heuristic in `workspaceTreeFor`.
16
+ */
17
+ export function useWorktreeInfo(sessions: SessionInfo[], enabled: boolean): WorktreeResolver {
18
+ const [map, setMap] = useState<WorktreeMap>({})
19
+ const cacheRef = useRef<{ data: WorktreeMap; key: string }>({ data: {}, key: '' })
20
+
21
+ useEffect(() => {
22
+ if (!enabled) {
23
+ return
24
+ }
25
+
26
+ const key = desktopFsCacheKey()
27
+
28
+ if (cacheRef.current.key !== key) {
29
+ cacheRef.current = { data: {}, key }
30
+ setMap({})
31
+ }
32
+
33
+ const missing = uniqueCwds(sessions).filter(cwd => !(cwd in cacheRef.current.data))
34
+
35
+ if (!missing.length) {
36
+ return
37
+ }
38
+
39
+ let cancelled = false
40
+
41
+ void desktopWorktrees(missing)
42
+ .then(result => {
43
+ if (cancelled) {
44
+ return
45
+ }
46
+
47
+ // Record every probed cwd (null when absent) so we never re-probe it.
48
+ const next: WorktreeMap = { ...cacheRef.current.data }
49
+
50
+ for (const cwd of missing) {
51
+ next[cwd] = result[cwd] ?? null
52
+ }
53
+
54
+ cacheRef.current = { data: next, key }
55
+ setMap(next)
56
+ })
57
+ .catch(() => {
58
+ // Bridge unavailable / probe failed — leave cwds unresolved so the
59
+ // heuristic fallback handles them.
60
+ })
61
+
62
+ return () => {
63
+ cancelled = true
64
+ }
65
+ }, [sessions, enabled])
66
+
67
+ return useMemo<WorktreeResolver>(() => (cwd: string) => map[cwd], [map])
68
+ }
@@ -0,0 +1,12 @@
1
+ import { en } from './en'
2
+ import { ja } from './ja'
3
+ import type { Locale, Translations } from './types'
4
+ import { zh } from './zh'
5
+ import { zhHant } from './zh-hant'
6
+
7
+ export const TRANSLATIONS: Record<Locale, Translations> = {
8
+ en,
9
+ zh,
10
+ 'zh-hant': zhHant,
11
+ ja
12
+ }