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,25 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import { Check, Loader2 } from '@/lib/icons'
4
+
5
+ // idle → saving → done label+icon for action buttons (create / rename / delete…).
6
+ export function ActionStatus({
7
+ state,
8
+ idle,
9
+ busy,
10
+ done,
11
+ idleIcon = null
12
+ }: {
13
+ state: 'done' | 'idle' | 'saving'
14
+ idle: string
15
+ busy: string
16
+ done: string
17
+ idleIcon?: ReactNode
18
+ }) {
19
+ return (
20
+ <>
21
+ {state === 'saving' ? <Loader2 className="animate-spin" /> : state === 'done' ? <Check /> : idleIcon}
22
+ {state === 'saving' ? busy : state === 'done' ? done : idle}
23
+ </>
24
+ )
25
+ }
@@ -0,0 +1,53 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority'
2
+ import * as React from 'react'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ const alertVariants = cva(
7
+ 'relative grid w-full grid-cols-[auto_minmax(0,1fr)] items-start gap-x-3 gap-y-1 rounded-lg border bg-card px-4 py-3 text-sm text-card-foreground shadow-xs [&>svg]:mt-0.5 [&>svg]:size-4 [&>svg]:shrink-0',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'border-border',
12
+ destructive:
13
+ 'border-destructive/35 bg-[color-mix(in_srgb,var(--dt-card)_96%,var(--dt-destructive)_4%)] [&>svg]:text-destructive',
14
+ warning:
15
+ 'border-primary/30 bg-[color-mix(in_srgb,var(--dt-card)_96%,var(--dt-primary)_4%)] [&>svg]:text-primary',
16
+ success:
17
+ 'border-primary/25 bg-[color-mix(in_srgb,var(--dt-card)_97%,var(--dt-primary)_3%)] [&>svg]:text-primary'
18
+ }
19
+ },
20
+ defaultVariants: {
21
+ variant: 'default'
22
+ }
23
+ }
24
+ )
25
+
26
+ function Alert({ className, variant, ...props }: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
27
+ return <div className={cn(alertVariants({ variant }), className)} data-slot="alert" role="alert" {...props} />
28
+ }
29
+
30
+ function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
31
+ return (
32
+ <div
33
+ className={cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight text-foreground', className)}
34
+ data-slot="alert-title"
35
+ {...props}
36
+ />
37
+ )
38
+ }
39
+
40
+ function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) {
41
+ return (
42
+ <div
43
+ className={cn(
44
+ 'col-start-2 grid justify-items-start gap-1 text-muted-foreground [&_p]:leading-relaxed',
45
+ className
46
+ )}
47
+ data-slot="alert-description"
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ export { Alert, AlertDescription, AlertTitle }
@@ -0,0 +1,35 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority'
2
+ import { Slot } from 'radix-ui'
3
+ import type * as React from 'react'
4
+
5
+ import { cn } from '@/lib/utils'
6
+
7
+ // Small status/metadata tag. App radius (not a full pill); tones map to the
8
+ // shared accent/muted/destructive surfaces so badges read consistently.
9
+ const badgeVariants = cva(
10
+ 'inline-flex w-fit shrink-0 items-center gap-1 rounded-[3px] px-1.5 py-0.5 text-[0.65rem] font-medium leading-none whitespace-nowrap [&_svg]:size-3 [&_svg]:pointer-events-none',
11
+ {
12
+ variants: {
13
+ variant: {
14
+ default: 'bg-primary/10 text-primary',
15
+ muted: 'bg-muted text-muted-foreground',
16
+ warn: 'bg-amber-500/10 text-amber-600 dark:text-amber-300',
17
+ destructive: 'bg-destructive/10 text-destructive',
18
+ outline: 'border border-(--ui-stroke-secondary) text-muted-foreground'
19
+ }
20
+ },
21
+ defaultVariants: { variant: 'default' }
22
+ }
23
+ )
24
+
25
+ export interface BadgeProps extends React.ComponentProps<'span'>, VariantProps<typeof badgeVariants> {
26
+ asChild?: boolean
27
+ }
28
+
29
+ export function Badge({ asChild = false, className, variant, ...props }: BadgeProps) {
30
+ const Comp = asChild ? Slot.Root : 'span'
31
+
32
+ return <Comp className={cn(badgeVariants({ variant }), className)} data-slot="badge" {...props} />
33
+ }
34
+
35
+ export { badgeVariants }
@@ -0,0 +1,61 @@
1
+ import { useEffect, useState } from 'react'
2
+ import spinners, { type BrailleSpinnerName } from 'unicode-animations'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ interface NormalisedSpinner {
7
+ frames: readonly string[]
8
+ interval: number
9
+ }
10
+
11
+ // Some spinners ship multi-character frames. Pull the first cell so each
12
+ // frame fits in one monospace box — matches how the TUI uses them.
13
+ const FRAMES_BY_NAME: Record<BrailleSpinnerName, NormalisedSpinner> = (() => {
14
+ const out = {} as Record<BrailleSpinnerName, NormalisedSpinner>
15
+
16
+ for (const name of Object.keys(spinners) as BrailleSpinnerName[]) {
17
+ const raw = spinners[name]
18
+
19
+ out[name] = {
20
+ frames: raw.frames.map(frame => [...frame][0] ?? '⠀'),
21
+ interval: raw.interval
22
+ }
23
+ }
24
+
25
+ return out
26
+ })()
27
+
28
+ interface BrailleSpinnerProps {
29
+ ariaLabel?: string
30
+ className?: string
31
+ spinner?: BrailleSpinnerName
32
+ }
33
+
34
+ /**
35
+ * One-char braille spinner driven by `unicode-animations`. Mirrors the
36
+ * spinner used by the Ink TUI so the desktop and terminal experiences
37
+ * read the same visually. Renders inside an `inline-flex` cell with
38
+ * `leading-none` and `items-center` so it sits vertically centred inside
39
+ * its parent's line-box (e.g. the 1.1rem disclosure row).
40
+ */
41
+ export function BrailleSpinner({ ariaLabel = 'Loading', className, spinner = 'breathe' }: BrailleSpinnerProps) {
42
+ const spin = FRAMES_BY_NAME[spinner] ?? FRAMES_BY_NAME.breathe!
43
+ const [frame, setFrame] = useState(0)
44
+
45
+ useEffect(() => {
46
+ setFrame(0)
47
+ const id = window.setInterval(() => setFrame(f => (f + 1) % spin.frames.length), spin.interval)
48
+
49
+ return () => window.clearInterval(id)
50
+ }, [spin])
51
+
52
+ return (
53
+ <span
54
+ aria-label={ariaLabel}
55
+ className={cn('inline-flex items-center justify-center font-mono leading-none tabular-nums', className)}
56
+ role="status"
57
+ >
58
+ {spin.frames[frame]}
59
+ </span>
60
+ )
61
+ }
@@ -0,0 +1,81 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority'
2
+ import { Slot } from 'radix-ui'
3
+ import * as React from 'react'
4
+
5
+ import { cn } from '@/lib/utils'
6
+
7
+ // Text buttons are square (no radius) and sized by padding + line-height — no
8
+ // fixed heights — so they stay snug and scale with content. Only icon buttons
9
+ // (inherently square) carry the shared 4px radius.
10
+ const buttonVariants = cva(
11
+ "inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 rounded-[2.5px] text-xs leading-4 font-medium whitespace-nowrap shadow-none transition-all duration-100 outline-none focus-visible:border-ring focus-visible:ring-[0.1875rem] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-default disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
12
+ {
13
+ variants: {
14
+ variant: {
15
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
16
+ destructive:
17
+ 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40',
18
+ // Quiet action — transparent fill with a 1.5px inset ring (no layout-shifting border).
19
+ outline:
20
+ 'bg-transparent text-(--ui-text-primary) shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--ui-stroke-secondary)_50%,transparent)] hover:bg-(--chrome-action-hover) hover:text-(--ui-text-primary)',
21
+ // Soft-fill action (the default "non-primary button" look).
22
+ secondary:
23
+ 'bg-(--ui-bg-quaternary) text-(--ui-text-primary) hover:bg-(--chrome-action-hover) hover:text-(--ui-text-primary)',
24
+ ghost: 'text-(--ui-text-secondary) hover:bg-(--chrome-action-hover) hover:text-(--ui-text-primary)',
25
+ link: 'text-primary underline-offset-4 decoration-current/20 hover:underline',
26
+ // Boxless inline-text action (no bg/border). Quiet by default — reads as
27
+ // muted label text, underlines on hover (e.g. "Cancel", "Clear").
28
+ text: 'text-muted-foreground underline-offset-4 hover:text-foreground hover:underline',
29
+ // Emphasized inline-text action: bold + always-underlined link. Use for
30
+ // the actionable affordance in a row ("Change", "Set", "Open logs", …).
31
+ textStrong: 'font-semibold text-muted-foreground underline underline-offset-4 hover:text-foreground'
32
+ },
33
+ size: {
34
+ default: 'px-3 py-1.5 has-[>svg]:px-2.5',
35
+ xs: "gap-1 px-2 py-0.5 text-[0.6875rem] leading-4 has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
36
+ sm: 'px-2.5 py-1 has-[>svg]:px-2',
37
+ lg: 'px-5 py-2 text-sm leading-5 has-[>svg]:px-4',
38
+ // Flush inline text action — no box padding/height. Pair with text/link
39
+ // variants when the button must sit inline in a heading or sentence
40
+ // (replaces ad-hoc `h-auto px-0 py-0` overrides).
41
+ inline: 'h-auto gap-1 p-0 has-[>svg]:px-0',
42
+ icon: 'size-9 rounded-[4px]',
43
+ micro: "gap-0.5 px-1.5 py-0 text-[0.625rem] leading-4 has-[>svg]:px-1 [&_svg:not([class*='size-'])]:size-2.5",
44
+ 'icon-xs': "size-6 rounded-[4px] [&_svg:not([class*='size-'])]:size-3",
45
+ 'icon-sm': 'size-8 rounded-[4px]',
46
+ 'icon-lg': 'size-10 rounded-[4px]',
47
+ 'icon-titlebar':
48
+ 'h-(--titlebar-control-height) w-(--titlebar-control-size) rounded-[4px] [&_.codicon]:text-[0.875rem]'
49
+ }
50
+ },
51
+ defaultVariants: {
52
+ variant: 'default',
53
+ size: 'default'
54
+ }
55
+ }
56
+ )
57
+
58
+ function Button({
59
+ className,
60
+ variant = 'default',
61
+ size = 'default',
62
+ asChild = false,
63
+ ...props
64
+ }: React.ComponentProps<'button'> &
65
+ VariantProps<typeof buttonVariants> & {
66
+ asChild?: boolean
67
+ }) {
68
+ const Comp = asChild ? Slot.Root : 'button'
69
+
70
+ return (
71
+ <Comp
72
+ className={cn(buttonVariants({ variant, size }), className)}
73
+ data-size={size}
74
+ data-slot="button"
75
+ data-variant={variant}
76
+ {...props}
77
+ />
78
+ )
79
+ }
80
+
81
+ export { Button, buttonVariants }
@@ -0,0 +1,27 @@
1
+ import { Checkbox as CheckboxPrimitive } 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
+ function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
8
+ return (
9
+ <CheckboxPrimitive.Root
10
+ className={cn(
11
+ 'peer size-4 shrink-0 rounded-sm border border-input shadow-xs outline-none transition-shadow focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40',
12
+ className
13
+ )}
14
+ data-slot="checkbox"
15
+ {...props}
16
+ >
17
+ <CheckboxPrimitive.Indicator
18
+ className="flex items-center justify-center text-current"
19
+ data-slot="checkbox-indicator"
20
+ >
21
+ <Codicon name="check" size="0.875rem" />
22
+ </CheckboxPrimitive.Indicator>
23
+ </CheckboxPrimitive.Root>
24
+ )
25
+ }
26
+
27
+ export { Checkbox }
@@ -0,0 +1,20 @@
1
+ import type * as React from 'react'
2
+
3
+ import { cn } from '@/lib/utils'
4
+
5
+ export interface CodiconProps extends React.HTMLAttributes<HTMLElement> {
6
+ name: string
7
+ size?: number | string
8
+ spinning?: boolean
9
+ }
10
+
11
+ export function Codicon({ className, name, size, spinning, style, ...props }: CodiconProps) {
12
+ return (
13
+ <i
14
+ aria-hidden="true"
15
+ className={cn('codicon', `codicon-${name}`, spinning && 'codicon-modifier-spin', className)}
16
+ style={{ fontSize: size, ...style }}
17
+ {...props}
18
+ />
19
+ )
20
+ }
@@ -0,0 +1,111 @@
1
+ import { Command as CommandPrimitive } from 'cmdk'
2
+ import * as React from 'react'
3
+
4
+ import { SearchIcon } from '@/lib/icons'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {
8
+ return (
9
+ <CommandPrimitive
10
+ className={cn(
11
+ 'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',
12
+ className
13
+ )}
14
+ data-slot="command"
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ function CommandInput({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Input>) {
21
+ return (
22
+ <div className="flex h-11 items-center gap-2 border-b border-border px-3" data-slot="command-input-wrapper">
23
+ <SearchIcon className="size-4 shrink-0 text-muted-foreground" />
24
+ <CommandPrimitive.Input
25
+ className={cn(
26
+ 'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
27
+ className
28
+ )}
29
+ data-slot="command-input"
30
+ {...props}
31
+ />
32
+ </div>
33
+ )
34
+ }
35
+
36
+ function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
37
+ return (
38
+ <CommandPrimitive.List
39
+ className={cn('max-h-100 overflow-y-auto overflow-x-hidden', className)}
40
+ data-slot="command-list"
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
47
+ return (
48
+ <CommandPrimitive.Empty
49
+ className="py-6 text-center text-sm text-muted-foreground"
50
+ data-slot="command-empty"
51
+ {...props}
52
+ />
53
+ )
54
+ }
55
+
56
+ function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {
57
+ return (
58
+ <CommandPrimitive.Group
59
+ className={cn(
60
+ 'overflow-hidden p-1 text-foreground **:[[cmdk-group-heading]]:sticky **:[[cmdk-group-heading]]:top-0 **:[[cmdk-group-heading]]:z-10 **:[[cmdk-group-heading]]:bg-popover **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground',
61
+ className
62
+ )}
63
+ data-slot="command-group"
64
+ {...props}
65
+ />
66
+ )
67
+ }
68
+
69
+ function CommandSeparator({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
70
+ return (
71
+ <CommandPrimitive.Separator
72
+ className={cn('-mx-1 h-px bg-border', className)}
73
+ data-slot="command-separator"
74
+ {...props}
75
+ />
76
+ )
77
+ }
78
+
79
+ function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {
80
+ return (
81
+ <CommandPrimitive.Item
82
+ className={cn(
83
+ 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50',
84
+ className
85
+ )}
86
+ data-slot="command-item"
87
+ {...props}
88
+ />
89
+ )
90
+ }
91
+
92
+ function CommandShortcut({ className, ...props }: React.ComponentProps<'span'>) {
93
+ return (
94
+ <span
95
+ className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
96
+ data-slot="command-shortcut"
97
+ {...props}
98
+ />
99
+ )
100
+ }
101
+
102
+ export {
103
+ Command,
104
+ CommandEmpty,
105
+ CommandGroup,
106
+ CommandInput,
107
+ CommandItem,
108
+ CommandList,
109
+ CommandSeparator,
110
+ CommandShortcut
111
+ }
@@ -0,0 +1,109 @@
1
+ import type { ReactNode } from 'react'
2
+ import { useEffect, useState } from 'react'
3
+
4
+ import { ActionStatus } from '@/components/ui/action-status'
5
+ import { Button } from '@/components/ui/button'
6
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
7
+ import { useI18n } from '@/i18n'
8
+ import { AlertTriangle } from '@/lib/icons'
9
+
10
+ interface ConfirmDialogProps {
11
+ open: boolean
12
+ onClose: () => void
13
+ // Does the work. Throw to surface an inline error and keep the dialog open.
14
+ onConfirm: () => Promise<void> | void
15
+ title: ReactNode
16
+ description?: ReactNode
17
+ confirmLabel?: string
18
+ busyLabel?: string
19
+ doneLabel?: string
20
+ cancelLabel?: string
21
+ destructive?: boolean
22
+ }
23
+
24
+ // Shared confirmation dialog: Enter confirms (from anywhere in the dialog),
25
+ // Esc/Cancel/backdrop dismiss. Owns the pending → done → close beat and inline
26
+ // error, so callers pass only an async onConfirm that does the work.
27
+ export function ConfirmDialog({
28
+ open,
29
+ onClose,
30
+ onConfirm,
31
+ title,
32
+ description,
33
+ confirmLabel,
34
+ busyLabel,
35
+ doneLabel,
36
+ cancelLabel,
37
+ destructive = false
38
+ }: ConfirmDialogProps) {
39
+ const { t } = useI18n()
40
+ const [status, setStatus] = useState<'done' | 'idle' | 'saving'>('idle')
41
+ const [error, setError] = useState<null | string>(null)
42
+ const busy = status === 'saving' || status === 'done'
43
+ const resolvedConfirmLabel = confirmLabel ?? t.common.confirm
44
+ const resolvedBusyLabel = busyLabel ?? t.common.loading
45
+ const resolvedDoneLabel = doneLabel ?? t.common.done
46
+ const resolvedCancelLabel = cancelLabel ?? t.common.cancel
47
+
48
+ useEffect(() => {
49
+ if (open) {
50
+ setStatus('idle')
51
+ setError(null)
52
+ }
53
+ }, [open])
54
+
55
+ async function run() {
56
+ if (busy) {
57
+ return
58
+ }
59
+
60
+ setStatus('saving')
61
+ setError(null)
62
+
63
+ try {
64
+ await onConfirm()
65
+ setStatus('done')
66
+ window.setTimeout(onClose, 600)
67
+ } catch (err) {
68
+ setStatus('idle')
69
+ setError(err instanceof Error ? err.message : t.errors.genericFailure)
70
+ }
71
+ }
72
+
73
+ return (
74
+ <Dialog onOpenChange={value => !value && !busy && onClose()} open={open}>
75
+ <DialogContent
76
+ className="max-w-md"
77
+ onKeyDown={event => {
78
+ // Enter/Space confirm regardless of which button holds focus
79
+ // (preventDefault stops a focused Cancel from swallowing it).
80
+ if ((event.key === 'Enter' || event.key === ' ') && !busy) {
81
+ event.preventDefault()
82
+ void run()
83
+ }
84
+ }}
85
+ >
86
+ <DialogHeader>
87
+ <DialogTitle>{title}</DialogTitle>
88
+ {description ? <DialogDescription>{description}</DialogDescription> : null}
89
+ </DialogHeader>
90
+
91
+ {error && (
92
+ <div className="flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
93
+ <AlertTriangle className="mt-0.5 size-3.5 shrink-0" />
94
+ <span>{error}</span>
95
+ </div>
96
+ )}
97
+
98
+ <DialogFooter>
99
+ <Button disabled={busy} onClick={onClose} type="button" variant="ghost">
100
+ {resolvedCancelLabel}
101
+ </Button>
102
+ <Button disabled={busy} onClick={() => void run()} variant={destructive ? 'destructive' : 'default'}>
103
+ <ActionStatus busy={resolvedBusyLabel} done={resolvedDoneLabel} idle={resolvedConfirmLabel} state={status} />
104
+ </Button>
105
+ </DialogFooter>
106
+ </DialogContent>
107
+ </Dialog>
108
+ )
109
+ }
@@ -0,0 +1,141 @@
1
+ import { ContextMenu as ContextMenuPrimitive } 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
+ function ContextMenu({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
8
+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
9
+ }
10
+
11
+ function ContextMenuPortal({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
12
+ return <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
13
+ }
14
+
15
+ function ContextMenuTrigger({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
16
+ return <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
17
+ }
18
+
19
+ function ContextMenuGroup({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
20
+ return <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
21
+ }
22
+
23
+ function ContextMenuContent({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
24
+ return (
25
+ <ContextMenuPrimitive.Portal>
26
+ <ContextMenuPrimitive.Content
27
+ className={cn(
28
+ 'z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-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',
29
+ className
30
+ )}
31
+ data-slot="context-menu-content"
32
+ {...props}
33
+ />
34
+ </ContextMenuPrimitive.Portal>
35
+ )
36
+ }
37
+
38
+ function ContextMenuItem({
39
+ className,
40
+ inset,
41
+ variant = 'default',
42
+ ...props
43
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
44
+ inset?: boolean
45
+ variant?: 'default' | 'destructive'
46
+ }) {
47
+ return (
48
+ <ContextMenuPrimitive.Item
49
+ className={cn(
50
+ "relative flex cursor-default 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!",
51
+ className
52
+ )}
53
+ data-inset={inset}
54
+ data-slot="context-menu-item"
55
+ data-variant={variant}
56
+ {...props}
57
+ />
58
+ )
59
+ }
60
+
61
+ function ContextMenuLabel({
62
+ className,
63
+ inset,
64
+ ...props
65
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
66
+ inset?: boolean
67
+ }) {
68
+ return (
69
+ <ContextMenuPrimitive.Label
70
+ className={cn('px-2 py-1 text-xs font-medium text-(--ui-text-tertiary) data-[inset]:pl-7', className)}
71
+ data-inset={inset}
72
+ data-slot="context-menu-label"
73
+ {...props}
74
+ />
75
+ )
76
+ }
77
+
78
+ function ContextMenuSeparator({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
79
+ return (
80
+ <ContextMenuPrimitive.Separator
81
+ className={cn('-mx-1 my-1 h-px bg-(--ui-stroke-tertiary)', className)}
82
+ data-slot="context-menu-separator"
83
+ {...props}
84
+ />
85
+ )
86
+ }
87
+
88
+ function ContextMenuSub({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
89
+ return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
90
+ }
91
+
92
+ function ContextMenuSubTrigger({
93
+ className,
94
+ inset,
95
+ children,
96
+ ...props
97
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
98
+ inset?: boolean
99
+ }) {
100
+ return (
101
+ <ContextMenuPrimitive.SubTrigger
102
+ className={cn(
103
+ "flex cursor-default 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)",
104
+ className
105
+ )}
106
+ data-inset={inset}
107
+ data-slot="context-menu-sub-trigger"
108
+ {...props}
109
+ >
110
+ {children}
111
+ <Codicon className="ml-auto text-(--ui-text-tertiary)" name="chevron-right" size="1rem" />
112
+ </ContextMenuPrimitive.SubTrigger>
113
+ )
114
+ }
115
+
116
+ function ContextMenuSubContent({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
117
+ return (
118
+ <ContextMenuPrimitive.SubContent
119
+ className={cn(
120
+ 'z-50 min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-hidden 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',
121
+ className
122
+ )}
123
+ data-slot="context-menu-sub-content"
124
+ {...props}
125
+ />
126
+ )
127
+ }
128
+
129
+ export {
130
+ ContextMenu,
131
+ ContextMenuContent,
132
+ ContextMenuGroup,
133
+ ContextMenuItem,
134
+ ContextMenuLabel,
135
+ ContextMenuPortal,
136
+ ContextMenuSeparator,
137
+ ContextMenuSub,
138
+ ContextMenuSubContent,
139
+ ContextMenuSubTrigger,
140
+ ContextMenuTrigger
141
+ }