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,291 @@
1
+ import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui'
2
+ import * as React from 'react'
3
+
4
+ import { Codicon } from '@/components/ui/codicon'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ // Shared class tokens for edge-to-edge menus (use with `p-0` content): rows go
8
+ // full-width, square, and compact so the highlight spans the whole surface.
9
+ // Reuse these instead of re-deriving per menu so every searchable/compact menu
10
+ // reads identically.
11
+ export const dropdownMenuRow = 'gap-2 rounded-none px-2.5 py-1 text-xs'
12
+ export const dropdownMenuSectionLabel = 'px-2.5 pt-1 pb-0.5 text-[0.625rem] font-medium uppercase tracking-wide'
13
+
14
+ // Keys that must reach Radix's menu handler (navigation/close). Everything else
15
+ // is a filter keystroke and is stopped so the menu's typeahead doesn't hijack it.
16
+ const DROPDOWN_NAV_KEYS = new Set(['ArrowDown', 'ArrowUp', 'Enter', 'Escape', 'Tab'])
17
+
18
+ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
19
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
20
+ }
21
+
22
+ function DropdownMenuPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
23
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
24
+ }
25
+
26
+ function DropdownMenuTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
27
+ return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
28
+ }
29
+
30
+ /**
31
+ * Borderless filter input for a searchable dropdown. Autofocuses, keeps the
32
+ * menu's typeahead from eating keystrokes, and still lets arrow/enter/escape
33
+ * drive the list. Drop it in as the first child of a `DropdownMenuContent`.
34
+ */
35
+ function DropdownMenuSearch({
36
+ className,
37
+ onChange,
38
+ onKeyDown,
39
+ onValueChange,
40
+ ...props
41
+ }: Omit<React.ComponentProps<'input'>, 'type'> & {
42
+ onValueChange?: (value: string) => void
43
+ }) {
44
+ return (
45
+ <div className="px-2.5 py-1.5" data-slot="dropdown-menu-search">
46
+ <input
47
+ autoFocus
48
+ className={cn(
49
+ 'h-4 w-full bg-transparent text-xs leading-none text-foreground placeholder:text-(--ui-text-tertiary) focus:outline-none',
50
+ className
51
+ )}
52
+ onChange={event => {
53
+ onChange?.(event)
54
+ onValueChange?.(event.target.value)
55
+ }}
56
+ onKeyDown={event => {
57
+ if (!DROPDOWN_NAV_KEYS.has(event.key)) {
58
+ event.stopPropagation()
59
+ }
60
+
61
+ onKeyDown?.(event)
62
+ }}
63
+ type="text"
64
+ {...props}
65
+ />
66
+ </div>
67
+ )
68
+ }
69
+
70
+ function DropdownMenuContent({
71
+ className,
72
+ collisionPadding = 8,
73
+ sideOffset = 4,
74
+ ...props
75
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
76
+ return (
77
+ <DropdownMenuPrimitive.Portal>
78
+ <DropdownMenuPrimitive.Content
79
+ // `dt-portal-scrollbar` reproduces the thin themed scrollbar from
80
+ // `.scrollbar-dt` for portaled overlays (Radix renders this under
81
+ // document.body, outside #root's scope). See styles.css.
82
+ className={cn(
83
+ 'dt-portal-scrollbar z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-36 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-(--ui-stroke-secondary) bg-[color-mix(in_srgb,var(--ui-bg-elevated)_96%,transparent)] p-1 text-[length:var(--conversation-text-font-size)] text-popover-foreground shadow-md backdrop-blur-md data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
84
+ className
85
+ )}
86
+ // Keep the menu inside the viewport: Radix flips/shifts away from edges
87
+ // (avoidCollisions defaults on); the padding stops it kissing the edge.
88
+ collisionPadding={collisionPadding}
89
+ data-slot="dropdown-menu-content"
90
+ sideOffset={sideOffset}
91
+ {...props}
92
+ />
93
+ </DropdownMenuPrimitive.Portal>
94
+ )
95
+ }
96
+
97
+ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
98
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
99
+ }
100
+
101
+ function DropdownMenuItem({
102
+ className,
103
+ inset,
104
+ variant = 'default',
105
+ ...props
106
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
107
+ inset?: boolean
108
+ variant?: 'default' | 'destructive'
109
+ }) {
110
+ return (
111
+ <DropdownMenuPrimitive.Item
112
+ className={cn(
113
+ "relative flex items-center gap-2 rounded-md px-2 py-1 text-xs outline-hidden select-none focus:bg-(--ui-control-active-background) focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg:not([class*='text-'])]:text-(--ui-text-tertiary) data-[variant=destructive]:*:[svg]:text-destructive!",
114
+ className
115
+ )}
116
+ data-inset={inset}
117
+ data-slot="dropdown-menu-item"
118
+ data-variant={variant}
119
+ {...props}
120
+ />
121
+ )
122
+ }
123
+
124
+ function DropdownMenuCheckboxItem({
125
+ className,
126
+ children,
127
+ checked,
128
+ ...props
129
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
130
+ return (
131
+ <DropdownMenuPrimitive.CheckboxItem
132
+ checked={checked}
133
+ className={cn(
134
+ "relative flex items-center gap-2 rounded-md px-2 py-1 text-xs outline-hidden select-none focus:bg-(--ui-control-active-background) focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
135
+ className
136
+ )}
137
+ data-slot="dropdown-menu-checkbox-item"
138
+ {...props}
139
+ >
140
+ {children}
141
+ <DropdownMenuPrimitive.ItemIndicator className="ml-auto flex items-center pl-2 text-foreground">
142
+ <Codicon name="check" size="0.75rem" />
143
+ </DropdownMenuPrimitive.ItemIndicator>
144
+ </DropdownMenuPrimitive.CheckboxItem>
145
+ )
146
+ }
147
+
148
+ function DropdownMenuRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
149
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
150
+ }
151
+
152
+ function DropdownMenuRadioItem({
153
+ className,
154
+ children,
155
+ ...props
156
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
157
+ return (
158
+ <DropdownMenuPrimitive.RadioItem
159
+ className={cn(
160
+ "relative flex items-center gap-2 rounded-md px-2 py-1 text-xs outline-hidden select-none focus:bg-(--ui-control-active-background) focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
161
+ className
162
+ )}
163
+ data-slot="dropdown-menu-radio-item"
164
+ {...props}
165
+ >
166
+ {children}
167
+ <DropdownMenuPrimitive.ItemIndicator className="ml-auto flex items-center pl-2 text-foreground">
168
+ <Codicon name="check" size="0.75rem" />
169
+ </DropdownMenuPrimitive.ItemIndicator>
170
+ </DropdownMenuPrimitive.RadioItem>
171
+ )
172
+ }
173
+
174
+ function DropdownMenuLabel({
175
+ className,
176
+ inset,
177
+ ...props
178
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
179
+ inset?: boolean
180
+ }) {
181
+ return (
182
+ <DropdownMenuPrimitive.Label
183
+ className={cn('px-2 py-1 text-xs font-medium text-(--ui-text-tertiary) data-[inset]:pl-7', className)}
184
+ data-inset={inset}
185
+ data-slot="dropdown-menu-label"
186
+ {...props}
187
+ />
188
+ )
189
+ }
190
+
191
+ function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
192
+ return (
193
+ <DropdownMenuPrimitive.Separator
194
+ className={cn('-mx-1 my-1 h-px bg-(--ui-stroke-tertiary)', className)}
195
+ data-slot="dropdown-menu-separator"
196
+ {...props}
197
+ />
198
+ )
199
+ }
200
+
201
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
202
+ return (
203
+ <span
204
+ className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
205
+ data-slot="dropdown-menu-shortcut"
206
+ {...props}
207
+ />
208
+ )
209
+ }
210
+
211
+ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
212
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
213
+ }
214
+
215
+ function DropdownMenuSubTrigger({
216
+ className,
217
+ inset,
218
+ hideChevron = false,
219
+ children,
220
+ ...props
221
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
222
+ inset?: boolean
223
+ /** Suppress the trailing caret — for triggers that own their right-side affordance. */
224
+ hideChevron?: boolean
225
+ }) {
226
+ return (
227
+ <DropdownMenuPrimitive.SubTrigger
228
+ className={cn(
229
+ "flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-xs outline-hidden select-none focus:bg-(--ui-control-active-background) focus:text-foreground data-[inset]:pl-7 data-[state=open]:bg-(--ui-control-active-background) data-[state=open]:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg:not([class*='text-'])]:text-(--ui-text-tertiary)",
230
+ className
231
+ )}
232
+ data-inset={inset}
233
+ data-slot="dropdown-menu-sub-trigger"
234
+ {...props}
235
+ >
236
+ {children}
237
+ {!hideChevron && <Codicon className="ml-auto text-(--ui-text-tertiary)" name="chevron-right" size="1rem" />}
238
+ </DropdownMenuPrimitive.SubTrigger>
239
+ )
240
+ }
241
+
242
+ function DropdownMenuSubContent({
243
+ className,
244
+ collisionPadding = 8,
245
+ ...props
246
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
247
+ return (
248
+ // Portal the submenu out of the parent Content so it escapes that Content's
249
+ // `overflow` clip. Without this, a submenu opening from a scrollable menu
250
+ // gets visually cut off at the parent's edges. Radix Popper still anchors
251
+ // it to the SubTrigger and handles collision/flip, so portaling is safe.
252
+ <DropdownMenuPrimitive.Portal>
253
+ <DropdownMenuPrimitive.SubContent
254
+ // `dt-portal-scrollbar` reproduces the themed scrollbar for portaled
255
+ // overlays (rendered under document.body). Use a fixed `max-h-80`
256
+ // rather than the Radix available-height variable: that variable is
257
+ // only published on Content, NOT SubContent — using it here collapses
258
+ // the submenu to 0px height.
259
+ className={cn(
260
+ 'dt-portal-scrollbar z-50 max-h-80 min-w-36 origin-(--radix-dropdown-menu-content-transform-origin) overflow-y-auto rounded-lg border border-(--ui-stroke-secondary) bg-[color-mix(in_srgb,var(--ui-bg-elevated)_96%,transparent)] p-1 text-[length:var(--conversation-text-font-size)] text-popover-foreground shadow-md backdrop-blur-md data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
261
+ className
262
+ )}
263
+ // Flip to the other side / shift vertically when near a viewport edge
264
+ // (e.g. the status bar menu opening from the bottom-right corner) so
265
+ // the submenu never gets clipped.
266
+ collisionPadding={collisionPadding}
267
+ data-slot="dropdown-menu-sub-content"
268
+ {...props}
269
+ />
270
+ </DropdownMenuPrimitive.Portal>
271
+ )
272
+ }
273
+
274
+ export {
275
+ DropdownMenu,
276
+ DropdownMenuCheckboxItem,
277
+ DropdownMenuContent,
278
+ DropdownMenuGroup,
279
+ DropdownMenuItem,
280
+ DropdownMenuLabel,
281
+ DropdownMenuPortal,
282
+ DropdownMenuRadioGroup,
283
+ DropdownMenuRadioItem,
284
+ DropdownMenuSearch,
285
+ DropdownMenuSeparator,
286
+ DropdownMenuShortcut,
287
+ DropdownMenuSub,
288
+ DropdownMenuSubContent,
289
+ DropdownMenuSubTrigger,
290
+ DropdownMenuTrigger
291
+ }
@@ -0,0 +1,50 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import { Codicon } from '@/components/ui/codicon'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ // The single canonical error glyph (codicon's filled error mark). Use this
7
+ // everywhere an error is surfaced (boundaries, dialogs, banners) so failures
8
+ // read identically — one icon, one color, no background chip.
9
+ export function ErrorIcon({ className, size = '1.75rem' }: { className?: string; size?: string }) {
10
+ return <Codicon className={cn('text-destructive', className)} name="error" size={size} />
11
+ }
12
+
13
+ export interface ErrorStateProps {
14
+ /** Optional actions row/stack rendered below the copy. */
15
+ children?: ReactNode
16
+ className?: string
17
+ description?: ReactNode
18
+ /** Defaults to a destructive AlertCircle. */
19
+ icon?: ReactNode
20
+ title: ReactNode
21
+ }
22
+
23
+ // Shared, presentation-only error layout: the canonical ErrorIcon (no bg chip)
24
+ // over a centered title + body, with an optional actions stack. Used by the
25
+ // React error boundary, the in-dialog update error, and the boot-failure banner
26
+ // so every failure reads the same. Title/description accept nodes so Radix
27
+ // Dialog callers can pass DialogTitle/DialogDescription for accessibility.
28
+ export function ErrorState({ children, className, description, icon, title }: ErrorStateProps) {
29
+ return (
30
+ <div className={cn('grid gap-5', className)}>
31
+ <div className="flex flex-col items-center gap-3 text-center">
32
+ {icon ?? <ErrorIcon />}
33
+
34
+ {typeof title === 'string' ? (
35
+ <h2 className="text-center text-xl font-semibold tracking-tight">{title}</h2>
36
+ ) : (
37
+ title
38
+ )}
39
+
40
+ {typeof description === 'string' ? (
41
+ <p className="max-w-prose text-center text-sm leading-5 text-muted-foreground">{description}</p>
42
+ ) : (
43
+ description
44
+ )}
45
+ </div>
46
+
47
+ {children && <div className="grid gap-2">{children}</div>}
48
+ </div>
49
+ )
50
+ }
@@ -0,0 +1,110 @@
1
+ import type { ComponentProps, CSSProperties } from 'react'
2
+ import { memo, useCallback, useRef, useState } from 'react'
3
+
4
+ import { useResizeObserver } from '@/hooks/use-resize-observer'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ interface FadeTextProps extends Omit<ComponentProps<'span'>, 'children'> {
8
+ children: React.ReactNode
9
+ /**
10
+ * Width of the fade region on the trailing edge. Accepts any CSS length.
11
+ * Defaults to 3rem so long strings clearly trail off — short enough to
12
+ * preserve readable content, long enough to feel like a deliberate fade
13
+ * rather than a clipped ellipsis.
14
+ */
15
+ fadeWidth?: string
16
+ }
17
+
18
+ /**
19
+ * Single-line text that fades out instead of truncating with an ellipsis.
20
+ *
21
+ * Uses an inline mask-image so the fade resolves against whatever the parent
22
+ * background is — no need to know the surface color, no after-pseudo overlap.
23
+ * The mask is only applied when the text is actually overflowing, so short
24
+ * strings render as plain text without an unnecessary gradient on their tail.
25
+ *
26
+ * Layout reads (`el.scrollWidth`) are forced reflows. To avoid measuring
27
+ * once per parent re-render — which during streaming happens on every token —
28
+ * we only re-measure when the ResizeObserver fires (real size changes), not
29
+ * on every `children` reference change. Wrapped in `memo` with a custom
30
+ * comparator so scalar-string children skip re-render entirely when the text
31
+ * is unchanged but the parent re-rendered.
32
+ */
33
+ function FadeTextImpl({ children, className, fadeWidth = '3rem', style, ...rest }: FadeTextProps) {
34
+ const ref = useRef<HTMLSpanElement>(null)
35
+ const [overflowing, setOverflowing] = useState(false)
36
+
37
+ const measureOverflow = useCallback(() => {
38
+ const el = ref.current
39
+
40
+ if (!el) {
41
+ return
42
+ }
43
+
44
+ setOverflowing(el.scrollWidth - el.clientWidth > 1)
45
+ }, [])
46
+
47
+ useResizeObserver(measureOverflow, ref)
48
+
49
+ const maskStyle: CSSProperties = overflowing
50
+ ? {
51
+ maskImage: `linear-gradient(to right, black calc(100% - ${fadeWidth}), transparent)`,
52
+ WebkitMaskImage: `linear-gradient(to right, black calc(100% - ${fadeWidth}), transparent)`,
53
+ ...style
54
+ }
55
+ : (style ?? {})
56
+
57
+ return (
58
+ <span
59
+ {...rest}
60
+ className={cn('block min-w-0 max-w-full overflow-hidden whitespace-nowrap', className)}
61
+ ref={ref}
62
+ style={maskStyle}
63
+ >
64
+ {children}
65
+ </span>
66
+ )
67
+ }
68
+
69
+ function styleEqual(a: CSSProperties | undefined, b: CSSProperties | undefined) {
70
+ if (a === b) {
71
+ return true
72
+ }
73
+
74
+ if (!a || !b) {
75
+ return false
76
+ }
77
+
78
+ const aKeys = Object.keys(a)
79
+
80
+ if (aKeys.length !== Object.keys(b).length) {
81
+ return false
82
+ }
83
+
84
+ for (const k of aKeys) {
85
+ if ((a as Record<string, unknown>)[k] !== (b as Record<string, unknown>)[k]) {
86
+ return false
87
+ }
88
+ }
89
+
90
+ return true
91
+ }
92
+
93
+ export const FadeText = memo(FadeTextImpl, (prev, next) => {
94
+ if (prev.className !== next.className) {
95
+ return false
96
+ }
97
+
98
+ if (prev.fadeWidth !== next.fadeWidth) {
99
+ return false
100
+ }
101
+
102
+ if (!styleEqual(prev.style, next.style)) {
103
+ return false
104
+ }
105
+
106
+ // Cheap path: the common case is a scalar string/number child. Identity
107
+ // comparison is correct for any other element type (a new JSX node should
108
+ // force a re-render).
109
+ return prev.children === next.children
110
+ })
@@ -0,0 +1,63 @@
1
+ import { useEffect, useState } from 'react'
2
+ import spinners, { type BrailleSpinnerName as SpinnerName } from 'unicode-animations'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ export type { SpinnerName }
7
+
8
+ interface NormalisedSpinner {
9
+ frames: readonly string[]
10
+ interval: number
11
+ }
12
+
13
+ // Some spinners ship multi-character frames. Pull the first cell so each
14
+ // frame fits in one monospace box — matches how the TUI uses them.
15
+ const FRAMES_BY_NAME: Record<SpinnerName, NormalisedSpinner> = (() => {
16
+ const out = {} as Record<SpinnerName, NormalisedSpinner>
17
+
18
+ for (const name of Object.keys(spinners) as SpinnerName[]) {
19
+ const raw = spinners[name]
20
+
21
+ out[name] = {
22
+ frames: raw.frames.map(frame => [...frame][0] ?? '⠀'),
23
+ interval: raw.interval
24
+ }
25
+ }
26
+
27
+ return out
28
+ })()
29
+
30
+ interface GlyphSpinnerProps {
31
+ ariaLabel?: string
32
+ className?: string
33
+ spinner?: SpinnerName
34
+ }
35
+
36
+ /**
37
+ * One-char glyph spinner driven by `unicode-animations` (braille, orbit, scan,
38
+ * etc. — pick any `spinner` name). Mirrors the spinner used by the Ink TUI so
39
+ * the desktop and terminal experiences read the same visually. Renders inside
40
+ * an `inline-flex` cell with `leading-none` and `items-center` so it sits
41
+ * vertically centred inside its parent's line-box.
42
+ */
43
+ export function GlyphSpinner({ ariaLabel = 'Loading', className, spinner = 'braille' }: GlyphSpinnerProps) {
44
+ const spin = FRAMES_BY_NAME[spinner] ?? FRAMES_BY_NAME.braille!
45
+ const [frame, setFrame] = useState(0)
46
+
47
+ useEffect(() => {
48
+ setFrame(0)
49
+ const id = window.setInterval(() => setFrame(f => (f + 1) % spin.frames.length), spin.interval)
50
+
51
+ return () => window.clearInterval(id)
52
+ }, [spin])
53
+
54
+ return (
55
+ <span
56
+ aria-label={ariaLabel}
57
+ className={cn('inline-flex items-center justify-center font-mono leading-none tabular-nums', className)}
58
+ role="status"
59
+ >
60
+ {spin.frames[frame]}
61
+ </span>
62
+ )
63
+ }
@@ -0,0 +1,22 @@
1
+ import * as React from 'react'
2
+
3
+ import { cn } from '@/lib/utils'
4
+
5
+ import { type ControlVariantProps, controlVariants } from './control'
6
+
7
+ function Input({ className, type, size, ...props }: Omit<React.ComponentProps<'input'>, 'size'> & ControlVariantProps) {
8
+ return (
9
+ <input
10
+ className={cn(
11
+ controlVariants({ size }),
12
+ 'selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-xs file:font-medium file:text-foreground',
13
+ className
14
+ )}
15
+ data-slot="input"
16
+ type={type}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ export { Input }
@@ -0,0 +1,37 @@
1
+ import * as React from 'react'
2
+
3
+ import { cn } from '@/lib/utils'
4
+
5
+ function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
6
+ return (
7
+ <kbd
8
+ className={cn(
9
+ 'inline-grid h-4 min-w-4 place-items-center rounded-sm border border-border/70 bg-muted/45 px-1 font-mono text-[0.5625rem] font-medium leading-none text-muted-foreground shadow-xs',
10
+ className
11
+ )}
12
+ data-slot="kbd"
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ interface KbdGroupProps extends Omit<React.ComponentProps<'span'>, 'children'> {
19
+ keys: string[]
20
+ }
21
+
22
+ function KbdGroup({ className, keys, ...props }: KbdGroupProps) {
23
+ return (
24
+ <span
25
+ aria-label={keys.join(' ')}
26
+ className={cn('inline-flex shrink-0 items-center gap-0.5 opacity-55', className)}
27
+ data-slot="kbd-group"
28
+ {...props}
29
+ >
30
+ {keys.map(key => (
31
+ <Kbd key={key}>{key}</Kbd>
32
+ ))}
33
+ </span>
34
+ )
35
+ }
36
+
37
+ export { Kbd, KbdGroup }