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,70 @@
1
+ "use strict"
2
+
3
+ // Build-time guard: refuse to hand a half-built renderer to electron-builder.
4
+ //
5
+ // `npm run pack` / `npm run dist*` are `npm run build && npm run builder`.
6
+ // If the `build` step (tsc -b && vite build) fails but packaging proceeds
7
+ // anyway — a stale checkout that fails typecheck, an interrupted vite build,
8
+ // or npm not short-circuiting `&&` in some shells — electron-builder happily
9
+ // packages an app with an empty or missing `dist/`. The result launches but
10
+ // blank-pages with `ERR_FILE_NOT_FOUND` for dist/index.html, with no clue why.
11
+ //
12
+ // This runs at the tail of `build`, after vite build, so any packaging path
13
+ // inherits it. It fails loud and early instead of shipping a broken bundle.
14
+ // See issues #39484 (renderer blank page) and #41327 / #39472 (dashboard 404).
15
+
16
+ const fs = require("fs")
17
+ const path = require("path")
18
+
19
+ // Pure check — returns { ok: true } or { ok: false, error: "..." }.
20
+ // Kept side-effect-free so it can be unit tested without spawning a process.
21
+ function checkDistBuilt(distDir) {
22
+ if (!fs.existsSync(distDir) || !fs.statSync(distDir).isDirectory()) {
23
+ return { ok: false, error: `no dist directory at ${distDir}` }
24
+ }
25
+
26
+ const indexHtml = path.join(distDir, "index.html")
27
+ if (!fs.existsSync(indexHtml) || !fs.statSync(indexHtml).isFile()) {
28
+ return { ok: false, error: `dist/index.html is missing at ${indexHtml}` }
29
+ }
30
+ if (fs.statSync(indexHtml).size === 0) {
31
+ return { ok: false, error: `dist/index.html is empty at ${indexHtml}` }
32
+ }
33
+
34
+ // index.html alone isn't enough — vite emits hashed JS into dist/assets.
35
+ // An index.html with no script bundle still blank-pages.
36
+ const assetsDir = path.join(distDir, "assets")
37
+ const hasAssets =
38
+ fs.existsSync(assetsDir) &&
39
+ fs.statSync(assetsDir).isDirectory() &&
40
+ fs.readdirSync(assetsDir).some(name => name.endsWith(".js"))
41
+ if (!hasAssets) {
42
+ return { ok: false, error: `dist/assets has no built JS bundle (expected vite output under ${assetsDir})` }
43
+ }
44
+
45
+ return { ok: true }
46
+ }
47
+
48
+ function main() {
49
+ const desktopRoot = path.resolve(__dirname, "..")
50
+ const distDir = path.join(desktopRoot, "dist")
51
+ const result = checkDistBuilt(distDir)
52
+
53
+ if (!result.ok) {
54
+ console.error(`\n✗ assert-dist-built: ${result.error}`)
55
+ console.error(" The renderer bundle is missing or incomplete, so packaging")
56
+ console.error(" would produce an app that launches to a blank page.")
57
+ console.error(" Re-run the build and check the tsc/vite output above for the")
58
+ console.error(" real failure, then package again:")
59
+ console.error(` cd ${desktopRoot} && npm run build\n`)
60
+ process.exit(1)
61
+ }
62
+
63
+ console.log("✓ assert-dist-built: dist/index.html + assets present")
64
+ }
65
+
66
+ if (require.main === module) {
67
+ main()
68
+ }
69
+
70
+ module.exports = { checkDistBuilt }
@@ -0,0 +1,84 @@
1
+ const assert = require('node:assert/strict')
2
+ const fs = require('node:fs')
3
+ const os = require('node:os')
4
+ const path = require('node:path')
5
+ const test = require('node:test')
6
+
7
+ const { checkDistBuilt } = require('../scripts/assert-dist-built.cjs')
8
+
9
+ function makeDist(extra) {
10
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nastech-assert-dist-'))
11
+ const distDir = path.join(tempRoot, 'dist')
12
+ fs.mkdirSync(distDir, { recursive: true })
13
+ if (extra) extra(distDir)
14
+ return { tempRoot, distDir }
15
+ }
16
+
17
+ test('checkDistBuilt passes when index.html + an assets JS bundle exist', () => {
18
+ const { tempRoot, distDir } = makeDist(d => {
19
+ fs.writeFileSync(path.join(d, 'index.html'), '<!doctype html><div id=root></div>', 'utf8')
20
+ fs.mkdirSync(path.join(d, 'assets'))
21
+ fs.writeFileSync(path.join(d, 'assets', 'index-abc123.js'), 'console.log(1)', 'utf8')
22
+ })
23
+ try {
24
+ assert.deepEqual(checkDistBuilt(distDir), { ok: true })
25
+ } finally {
26
+ fs.rmSync(tempRoot, { recursive: true, force: true })
27
+ }
28
+ })
29
+
30
+ test('checkDistBuilt fails when the dist directory is absent', () => {
31
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nastech-assert-dist-'))
32
+ try {
33
+ const result = checkDistBuilt(path.join(tempRoot, 'dist'))
34
+ assert.equal(result.ok, false)
35
+ assert.match(result.error, /no dist directory/)
36
+ } finally {
37
+ fs.rmSync(tempRoot, { recursive: true, force: true })
38
+ }
39
+ })
40
+
41
+ test('checkDistBuilt fails when index.html is missing', () => {
42
+ const { tempRoot, distDir } = makeDist(d => {
43
+ fs.mkdirSync(path.join(d, 'assets'))
44
+ fs.writeFileSync(path.join(d, 'assets', 'index-abc123.js'), 'console.log(1)', 'utf8')
45
+ })
46
+ try {
47
+ const result = checkDistBuilt(distDir)
48
+ assert.equal(result.ok, false)
49
+ assert.match(result.error, /index\.html is missing/)
50
+ } finally {
51
+ fs.rmSync(tempRoot, { recursive: true, force: true })
52
+ }
53
+ })
54
+
55
+ test('checkDistBuilt fails when index.html is empty', () => {
56
+ const { tempRoot, distDir } = makeDist(d => {
57
+ fs.writeFileSync(path.join(d, 'index.html'), '', 'utf8')
58
+ fs.mkdirSync(path.join(d, 'assets'))
59
+ fs.writeFileSync(path.join(d, 'assets', 'index-abc123.js'), 'console.log(1)', 'utf8')
60
+ })
61
+ try {
62
+ const result = checkDistBuilt(distDir)
63
+ assert.equal(result.ok, false)
64
+ assert.match(result.error, /index\.html is empty/)
65
+ } finally {
66
+ fs.rmSync(tempRoot, { recursive: true, force: true })
67
+ }
68
+ })
69
+
70
+ test('checkDistBuilt fails when assets/ has no JS bundle', () => {
71
+ const { tempRoot, distDir } = makeDist(d => {
72
+ fs.writeFileSync(path.join(d, 'index.html'), '<!doctype html>', 'utf8')
73
+ fs.mkdirSync(path.join(d, 'assets'))
74
+ // CSS only, no JS — still a blank page at runtime.
75
+ fs.writeFileSync(path.join(d, 'assets', 'index-abc123.css'), 'body{}', 'utf8')
76
+ })
77
+ try {
78
+ const result = checkDistBuilt(distDir)
79
+ assert.equal(result.ok, false)
80
+ assert.match(result.error, /no built JS bundle/)
81
+ } finally {
82
+ fs.rmSync(tempRoot, { recursive: true, force: true })
83
+ }
84
+ })
@@ -0,0 +1,13 @@
1
+ "use strict"
2
+
3
+ const fs = require("fs")
4
+ const path = require("path")
5
+
6
+ const root = path.resolve(__dirname, "..", "..", "..")
7
+
8
+ try {
9
+ fs.accessSync(path.join(root, "node_modules", "vite", "package.json"))
10
+ } catch {
11
+ console.error(`Run from repo root: cd ${root} && npm ci`)
12
+ process.exit(1)
13
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Desktop bundles ship precompiled renderer assets. Returning false here tells
3
+ * electron-builder to skip the node_modules collector/install step, which
4
+ * avoids workspace dependency graph explosions and keeps packaging
5
+ * deterministic across environments. The NasTech Agent Python payload is no
6
+ * longer bundled; the Electron app fetches it at first launch via
7
+ * `install.ps1`'s stage protocol (Windows). See `electron/main.cjs`.
8
+ */
9
+ module.exports = async function beforeBuild() {
10
+ return false
11
+ }
@@ -0,0 +1,78 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * before-pack.cjs — electron-builder beforePack hook.
5
+ *
6
+ * Removes any stale unpacked app directory (`appOutDir`) before
7
+ * electron-builder stages the Electron binaries into it.
8
+ *
9
+ * WHY THIS EXISTS
10
+ * ---------------
11
+ * electron-builder's final packaging step copies the stock `electron`
12
+ * binary into `release/<platform>-unpacked/` and then renames it to the
13
+ * product name (`NasTech`). If a PREVIOUS `npm run pack` was interrupted
14
+ * (Ctrl-C, OOM kill, crash, full disk) the unpacked directory is left in a
15
+ * corrupted partial state: it keeps the already-renamed `LICENSE.electron.txt`
16
+ * and the Chromium payload (.pak/.so/icudtl.dat/chrome-sandbox) but is MISSING
17
+ * the `electron` binary itself.
18
+ *
19
+ * On the next run, electron-builder sees the destination directory already
20
+ * populated, skips re-copying the binary it thinks is present, then tries to
21
+ * rename a `electron` file that no longer exists. The build dies with:
22
+ *
23
+ * ENOENT: no such file or directory, rename
24
+ * '.../release/linux-unpacked/electron' -> '.../release/linux-unpacked/NasTech'
25
+ *
26
+ * This is a hard failure with no obvious cause for the user — `nastech desktop`
27
+ * just prints "Desktop GUI build failed" and the only fix is to manually
28
+ * `rm -rf` the release directory, which a normal user has no way to know.
29
+ *
30
+ * The packaging step is not idempotent across an interrupted run, so we make
31
+ * it idempotent ourselves: wipe the target unpacked directory up front so
32
+ * electron-builder always stages into a clean tree. This is safe — the
33
+ * directory is a pure build artifact that electron-builder fully recreates
34
+ * on every pack; nothing else depends on its prior contents.
35
+ *
36
+ * Cross-platform: the same partial-state trap exists on macOS
37
+ * (the mac-unpacked NasTech.app bundle) and Windows (win-unpacked), so we
38
+ * clean whatever `appOutDir` electron-builder hands us regardless of platform.
39
+ *
40
+ * Best-effort: a cleanup failure must never mask the real build. We log and
41
+ * resolve rather than throw — worst case electron-builder hits the original
42
+ * ENOENT, which is no worse than not having this hook at all.
43
+ *
44
+ * electron-builder passes a context with:
45
+ * - appOutDir: the unpacked app directory about to be staged
46
+ * - electronPlatformName: 'win32' | 'darwin' | 'linux'
47
+ */
48
+
49
+ const fs = require('node:fs')
50
+
51
+ function cleanStaleAppOutDir(appOutDir) {
52
+ if (!appOutDir || typeof appOutDir !== 'string') {
53
+ return false
54
+ }
55
+ if (!fs.existsSync(appOutDir)) {
56
+ return false
57
+ }
58
+ // Recursive + force so a half-written tree (read-only bits, partial files)
59
+ // can't block the wipe. retry/maxRetries rides out transient EBUSY on
60
+ // Windows where an AV/indexer may briefly hold a handle.
61
+ fs.rmSync(appOutDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
62
+ return true
63
+ }
64
+
65
+ exports.cleanStaleAppOutDir = cleanStaleAppOutDir
66
+
67
+ exports.default = async function beforePack(context) {
68
+ const appOutDir = context && context.appOutDir
69
+ try {
70
+ if (cleanStaleAppOutDir(appOutDir)) {
71
+ console.log(`[before-pack] removed stale unpacked dir before staging: ${appOutDir}`)
72
+ }
73
+ } catch (err) {
74
+ // Never fail the build over cleanup; surface why so a genuinely stuck
75
+ // directory (permissions, mount) is still diagnosable.
76
+ console.warn(`[before-pack] could not clean ${appOutDir} (${err.message}); continuing`)
77
+ }
78
+ }
@@ -0,0 +1,53 @@
1
+ const assert = require('node:assert/strict')
2
+ const fs = require('node:fs')
3
+ const os = require('node:os')
4
+ const path = require('node:path')
5
+ const test = require('node:test')
6
+
7
+ const { cleanStaleAppOutDir } = require('../scripts/before-pack.cjs')
8
+
9
+ test('cleanStaleAppOutDir removes a populated unpacked directory', () => {
10
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nastech-before-pack-'))
11
+ try {
12
+ const appOutDir = path.join(tempRoot, 'linux-unpacked')
13
+ fs.mkdirSync(appOutDir, { recursive: true })
14
+ // Reproduce the corrupted partial state: license + payload present,
15
+ // electron binary missing — exactly what trips the ENOENT rename.
16
+ fs.writeFileSync(path.join(appOutDir, 'LICENSE.electron.txt'), 'x', 'utf8')
17
+ fs.writeFileSync(path.join(appOutDir, 'resources.pak'), 'x', 'utf8')
18
+ fs.mkdirSync(path.join(appOutDir, 'resources'), { recursive: true })
19
+ fs.writeFileSync(path.join(appOutDir, 'resources', 'app.asar'), 'x', 'utf8')
20
+
21
+ const removed = cleanStaleAppOutDir(appOutDir)
22
+
23
+ assert.equal(removed, true)
24
+ assert.equal(fs.existsSync(appOutDir), false)
25
+ } finally {
26
+ fs.rmSync(tempRoot, { recursive: true, force: true })
27
+ }
28
+ })
29
+
30
+ test('cleanStaleAppOutDir is a no-op when the directory is absent', () => {
31
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nastech-before-pack-'))
32
+ try {
33
+ const missing = path.join(tempRoot, 'does-not-exist')
34
+ assert.equal(cleanStaleAppOutDir(missing), false)
35
+ } finally {
36
+ fs.rmSync(tempRoot, { recursive: true, force: true })
37
+ }
38
+ })
39
+
40
+ test('cleanStaleAppOutDir ignores empty or invalid input', () => {
41
+ assert.equal(cleanStaleAppOutDir(''), false)
42
+ assert.equal(cleanStaleAppOutDir(undefined), false)
43
+ assert.equal(cleanStaleAppOutDir(null), false)
44
+ assert.equal(cleanStaleAppOutDir(42), false)
45
+ })
46
+
47
+ test('beforePack default export resolves even when cleanup throws', async () => {
48
+ const { default: beforePack } = require('../scripts/before-pack.cjs')
49
+ // A directory path that rmSync can't remove is simulated by passing a
50
+ // context whose appOutDir is a file the hook will try (and be allowed) to
51
+ // remove; the contract under test is that the hook never rejects.
52
+ await assert.doesNotReject(beforePack({ appOutDir: '', electronPlatformName: 'linux' }))
53
+ })
@@ -0,0 +1,51 @@
1
+ // Click on a session by partial title match.
2
+ const list = await (await fetch('http://127.0.0.1:9222/json/list')).json()
3
+ const tgt = list.find(t => t.type === 'page' && t.url.startsWith('http'))
4
+ const ws = new WebSocket(tgt.webSocketDebuggerUrl)
5
+ let id = 0
6
+ const pending = new Map()
7
+ ws.addEventListener('message', ev => {
8
+ const m = JSON.parse(ev.data)
9
+ if (m.id != null && pending.has(m.id)) {
10
+ pending.get(m.id)(m)
11
+ pending.delete(m.id)
12
+ }
13
+ })
14
+ await new Promise(r => ws.addEventListener('open', r))
15
+ const send = (method, params = {}) =>
16
+ new Promise(r => {
17
+ const i = ++id
18
+ pending.set(i, r)
19
+ ws.send(JSON.stringify({ id: i, method, params }))
20
+ })
21
+
22
+ const title = process.argv[2] || 'Phaser particle'
23
+ const r = await send('Runtime.evaluate', {
24
+ expression: `
25
+ (() => {
26
+ const titleMatch = ${JSON.stringify(title)}
27
+ const all = document.querySelectorAll('button, a, div[role="button"]')
28
+ const found = [...all].find(el => (el.textContent || '').includes(titleMatch))
29
+ if (!found) return JSON.stringify({ found: false, tried: titleMatch })
30
+ found.scrollIntoView()
31
+ found.click()
32
+ return JSON.stringify({ found: true, tag: found.tagName, text: (found.textContent || '').slice(0, 80) })
33
+ })()
34
+ `,
35
+ returnByValue: true
36
+ })
37
+ console.log('click raw:', JSON.stringify(r, null, 2))
38
+ await new Promise(r => setTimeout(r, 3000))
39
+
40
+ const status = await send('Runtime.evaluate', {
41
+ expression: `JSON.stringify({
42
+ url: location.href,
43
+ hasComposer: !!document.querySelector('[data-slot="composer-rich-input"]'),
44
+ threadMessages: document.querySelectorAll('[data-slot="aui_message"]').length,
45
+ bodyTextSnippet: document.body.innerText.slice(0, 500),
46
+ title: document.title
47
+ })`,
48
+ returnByValue: true
49
+ })
50
+ console.log('after click:', status.result.value)
51
+ ws.close()
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ // Launch the desktop renderer with HMR disabled so the React Fast Refresh
3
+ // preamble path is skipped. This sidesteps a current Vite 8 / plugin-react 6
4
+ // bug where the preamble script is not injected into index.html → renderer
5
+ // throws "$RefreshReg$ is not defined" on every TSX module → React tree
6
+ // never mounts.
7
+ //
8
+ // We're not trying to use HMR while profiling typing lag anyway. NasTech desktop
9
+ // boots, you type, profiler measures. HMR off is fine.
10
+ //
11
+ // Usage: node apps/desktop/scripts/dev-no-hmr.mjs
12
+ // (then in another shell, run electron --remote-debugging-port=9222 .)
13
+
14
+ import { createServer } from 'vite'
15
+
16
+ const server = await createServer({
17
+ configFile: new URL('../vite.config.ts', import.meta.url).pathname,
18
+ root: new URL('../', import.meta.url).pathname,
19
+ server: { hmr: false, host: '127.0.0.1', port: 5174, strictPort: true }
20
+ })
21
+ await server.listen()
22
+ server.printUrls()
@@ -0,0 +1,115 @@
1
+ // Wrap the thread scroller's properties and observe pin/scroll/RO events
2
+ // in real time during a submit, then print the timeline.
3
+ const list = await (await fetch('http://127.0.0.1:9222/json/list')).json()
4
+ const tgt = list.find(t => t.type === 'page' && t.url.startsWith('http'))
5
+ const ws = new WebSocket(tgt.webSocketDebuggerUrl)
6
+ let id = 0
7
+ const pending = new Map()
8
+ ws.addEventListener('message', ev => {
9
+ const m = JSON.parse(ev.data)
10
+ if (m.id != null && pending.has(m.id)) {
11
+ pending.get(m.id)(m)
12
+ pending.delete(m.id)
13
+ }
14
+ })
15
+ await new Promise(r => ws.addEventListener('open', r))
16
+ const send = (m, p = {}) =>
17
+ new Promise(r => {
18
+ const i = ++id
19
+ pending.set(i, r)
20
+ ws.send(JSON.stringify({ id: i, method: m, params: p }))
21
+ })
22
+ const evalP = async expr => {
23
+ const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true })
24
+ if (r.result?.exceptionDetails) throw new Error(r.result.exceptionDetails.text)
25
+ return r.result.result.value
26
+ }
27
+
28
+ await evalP(`(() => {
29
+ const v = document.querySelector('[data-slot="aui_thread-viewport"]')
30
+ if (v) v.scrollTop = v.scrollHeight
31
+ })()`)
32
+ await new Promise(r => setTimeout(r, 300))
33
+
34
+ await evalP(`(() => {
35
+ const el = document.querySelector('[data-slot="composer-rich-input"]')
36
+ el.focus()
37
+ const r = document.createRange(); r.selectNodeContents(el); r.collapse(false)
38
+ window.getSelection().removeAllRanges(); window.getSelection().addRange(r)
39
+ })()`)
40
+
41
+ const text = 'short follow-up'
42
+ for (const c of text) {
43
+ await send('Input.dispatchKeyEvent', { type: 'char', text: c, unmodifiedText: c })
44
+ await new Promise(r => setTimeout(r, 10))
45
+ }
46
+ await new Promise(r => setTimeout(r, 300))
47
+
48
+ // Hook into the viewport scrollTop setter + scroll + RO so we see every event
49
+ await evalP(`(() => {
50
+ const v = document.querySelector('[data-slot="aui_thread-viewport"]')
51
+ const events = []
52
+ window.__threadEvents = events
53
+ const t0 = performance.now()
54
+ const push = (kind, detail) => events.push({ t: performance.now() - t0, kind, ...detail })
55
+
56
+ // intercept scrollTop writes
57
+ const desc = Object.getOwnPropertyDescriptor(Element.prototype, 'scrollTop')
58
+ Object.defineProperty(v, 'scrollTop', {
59
+ get() { return desc.get.call(this) },
60
+ set(val) {
61
+ push('scrollTop=', { val, fromScrollHeight: this.scrollHeight, stackTop: (new Error()).stack.split('\\n').slice(2, 5).map(s => s.trim()).join(' | ') })
62
+ desc.set.call(this, val)
63
+ },
64
+ configurable: true
65
+ })
66
+
67
+ // scroll event
68
+ v.addEventListener('scroll', () => {
69
+ push('scroll', { scrollTop: v.scrollTop, scrollHeight: v.scrollHeight })
70
+ }, { passive: true, capture: true })
71
+
72
+ // RO on the viewport itself
73
+ const ro = new ResizeObserver((entries) => {
74
+ for (const e of entries) {
75
+ push('RO', { target: e.target.getAttribute('data-slot') || e.target.tagName, h: e.contentRect.height })
76
+ }
77
+ })
78
+ ro.observe(v)
79
+ if (v.firstElementChild) ro.observe(v.firstElementChild)
80
+
81
+ // mutationobserver on the viewport
82
+ const mo = new MutationObserver((muts) => {
83
+ push('mut', { count: muts.length, added: muts.reduce((s, m) => s + m.addedNodes.length, 0), removed: muts.reduce((s, m) => s + m.removedNodes.length, 0) })
84
+ })
85
+ mo.observe(v, { childList: true, subtree: true, characterData: true })
86
+
87
+ window.__teardown = () => { ro.disconnect(); mo.disconnect() }
88
+ return true
89
+ })()`)
90
+
91
+ // fire Enter
92
+ await send('Input.dispatchKeyEvent', {
93
+ type: 'rawKeyDown', windowsVirtualKeyCode: 13, key: 'Enter', code: 'Enter', text: '\r', unmodifiedText: '\r'
94
+ })
95
+ await send('Input.dispatchKeyEvent', { type: 'keyUp', windowsVirtualKeyCode: 13, key: 'Enter', code: 'Enter' })
96
+
97
+ await new Promise(r => setTimeout(r, 1200))
98
+
99
+ const events = JSON.parse(await evalP(`JSON.stringify(window.__threadEvents || [])`))
100
+ console.log(`\n${events.length} events:`)
101
+ for (const e of events) {
102
+ const t = String(e.t.toFixed(0)).padStart(5)
103
+ const { kind, t: _t, ...rest } = e
104
+ console.log(` ${t}ms ${kind.padEnd(12)} ${JSON.stringify(rest)}`)
105
+ }
106
+
107
+ await evalP(`window.__teardown?.()`)
108
+ // Cancel running agent
109
+ await evalP(`(() => {
110
+ for (const b of document.querySelectorAll('button')) {
111
+ if ((b.getAttribute('aria-label') || '').toLowerCase().includes('stop')) { b.click(); return 'stopped' }
112
+ }
113
+ })()`)
114
+
115
+ ws.close()