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,279 @@
1
+ import { type FC, useCallback, useEffect, useRef } from 'react'
2
+
3
+ import { useResizeObserver } from '@/hooks/use-resize-observer'
4
+ import { useI18n } from '@/i18n'
5
+
6
+ type Rgb = { r: number; g: number; b: number }
7
+
8
+ const RAMP = ' .,:;-=+*#%@'
9
+
10
+ const FALLBACKS = {
11
+ card: { r: 255, g: 255, b: 255 },
12
+ muted: { r: 240, g: 240, b: 239 },
13
+ foreground: { r: 36, g: 36, b: 36 },
14
+ primary: { r: 207, g: 128, b: 109 },
15
+ ring: { r: 185, g: 121, b: 105 }
16
+ } satisfies Record<string, Rgb>
17
+
18
+ const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value))
19
+
20
+ const smoothstep = (edge0: number, edge1: number, value: number) => {
21
+ const t = clamp((value - edge0) / (edge1 - edge0), 0, 1)
22
+
23
+ return t * t * (3 - 2 * t)
24
+ }
25
+
26
+ const parseColor = (value: string, fallback: Rgb): Rgb => {
27
+ const hex = value.trim().match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i)
28
+
29
+ if (hex) {
30
+ return {
31
+ r: Number.parseInt(hex[1], 16),
32
+ g: Number.parseInt(hex[2], 16),
33
+ b: Number.parseInt(hex[3], 16)
34
+ }
35
+ }
36
+
37
+ const rgb = value.trim().match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i)
38
+
39
+ return rgb ? { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]) } : fallback
40
+ }
41
+
42
+ const mix = (a: Rgb, b: Rgb, amount: number): Rgb => ({
43
+ r: Math.round(a.r + (b.r - a.r) * amount),
44
+ g: Math.round(a.g + (b.g - a.g) * amount),
45
+ b: Math.round(a.b + (b.b - a.b) * amount)
46
+ })
47
+
48
+ const rgba = ({ r, g, b }: Rgb, alpha: number) => `rgba(${r}, ${g}, ${b}, ${alpha})`
49
+
50
+ const hash2 = (x: number, y: number) => {
51
+ const n = Math.sin(x * 127.1 + y * 311.7) * 43758.5453
52
+
53
+ return n - Math.floor(n)
54
+ }
55
+
56
+ const noise2 = (x: number, y: number) => {
57
+ const xi = Math.floor(x)
58
+ const yi = Math.floor(y)
59
+ const xf = x - xi
60
+ const yf = y - yi
61
+ const u = xf * xf * (3 - 2 * xf)
62
+ const v = yf * yf * (3 - 2 * yf)
63
+ const a = hash2(xi, yi)
64
+ const b = hash2(xi + 1, yi)
65
+ const c = hash2(xi, yi + 1)
66
+ const d = hash2(xi + 1, yi + 1)
67
+
68
+ return a + (b - a) * u + (c - a) * v + (a - b - c + d) * u * v
69
+ }
70
+
71
+ const fbm = (x: number, y: number) => {
72
+ let value = 0
73
+ let amplitude = 0.5
74
+ let frequency = 1
75
+
76
+ for (let i = 0; i < 4; i += 1) {
77
+ value += amplitude * noise2(x * frequency, y * frequency)
78
+ frequency *= 2.04
79
+ amplitude *= 0.52
80
+ }
81
+
82
+ return value
83
+ }
84
+
85
+ const readTheme = () => {
86
+ const styles = getComputedStyle(document.documentElement)
87
+
88
+ return {
89
+ card: parseColor(styles.getPropertyValue('--dt-card'), FALLBACKS.card),
90
+ muted: parseColor(styles.getPropertyValue('--dt-muted'), FALLBACKS.muted),
91
+ foreground: parseColor(styles.getPropertyValue('--dt-foreground'), FALLBACKS.foreground),
92
+ primary: parseColor(styles.getPropertyValue('--dt-primary'), FALLBACKS.primary),
93
+ ring: parseColor(styles.getPropertyValue('--dt-ring'), FALLBACKS.ring)
94
+ }
95
+ }
96
+
97
+ const fitCanvas = (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) => {
98
+ const rect = canvas.getBoundingClientRect()
99
+ const dpr = Math.min(window.devicePixelRatio || 1, 2)
100
+ const width = Math.max(1, rect.width)
101
+ const height = Math.max(1, rect.height)
102
+
103
+ canvas.width = Math.round(width * dpr)
104
+ canvas.height = Math.round(height * dpr)
105
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
106
+
107
+ return { width, height }
108
+ }
109
+
110
+ const drawAsciiDiffusion = (ctx: CanvasRenderingContext2D, width: number, height: number, time: number) => {
111
+ const theme = readTheme()
112
+ const bg = ctx.createLinearGradient(0, 0, width, height)
113
+ bg.addColorStop(0, rgba(mix(theme.card, theme.primary, 0.08), 1))
114
+ bg.addColorStop(0.54, rgba(mix(theme.card, theme.muted, 0.68), 1))
115
+ bg.addColorStop(1, rgba(mix(theme.muted, theme.ring, 0.12), 1))
116
+ ctx.fillStyle = bg
117
+ ctx.fillRect(0, 0, width, height)
118
+
119
+ const cycle = (time * 0.028) % 1
120
+
121
+ const denoise = cycle < 0.82 ? smoothstep(0.02, 0.82, cycle) : 1 - smoothstep(0.82, 1, cycle)
122
+
123
+ const fontSize = clamp(width / 58, 8, 13)
124
+ const cellWidth = fontSize * 0.78
125
+ const cellHeight = fontSize * 1.28
126
+ const cols = Math.ceil(width / cellWidth)
127
+ const rows = Math.ceil(height / cellHeight)
128
+ const centerX = 0.53 + Math.sin(time * 0.055) * 0.02
129
+ const centerY = 0.5 + Math.cos(time * 0.048) * 0.02
130
+ const timestep = Math.floor(time * 1.15)
131
+ const timestepBlend = smoothstep(0, 1, time * 1.15 - timestep)
132
+
133
+ ctx.font = `${fontSize}px "SF Mono", "Cascadia Code", Menlo, Consolas, monospace`
134
+ ctx.textAlign = 'center'
135
+ ctx.textBaseline = 'middle'
136
+
137
+ for (let row = -1; row <= rows + 1; row += 1) {
138
+ for (let col = -1; col <= cols + 1; col += 1) {
139
+ const x = col * cellWidth + cellWidth * 0.5
140
+ const y = row * cellHeight + cellHeight * 0.5
141
+ const nx = x / width
142
+ const ny = y / height
143
+ const dx = (nx - centerX) * 1.2
144
+ const dy = (ny - centerY) * 0.95
145
+ const radius = Math.hypot(dx, dy)
146
+ const angle = Math.atan2(dy, dx)
147
+
148
+ const bloom =
149
+ Math.exp(-(radius * radius) / 0.075) * 0.72 +
150
+ Math.exp(-((radius - (0.28 + Math.sin(angle * 5 + time * 0.16) * 0.035)) ** 2) / 0.0028) * 0.8
151
+
152
+ const contour =
153
+ Math.exp(-((Math.sin(angle * 3 + radius * 17 - time * 0.17) * 0.5 + 0.5 - radius) ** 2) / 0.016) * 0.38
154
+
155
+ const stem = Math.exp(-((nx - centerX + 0.05) ** 2 / 0.004 + (ny - centerY - 0.25) ** 2 / 0.08)) * 0.46
156
+
157
+ const latent = clamp(bloom + contour + stem, 0, 1)
158
+ const staticA = hash2(col + timestep * 19, row - timestep * 11)
159
+
160
+ const staticB = hash2(col + (timestep + 1) * 19, row - (timestep + 1) * 11)
161
+
162
+ const staticNoise = staticA + (staticB - staticA) * timestepBlend
163
+ const livingNoise = fbm(col * 0.12 + time * 0.024, row * 0.12 - time * 0.018)
164
+ const denoiseWave = Math.exp(-((radius - denoise * 0.62) ** 2) / 0.006)
165
+
166
+ const signal = clamp(
167
+ staticNoise * (1 - denoise) +
168
+ latent * denoise +
169
+ (livingNoise - 0.45) * (0.45 - denoise * 0.26) +
170
+ denoiseWave * 0.3,
171
+ 0,
172
+ 1
173
+ )
174
+
175
+ const dropoutA = hash2(col - timestep * 7, row + timestep * 13)
176
+
177
+ const dropoutB = hash2(col - (timestep + 1) * 7, row + (timestep + 1) * 13)
178
+
179
+ const dropout = dropoutA + (dropoutB - dropoutA) * timestepBlend
180
+
181
+ if (dropout > 0.35 + signal * 0.68) {
182
+ continue
183
+ }
184
+
185
+ const glyph = RAMP[clamp(Math.floor(signal * (RAMP.length - 1)), 0, RAMP.length - 1)]
186
+
187
+ if (glyph === ' ') {
188
+ continue
189
+ }
190
+
191
+ const jitter = (1 - denoise) * 1.35 + (1 - latent) * 0.45
192
+ const jx = (noise2(col * 0.31, row * 0.31 + time * 0.09) - 0.5) * jitter
193
+ const jy = (noise2(col * 0.27 - time * 0.085, row * 0.27) - 0.5) * jitter
194
+ const tintAmount = clamp(latent * 0.7 + denoiseWave * 0.4, 0, 1)
195
+ const warm = mix(theme.primary, theme.ring, hash2(col, row))
196
+ const tint = mix(theme.foreground, warm, tintAmount)
197
+ const alpha = clamp(0.12 + signal * 0.68 + denoiseWave * 0.16, 0, 0.86)
198
+
199
+ if (signal > 0.58 && denoise > 0.34) {
200
+ ctx.fillStyle = rgba(theme.ring, alpha * 0.2)
201
+ ctx.fillText(glyph, x + jx + 0.75, y + jy - 0.45)
202
+ ctx.fillStyle = rgba(theme.primary, alpha * 0.18)
203
+ ctx.fillText(glyph, x + jx - 0.75, y + jy + 0.45)
204
+ }
205
+
206
+ ctx.fillStyle = rgba(tint, alpha)
207
+ ctx.fillText(glyph, x + jx, y + jy)
208
+ }
209
+ }
210
+
211
+ const veil = ctx.createRadialGradient(
212
+ width * centerX,
213
+ height * centerY,
214
+ 0,
215
+ width * centerX,
216
+ height * centerY,
217
+ Math.min(width, height) * (0.35 + denoise * 0.3)
218
+ )
219
+
220
+ veil.addColorStop(0, rgba(theme.card, 0.08 + denoise * 0.12))
221
+ veil.addColorStop(0.52, rgba(theme.card, 0.05))
222
+ veil.addColorStop(1, rgba(theme.card, 0))
223
+ ctx.fillStyle = veil
224
+ ctx.fillRect(0, 0, width, height)
225
+ }
226
+
227
+ const DiffusionCanvas: FC = () => {
228
+ const canvasRef = useRef<HTMLCanvasElement | null>(null)
229
+ const sizeRef = useRef({ width: 0, height: 0 })
230
+
231
+ const fitToContainer = useCallback(() => {
232
+ const canvas = canvasRef.current
233
+ const ctx = canvas?.getContext('2d')
234
+
235
+ if (!canvas || !ctx) {
236
+ return
237
+ }
238
+
239
+ sizeRef.current = fitCanvas(canvas, ctx)
240
+ }, [])
241
+
242
+ useResizeObserver(fitToContainer, canvasRef)
243
+
244
+ useEffect(() => {
245
+ const canvas = canvasRef.current
246
+ const ctx = canvas?.getContext('2d')
247
+
248
+ if (!canvas || !ctx) {
249
+ return
250
+ }
251
+
252
+ sizeRef.current = fitCanvas(canvas, ctx)
253
+
254
+ let frame = requestAnimationFrame(function draw(now) {
255
+ const { width, height } = sizeRef.current
256
+ ctx.clearRect(0, 0, width, height)
257
+ drawAsciiDiffusion(ctx, width, height, now / 1000)
258
+ frame = requestAnimationFrame(draw)
259
+ })
260
+
261
+ return () => {
262
+ cancelAnimationFrame(frame)
263
+ }
264
+ }, [])
265
+
266
+ return <canvas className="absolute inset-0 h-full w-full" ref={canvasRef} />
267
+ }
268
+
269
+ export const ImageGenerationPlaceholder: FC = () => {
270
+ const { t } = useI18n()
271
+
272
+ return (
273
+ <div aria-label={t.assistant.tool.renderingImage} aria-live="polite" className="w-full max-w-136 self-start" role="status">
274
+ <div className="relative h-(--image-preview-height) overflow-hidden rounded-4xl border border-border/55 shadow-[inset_0_0.0625rem_0_color-mix(in_srgb,white_45%,transparent),inset_0_0_0_0.0625rem_color-mix(in_srgb,var(--dt-border)_34%,transparent),inset_0_-0.75rem_1.75rem_color-mix(in_srgb,var(--dt-primary)_5%,transparent)]">
275
+ <DiffusionCanvas />
276
+ </div>
277
+ </div>
278
+ )
279
+ }
@@ -0,0 +1,75 @@
1
+ {"personality":"helpful","headline":"Ready when you are","body":"Ask me to open a repo, run tests, fix a bug, or draft a PR. I'll walk through the steps with you."}
2
+ {"personality":"helpful","headline":"How can I help today?","body":"Point me at a file, paste an error, or describe what you're building. I'll take it from there."}
3
+ {"personality":"helpful","headline":"Let's get started","body":"Try: review my diff, run the test suite, or explain this function. Ask anything about your code."}
4
+ {"personality":"helpful","headline":"Tell me what you need","body":"I can edit files, run commands, search the web, and walk you through tricky bugs. Just describe the task."}
5
+ {"personality":"helpful","headline":"Hi, NasTech here","body":"Share a repo path or a question to start. I keep replies clear and link back to the files I touch."}
6
+ {"personality":"concise","headline":"Ready.","body":"Describe the task. I'll do it."}
7
+ {"personality":"concise","headline":"Waiting for input","body":"Paste code, errors, or a goal. Short answers, fast edits."}
8
+ {"personality":"concise","headline":"Go.","body":"Ask. I'll read files, run tests, ship patches. No filler."}
9
+ {"personality":"concise","headline":"Standing by","body":"One line is enough. I'll expand only when it matters."}
10
+ {"personality":"concise","headline":"Your move","body":"Command, question, or file path. I handle the rest."}
11
+ {"personality":"technical","headline":"Shell mounted. Awaiting input.","body":"Provide repo path, failing test, or stack trace. Tools: fs, git, exec, search, patch, http."}
12
+ {"personality":"technical","headline":"Agent loop idle","body":"Send a prompt to trigger tool calls. Supports multi-file edits, test runs, git ops, and web fetches."}
13
+ {"personality":"technical","headline":"Ready for dispatch","body":"Enter task. I will plan, call tools, verify output. Logs stream inline; diffs returned pre-apply."}
14
+ {"personality":"technical","headline":"Stdin open","body":"Accepts natural language or structured commands. Typical flow: read -> plan -> patch -> test -> report."}
15
+ {"personality":"technical","headline":"Tools initialized","body":"filesystem, terminal, git, browser, search. Describe the change; I return diffs and test output."}
16
+ {"personality":"creative","headline":"A blank repo, a waiting cursor","body":"What shall we build? Paste an idea, a half-broken function, or a dream. I'll sketch it into shape."}
17
+ {"personality":"creative","headline":"Fresh canvas, warm compiler","body":"Give me a spark - a feature, a refactor, a wild prototype - and I'll turn it into code you can run."}
18
+ {"personality":"creative","headline":"Let's make something","body":"Describe the thing that doesn't exist yet. I'll pull tests, files, and APIs into a working draft."}
19
+ {"personality":"creative","headline":"New file, new possibilities","body":"Bring an intent, not a spec. We can prototype fast, refine later, and rewrite the world in the margins."}
20
+ {"personality":"creative","headline":"The muse is patched in","body":"Tell me what you're chasing. I'll remix examples, adapt snippets, and leave a tidy commit behind."}
21
+ {"personality":"teacher","headline":"Class is in session","body":"Ask about any file, concept, or error. I'll explain the why, not just the fix, and show a worked example."}
22
+ {"personality":"teacher","headline":"What shall we learn today?","body":"Paste code to review, a bug to debug, or a concept to unpack. I'll guide you step by step."}
23
+ {"personality":"teacher","headline":"Ready to walk you through it","body":"Share the problem. I'll break it into parts, explain each, and leave you able to solve the next one alone."}
24
+ {"personality":"teacher","headline":"Bring me a question","body":"We'll read the code together, find the root cause, and build a mental model you can reuse next time."}
25
+ {"personality":"teacher","headline":"Let's start with the basics","body":"Name the topic or paste the snippet. Expect explanations, diagrams in prose, and practice prompts."}
26
+ {"personality":"kawaii","headline":"hiii! ready to help! (^_^)","body":"paste a bug or a file path and i'll fix it super gently. tests, diffs, PRs - all with extra care! *sparkle*"}
27
+ {"personality":"kawaii","headline":"nastech-chan is here! <3","body":"tell me what you're making! i love refactors, tiny helpers, and big scary repos alike (>w<)"}
28
+ {"personality":"kawaii","headline":"let's code together!! :3","body":"drop an error, a goal, or a whole folder. i'll tidy it up with lots of love and a clean commit message!"}
29
+ {"personality":"kawaii","headline":"awaiting your wish~","body":"one task at a time, done neatly! i can run tests, patch files, and make your repo feel cozy again <3"}
30
+ {"personality":"kawaii","headline":"ready and happy! (>.<)","body":"say hi or paste a stack trace! no task too small, no repo too tangled. we'll untangle it together!"}
31
+ {"personality":"catgirl","headline":"nya~ what are we hacking on?","body":"paste a file, paw at a bug, or toss me a repo. i'll pounce on failing tests and leave clean diffs, nyan~"}
32
+ {"personality":"catgirl","headline":"*stretches* ready to code, nya","body":"describe the task. i'll patch, test, and purr over your PR. careful - i nip at unused imports!"}
33
+ {"personality":"catgirl","headline":"mrrp! new session opened","body":"give me a goal and i'll chase it through the codebase. reads, edits, runs - all with a twitchy tail."}
34
+ {"personality":"catgirl","headline":"tail up, claws sheathed","body":"paste an error or a plan. i debug like i hunt: quietly, thoroughly, with the occasional zoomie."}
35
+ {"personality":"catgirl","headline":"nyaaa~ nastech reporting","body":"say the word and i'll read your files, run your tests, and curl up in your branch with a tidy commit."}
36
+ {"personality":"pirate","headline":"Ahoy! Ready to sail the repo","body":"Name yer quarry - a bug, a feature, a cursed test - and I'll chase it down, matey. Diffs for plunder."}
37
+ {"personality":"pirate","headline":"NasTech at the helm, arrr","body":"Point me at the charts (the code) and I'll patch the hull, fire the cannons (tests), hoist a clean PR."}
38
+ {"personality":"pirate","headline":"What be the task, cap'n?","body":"Paste an error or a plan, ye scurvy dog. I'll navigate the stack trace and bring back treasure: green tests."}
39
+ {"personality":"pirate","headline":"Anchors aweigh, keyboard ready","body":"Tell me where X marks the spot. I read, edit, and commit with the discipline of a proper crew, arrr."}
40
+ {"personality":"pirate","headline":"Yo ho! Awaitin' orders","body":"Throw me a bug, a repo path, or a wild idea. I'll plunder the docs and return with workin' code."}
41
+ {"personality":"shakespeare","headline":"Pray, what task dost thou bring?","body":"Speak thy bug, thy file, thy weary test, and I shall mend it with a scholar's hand and honest diff."}
42
+ {"personality":"shakespeare","headline":"Hark! NasTech standeth ready","body":"Name the code that vexeth thee. I shall read, revise, and render a patch most fair and clean."}
43
+ {"personality":"shakespeare","headline":"What news from thy repository?","body":"Present thy stack trace or thy dream. I'll traverse files, run tests, and report in plainest verse."}
44
+ {"personality":"shakespeare","headline":"The stage is set, the cursor blinks","body":"Describe thy aim, good sir or madam. Thy branches shall be trimmed, thy bugs cast from the realm."}
45
+ {"personality":"shakespeare","headline":"Speak, and I shall act","body":"A line of intent sufficeth. I read, I edit, I commit - and leave thy history unblemished."}
46
+ {"personality":"surfer","headline":"Yo dude, what's the task?","body":"Drop a file, a bug, a gnarly stack trace - I'll ride it out. Clean diffs, green tests, no wipeouts."}
47
+ {"personality":"surfer","headline":"Waves lookin' clean, ready to code","body":"Paste your repo path or the bug that's bumming you out. We'll paddle in, fix it, paddle out. Easy."}
48
+ {"personality":"surfer","headline":"Hangin' ten at the prompt","body":"Tell me the vibe: feature, refactor, hotfix. I'll run tests, ship the patch, and keep it mellow, brah."}
49
+ {"personality":"surfer","headline":"Stoked to help, bro","body":"Big bug? Little typo? Whole rewrite? Just point. I handle the code; you chill with the rad commits."}
50
+ {"personality":"surfer","headline":"Tide's up, cursor's blinking","body":"Name the task and we're off. I read, edit, test, and leave a commit smoother than a dawn patrol."}
51
+ {"personality":"noir","headline":"Another repo, another rainy night","body":"Tell me what's broken. I'll read the files, dust for prints, and leave a diff on the desk by morning."}
52
+ {"personality":"noir","headline":"The cursor blinks. So do I.","body":"You've got a bug. I've got patience and a terminal. Name the case and I'll work it till it talks."}
53
+ {"personality":"noir","headline":"NasTech. Code investigator.","body":"Paste the stack trace, the suspect file, the alibi. I read between the lines and return with the truth."}
54
+ {"personality":"noir","headline":"Quiet night, open prompt","body":"Every bug leaves a trail. Give me the repo and a lead - I'll follow it, patch it, and close the file."}
55
+ {"personality":"noir","headline":"No case too small","body":"A typo, a segfault, a whole rotten architecture - hand me the keys. I'll bring back clean tests."}
56
+ {"personality":"uwu","headline":"uwu ready to hewp!","body":"paste a buggy fiwe or a goaw~ i'll wead, patch, and test, aww with tiny pawprints on the diff owo"}
57
+ {"personality":"uwu","headline":"nastech-san is wistening","body":"teww me the task, no matter how smoww~ i pwomise cwean commits and gentwe refactors, nyuu~"}
58
+ {"personality":"uwu","headline":"*tiny keyboard sounds*","body":"dwop yur ewwor message hewe! i'll find the cuwpwit, fix it, and weave a happy test suite behind me owo"}
59
+ {"personality":"uwu","headline":"wet's fix things togedda!","body":"give me a wepo path ow a buggo and i'll take cawe of it uwu. gwr at bad code, kind to yu~"}
60
+ {"personality":"uwu","headline":"awaiting yur command!","body":"i can wun tests, edit fiwes, and open pwease-wook PRs. just say da wowd, fwend uwu"}
61
+ {"personality":"philosopher","headline":"To code is to inquire. Ask.","body":"What problem sits before you? Describe it, and we shall examine its form, its cause, and its solution."}
62
+ {"personality":"philosopher","headline":"A blinking cursor, an open mind","body":"Every bug is a question in disguise. Share yours; I'll read, reason, and return an answer - and a patch."}
63
+ {"personality":"philosopher","headline":"Begin with a single question","body":"What do you wish to build, or to understand? I'll reason from first principles, edit, and verify with tests."}
64
+ {"personality":"philosopher","headline":"Consider the code, then speak","body":"Describe the end you seek. I pursue it through files, tests, and docs, and report what I found on the way."}
65
+ {"personality":"philosopher","headline":"The unexamined repo is not worth running","body":"Share a path, a puzzle, or a principle. I'll trace the logic, propose a change, and justify each edit."}
66
+ {"personality":"hype","headline":"LET'S GOOOO! READY TO SHIP!","body":"Paste that bug, that repo, that wild feature idea - I AM LOCKED IN. Clean diffs. Green tests. RIGHT NOW."}
67
+ {"personality":"hype","headline":"NASTECH ONLINE. LFG.","body":"Drop your task and watch me cook. Files read, tests run, PRs opened - we are NOT losing today, friend."}
68
+ {"personality":"hype","headline":"New session, infinite W's","body":"Bring the gnarliest bug you've got. I'll read, patch, test, commit like my life depends on it. LET'S GO."}
69
+ {"personality":"hype","headline":"ABSOLUTELY DIALED IN","body":"Describe the task. I'll blitz through files, crush failing tests, and leave a commit that SLAPS. Go go go."}
70
+ {"personality":"hype","headline":"Ready. So ready. Too ready.","body":"Tiny typo or huge refactor - doesn't matter. I'm shipping clean code today. Name the task and let's WORK."}
71
+ {"personality":"none","headline":"NasTech Agent is ready.","body":"Ask a question, paste an error, or point me at a repo. I can read code, run tools, and help you ship."}
72
+ {"personality":"none","headline":"What are we building today?","body":"Describe the task in your own words. I'll pick the right tools, explain my plan, and check in before risky steps."}
73
+ {"personality":"none","headline":"Start anywhere.","body":"Drop a file path, a traceback, or a rough idea. I'll investigate, suggest next steps, and keep things reversible."}
74
+ {"personality":"none","headline":"Your workspace, one prompt away.","body":"Search the repo, edit files, run tests, open PRs. Tell me the goal and I'll handle the mechanical parts."}
75
+ {"personality":"none","headline":"Ready when you are.","body":"Type a task, question, or snippet. I remember the session, cite my sources, and stop to ask when I'm unsure."}
@@ -0,0 +1,182 @@
1
+ import { type CSSProperties, useState } from 'react'
2
+
3
+ import introCopyJsonl from './intro-copy.jsonl?raw'
4
+
5
+ type IntroCopy = {
6
+ headline: string
7
+ body: string
8
+ }
9
+
10
+ type IntroCopyRecord = IntroCopy & {
11
+ personality: string
12
+ }
13
+
14
+ export type IntroProps = {
15
+ personality?: string
16
+ seed?: number
17
+ }
18
+
19
+ const NEUTRAL_PERSONALITIES = new Set(['', 'default', 'none', 'neutral'])
20
+
21
+ const FALLBACK_COPY: IntroCopy[] = [
22
+ {
23
+ headline: 'What are we moving today?',
24
+ body: "Send a bug, branch, plan, or rough idea. I'll inspect the repo and turn it into the next concrete step."
25
+ },
26
+ {
27
+ headline: "What's on your mind?",
28
+ body: "Bring the code, question, or stuck part. I'll read the room before making changes."
29
+ },
30
+ {
31
+ headline: 'What should NasTech look at?',
32
+ body: "Send the task, failing path, or half-formed plan. I'll help turn it into action."
33
+ },
34
+ {
35
+ headline: 'Where should we start?',
36
+ body: "Bring the problem, goal, or file. I'll inspect first and keep the next step concrete."
37
+ },
38
+ {
39
+ headline: 'What needs attention?',
40
+ body: "Send the context you have. I'll help sort it into a plan or a fix."
41
+ }
42
+ ]
43
+
44
+ function normalizeKey(value?: string): string {
45
+ return (value || '').trim().toLowerCase()
46
+ }
47
+
48
+ function titleize(value: string): string {
49
+ return value
50
+ .split(/[-_\s]+/)
51
+ .filter(Boolean)
52
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
53
+ .join(' ')
54
+ }
55
+
56
+ function isIntroCopyRecord(value: unknown): value is IntroCopyRecord {
57
+ if (!value || typeof value !== 'object') {
58
+ return false
59
+ }
60
+
61
+ const record = value as Record<string, unknown>
62
+
63
+ return (
64
+ typeof record.personality === 'string' &&
65
+ typeof record.headline === 'string' &&
66
+ typeof record.body === 'string' &&
67
+ Boolean(record.personality.trim()) &&
68
+ Boolean(record.headline.trim()) &&
69
+ Boolean(record.body.trim())
70
+ )
71
+ }
72
+
73
+ function parseIntroCopy(raw: string): Record<string, IntroCopy[]> {
74
+ const byPersonality: Record<string, IntroCopy[]> = {}
75
+
76
+ for (const line of raw.split(/\r?\n/)) {
77
+ const trimmed = line.trim()
78
+
79
+ if (!trimmed) {
80
+ continue
81
+ }
82
+
83
+ try {
84
+ const parsed: unknown = JSON.parse(trimmed)
85
+
86
+ if (!isIntroCopyRecord(parsed)) {
87
+ continue
88
+ }
89
+
90
+ const key = normalizeKey(parsed.personality)
91
+ byPersonality[key] ??= []
92
+ byPersonality[key].push({
93
+ headline: parsed.headline.trim(),
94
+ body: parsed.body.trim()
95
+ })
96
+ } catch {
97
+ // Bad generated copy should not break the whole desktop app.
98
+ }
99
+ }
100
+
101
+ return byPersonality
102
+ }
103
+
104
+ const INTRO_COPY_BY_PERSONALITY = parseIntroCopy(introCopyJsonl)
105
+
106
+ function neutralCopy(): IntroCopy[] {
107
+ return INTRO_COPY_BY_PERSONALITY.none || INTRO_COPY_BY_PERSONALITY.default || FALLBACK_COPY
108
+ }
109
+
110
+ function fallbackCopyForPersonality(personalityKey: string): IntroCopy[] {
111
+ if (NEUTRAL_PERSONALITIES.has(personalityKey)) {
112
+ return neutralCopy()
113
+ }
114
+
115
+ const label = titleize(personalityKey)
116
+
117
+ return [
118
+ {
119
+ headline: `${label} mode is on. What should we work on?`,
120
+ body: "Send the task, file, or rough idea. I'll use your configured voice and keep the work grounded in this repo."
121
+ },
122
+ {
123
+ headline: `What does ${label} NasTech need to see?`,
124
+ body: "Bring the context or the stuck part. I'll adapt to your configured personality."
125
+ },
126
+ {
127
+ headline: `${label} mode is ready.`,
128
+ body: "Send the problem, file, or idea. I'll follow the personality you've configured."
129
+ },
130
+ {
131
+ headline: `What should ${label} NasTech tackle?`,
132
+ body: "Drop the task here. I'll keep the work grounded in the repo."
133
+ },
134
+ {
135
+ headline: 'Where should we begin?',
136
+ body: `Give me the context and I'll answer in ${label} mode.`
137
+ }
138
+ ]
139
+ }
140
+
141
+ function pickCopy(copies: IntroCopy[], seed = 0): IntroCopy {
142
+ return copies[Math.abs(seed) % copies.length] || FALLBACK_COPY[0]
143
+ }
144
+
145
+ const WORDMARK = 'NASTECH AGENT'
146
+
147
+ function resolveCopy(personality?: string, seed?: number): IntroCopy {
148
+ const personalityKey = normalizeKey(personality)
149
+
150
+ const copies = NEUTRAL_PERSONALITIES.has(personalityKey)
151
+ ? INTRO_COPY_BY_PERSONALITY[personalityKey] || neutralCopy()
152
+ : INTRO_COPY_BY_PERSONALITY[personalityKey] || fallbackCopyForPersonality(personalityKey)
153
+
154
+ return pickCopy(copies, seed)
155
+ }
156
+
157
+ export function Intro({ personality, seed }: IntroProps) {
158
+ const [mountSeed] = useState(() => Math.floor(Math.random() * 100000))
159
+ const copy = resolveCopy(personality, mountSeed + (seed ?? 0))
160
+
161
+ return (
162
+ <div
163
+ className="pointer-events-none flex w-full min-w-0 flex-col items-center justify-center px-3 py-6 text-center text-muted-foreground sm:px-6 lg:px-8"
164
+ data-slot="aui_intro"
165
+ >
166
+ <div className="w-full min-w-0">
167
+ <p
168
+ aria-label={WORDMARK}
169
+ className="fit-text mx-auto mb-3 w-[88%] font-['Collapse'] font-bold uppercase leading-[0.9] tracking-[0.08em] text-midground mix-blend-plus-lighter dark:text-foreground/90"
170
+ style={{ '--fit-text-line-height': '0.9', '--fit-text-min': '2.75rem' } as CSSProperties}
171
+ >
172
+ <span>
173
+ <span>{WORDMARK}</span>
174
+ </span>
175
+ <span aria-hidden="true">{WORDMARK}</span>
176
+ </p>
177
+
178
+ <p className="m-0 text-center leading-normal tracking-tight">{copy.body}</p>
179
+ </div>
180
+ </div>
181
+ )
182
+ }
@@ -0,0 +1,125 @@
1
+ import { useStore } from '@nanostores/react'
2
+ import { useEffect, useRef, useState } from 'react'
3
+
4
+ import { useI18n } from '@/i18n'
5
+ import { MonitorPlay } from '@/lib/icons'
6
+ import { normalizeOrLocalPreviewTarget } from '@/lib/local-preview'
7
+ import { previewName } from '@/lib/preview-targets'
8
+ import { notifyError } from '@/store/notifications'
9
+ import {
10
+ $previewTarget,
11
+ dismissPreviewTarget,
12
+ type PreviewRecordSource,
13
+ setCurrentSessionPreviewTarget
14
+ } from '@/store/preview'
15
+ import { $currentCwd } from '@/store/session'
16
+
17
+ export function PreviewAttachment({ source = 'manual', target }: { source?: PreviewRecordSource; target: string }) {
18
+ const { t } = useI18n()
19
+ const cwd = useStore($currentCwd)
20
+ const activePreview = useStore($previewTarget)
21
+ const [opening, setOpening] = useState(false)
22
+ const activePreviewRef = useRef(activePreview)
23
+ const cwdRef = useRef(cwd)
24
+ const mountedRef = useRef(false)
25
+ const requestTokenRef = useRef(0)
26
+ const targetRef = useRef(target)
27
+ const name = previewName(target)
28
+ const isActive = activePreview?.source === target
29
+
30
+ activePreviewRef.current = activePreview
31
+ cwdRef.current = cwd
32
+ targetRef.current = target
33
+
34
+ useEffect(() => {
35
+ mountedRef.current = true
36
+
37
+ return () => {
38
+ mountedRef.current = false
39
+ requestTokenRef.current += 1
40
+ }
41
+ }, [])
42
+
43
+ useEffect(() => {
44
+ requestTokenRef.current += 1
45
+ setOpening(false)
46
+ }, [cwd, target])
47
+
48
+ async function togglePreview() {
49
+ if (opening) {
50
+ return
51
+ }
52
+
53
+ if (isActive) {
54
+ dismissPreviewTarget()
55
+
56
+ return
57
+ }
58
+
59
+ const requestToken = ++requestTokenRef.current
60
+ const requestTarget = target
61
+ const requestCwd = cwd
62
+
63
+ setOpening(true)
64
+
65
+ try {
66
+ const preview = await normalizeOrLocalPreviewTarget(requestTarget, requestCwd || undefined)
67
+
68
+ if (
69
+ !mountedRef.current ||
70
+ requestTokenRef.current !== requestToken ||
71
+ targetRef.current !== requestTarget ||
72
+ cwdRef.current !== requestCwd
73
+ ) {
74
+ return
75
+ }
76
+
77
+ if (!preview) {
78
+ throw new Error(`Could not open preview target: ${requestTarget}`)
79
+ }
80
+
81
+ const currentPreview = activePreviewRef.current
82
+
83
+ if (currentPreview?.source === preview.source && currentPreview.url === preview.url) {
84
+ return
85
+ }
86
+
87
+ setCurrentSessionPreviewTarget(preview, source, requestTarget)
88
+ } catch (error) {
89
+ if (
90
+ !mountedRef.current ||
91
+ requestTokenRef.current !== requestToken ||
92
+ targetRef.current !== requestTarget ||
93
+ cwdRef.current !== requestCwd
94
+ ) {
95
+ return
96
+ }
97
+
98
+ notifyError(error, t.preview.unavailable)
99
+ } finally {
100
+ if (mountedRef.current && requestTokenRef.current === requestToken) {
101
+ setOpening(false)
102
+ }
103
+ }
104
+ }
105
+
106
+ return (
107
+ <div className="flex w-full max-w-160 flex-wrap items-center gap-2.5 rounded-lg border border-border/55 bg-card/55 px-2.5 py-1.5 text-sm">
108
+ <span className="grid size-7 shrink-0 place-items-center rounded-md bg-muted/55 text-muted-foreground/85">
109
+ <MonitorPlay className="size-3.5" />
110
+ </span>
111
+ <div className="min-w-0 flex-1">
112
+ <div className="truncate text-[0.78rem] font-medium leading-[1.15rem] text-foreground/90">{name}</div>
113
+ <div className="truncate font-mono text-[0.66rem] leading-4 text-muted-foreground/70">{target}</div>
114
+ </div>
115
+ <button
116
+ className="ml-auto shrink-0 rounded-md border border-border/55 bg-background/40 px-2 py-1 text-[0.7rem] font-medium text-muted-foreground transition-colors hover:bg-accent/55 hover:text-foreground disabled:opacity-50 max-[28rem]:ml-9 max-[28rem]:w-[calc(100%-2.25rem)]"
117
+ disabled={opening}
118
+ onClick={() => void togglePreview()}
119
+ type="button"
120
+ >
121
+ {opening ? t.preview.opening : isActive ? t.preview.hide : t.preview.openPreview}
122
+ </button>
123
+ </div>
124
+ )
125
+ }