khal-os 1.260324.2

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 (408) hide show
  1. package/.env.example +23 -0
  2. package/.genie/mailbox/cli-sent.jsonl +3 -0
  3. package/.genie/mailbox/ds1-wave2-engineer-1.json +15 -0
  4. package/.genie/mailbox/ds1-wave2-engineer-2.json +15 -0
  5. package/.genie/mailbox/ds1-wave2-engineer-3.json +15 -0
  6. package/.genie/state/os-observability.json +39 -0
  7. package/.genie/state/tmux-control-mode-terminal.json +28 -0
  8. package/.genie/wishes/genieos-one-theme/WISH.md +417 -0
  9. package/.genie/wishes/workos-prod-rbac/WISH.md +345 -0
  10. package/.github/workflows/ci.yml +39 -0
  11. package/.github/workflows/release.yml +78 -0
  12. package/.github/workflows/version.yml +122 -0
  13. package/.husky/pre-commit +1 -0
  14. package/.pnpm-approve-builds.json +1 -0
  15. package/CLAUDE.md +117 -0
  16. package/LICENSE +21 -0
  17. package/README.md +38 -0
  18. package/biome.json +124 -0
  19. package/bun.lock +1249 -0
  20. package/docs/workos-setup.md +116 -0
  21. package/ecosystem.config.cjs +26 -0
  22. package/instrumentation.ts +8 -0
  23. package/knip.json +35 -0
  24. package/nats.conf +7 -0
  25. package/next.config.ts +25 -0
  26. package/package.json +78 -0
  27. package/packages/dev3000-app/components.ts +12 -0
  28. package/packages/dev3000-app/manifest.ts +19 -0
  29. package/packages/dev3000-app/package.json +23 -0
  30. package/packages/dev3000-app/views/dev3000/Dev3000App.tsx +758 -0
  31. package/packages/dev3000-app/views/dev3000/ErrorsPanel.tsx +160 -0
  32. package/packages/dev3000-app/views/dev3000/dev3000-context.tsx +21 -0
  33. package/packages/dev3000-app/views/dev3000/index.ts +4 -0
  34. package/packages/dev3000-app/views/dev3000/schema.ts +55 -0
  35. package/packages/dev3000-app/views/dev3000/service/index.ts +358 -0
  36. package/packages/dev3000-app/views/dev3000/service/runtime +1 -0
  37. package/packages/dev3000-app/views/dev3000/subjects.ts +9 -0
  38. package/packages/dev3000-app/views/dev3000/types.ts +77 -0
  39. package/packages/files-app/components.ts +12 -0
  40. package/packages/files-app/manifest.ts +19 -0
  41. package/packages/files-app/package.json +23 -0
  42. package/packages/files-app/views/files/ContextMenu.tsx +151 -0
  43. package/packages/files-app/views/files/DeleteConfirmDialog.tsx +39 -0
  44. package/packages/files-app/views/files/FileItem.tsx +128 -0
  45. package/packages/files-app/views/files/FilesApp.tsx +509 -0
  46. package/packages/files-app/views/files/FilesListView.tsx +201 -0
  47. package/packages/files-app/views/files/FilesToolbar.tsx +117 -0
  48. package/packages/files-app/views/files/GridView.tsx +90 -0
  49. package/packages/files-app/views/files/InlineInput.tsx +131 -0
  50. package/packages/files-app/views/files/UploadOverlay.tsx +61 -0
  51. package/packages/files-app/views/files/schema.ts +49 -0
  52. package/packages/files-app/views/files/service/index.ts +184 -0
  53. package/packages/files-app/views/files/service/runtime +1 -0
  54. package/packages/files-app/views/files/use-files.ts +201 -0
  55. package/packages/files-app/views/files/use-upload.ts +105 -0
  56. package/packages/genie-app/components.ts +12 -0
  57. package/packages/genie-app/lib/subjects.ts +87 -0
  58. package/packages/genie-app/manifest.ts +19 -0
  59. package/packages/genie-app/package.json +29 -0
  60. package/packages/genie-app/views/genie/service/agent-lifecycle.ts +136 -0
  61. package/packages/genie-app/views/genie/service/cli.ts +114 -0
  62. package/packages/genie-app/views/genie/service/comms.ts +141 -0
  63. package/packages/genie-app/views/genie/service/directory.ts +167 -0
  64. package/packages/genie-app/views/genie/service/index.ts +219 -0
  65. package/packages/genie-app/views/genie/service/system.ts +123 -0
  66. package/packages/genie-app/views/genie/service/teams.ts +191 -0
  67. package/packages/genie-app/views/genie/service/terminal-proxy.ts +184 -0
  68. package/packages/genie-app/views/genie/service/tmux-control.ts +318 -0
  69. package/packages/genie-app/views/genie/service/wishes.ts +270 -0
  70. package/packages/genie-app/views/genie/ui/GenieApp.tsx +5 -0
  71. package/packages/genie-app/views/genie/ui/PaneCard.tsx +307 -0
  72. package/packages/genie-app/views/genie/ui/Sidebar.tsx +212 -0
  73. package/packages/genie-app/views/genie/ui/TabBar.tsx +70 -0
  74. package/packages/genie-app/views/genie/ui/WorkspaceCanvas.tsx +343 -0
  75. package/packages/genie-app/views/genie/ui/XTermPane.tsx +306 -0
  76. package/packages/genie-app/views/genie/ui/hooks/useNatsAction.ts +54 -0
  77. package/packages/genie-app/views/genie/ui/hooks/useNatsRequest.ts +68 -0
  78. package/packages/genie-app/views/genie/ui/panels/AgentsPanel.tsx +399 -0
  79. package/packages/genie-app/views/genie/ui/panels/ChatPanel.tsx +351 -0
  80. package/packages/genie-app/views/genie/ui/panels/SystemPanel.tsx +195 -0
  81. package/packages/genie-app/views/genie/ui/panels/TeamsPanel.tsx +560 -0
  82. package/packages/genie-app/views/genie/ui/panels/WishesPanel.tsx +424 -0
  83. package/packages/nats-viewer-app/components.ts +12 -0
  84. package/packages/nats-viewer-app/manifest.ts +18 -0
  85. package/packages/nats-viewer-app/package.json +14 -0
  86. package/packages/nats-viewer-app/views/nats-viewer/ActiveSubs.tsx +34 -0
  87. package/packages/nats-viewer-app/views/nats-viewer/MessageLog.tsx +247 -0
  88. package/packages/nats-viewer-app/views/nats-viewer/NatsViewer.tsx +209 -0
  89. package/packages/nats-viewer-app/views/nats-viewer/PublishPanel.tsx +111 -0
  90. package/packages/nats-viewer-app/views/nats-viewer/RequestPanel.tsx +165 -0
  91. package/packages/nats-viewer-app/views/nats-viewer/Sidebar.tsx +59 -0
  92. package/packages/nats-viewer-app/views/nats-viewer/SubjectCatalog.tsx +63 -0
  93. package/packages/nats-viewer-app/views/nats-viewer/SubscribeInput.tsx +59 -0
  94. package/packages/nats-viewer-app/views/nats-viewer/index.ts +5 -0
  95. package/packages/nats-viewer-app/views/nats-viewer/nats-viewer-context.tsx +31 -0
  96. package/packages/nats-viewer-app/views/nats-viewer/types.ts +7 -0
  97. package/packages/nats-viewer-app/views/nats-viewer/use-message-buffer.ts +55 -0
  98. package/packages/os-cli/package.json +18 -0
  99. package/packages/os-cli/src/commands/events.ts +176 -0
  100. package/packages/os-cli/src/commands/logs.ts +96 -0
  101. package/packages/os-cli/src/commands/status.ts +53 -0
  102. package/packages/os-cli/src/commands/traces.ts +115 -0
  103. package/packages/os-cli/src/index.ts +15 -0
  104. package/packages/os-cli/src/lib/formatter.ts +123 -0
  105. package/packages/os-cli/src/lib/nats.ts +16 -0
  106. package/packages/os-cli/src/lib/trace-tree.ts +144 -0
  107. package/packages/os-cli/tsconfig.json +12 -0
  108. package/packages/os-sdk/package.json +27 -0
  109. package/packages/os-sdk/src/api/handler.ts +67 -0
  110. package/packages/os-sdk/src/config.ts +68 -0
  111. package/packages/os-sdk/src/db/factory.test.ts +42 -0
  112. package/packages/os-sdk/src/db/factory.ts +72 -0
  113. package/packages/os-sdk/src/db/migrate.ts +140 -0
  114. package/packages/os-sdk/src/db/provision.ts +44 -0
  115. package/packages/os-sdk/src/index.ts +36 -0
  116. package/packages/os-sdk/src/service/console-intercept.ts +60 -0
  117. package/packages/os-sdk/src/service/logger.ts +88 -0
  118. package/packages/os-sdk/src/service/o11y-streams.ts +88 -0
  119. package/packages/os-sdk/src/service/runtime.ts +259 -0
  120. package/packages/os-sdk/src/service/trace.ts +71 -0
  121. package/packages/os-sdk/tsconfig.json +16 -0
  122. package/packages/os-ui/package.json +13 -0
  123. package/packages/os-ui/src/index.ts +29 -0
  124. package/packages/os-ui/src/server.ts +4 -0
  125. package/packages/os-ui/tsconfig.json +19 -0
  126. package/packages/settings-app/components.ts +12 -0
  127. package/packages/settings-app/manifest.ts +18 -0
  128. package/packages/settings-app/package.json +14 -0
  129. package/packages/settings-app/views/settings/Settings.tsx +492 -0
  130. package/packages/terminal-app/components.ts +12 -0
  131. package/packages/terminal-app/manifest.ts +20 -0
  132. package/packages/terminal-app/package.json +23 -0
  133. package/packages/terminal-app/views/terminal/schema.ts +82 -0
  134. package/packages/terminal-app/views/terminal/service/index.ts +133 -0
  135. package/packages/terminal-app/views/terminal/service/runtime +1 -0
  136. package/packages/terminal-app/views/terminal/service/session.ts +290 -0
  137. package/packages/terminal-app/views/terminal/service/shell-hooks/bashrc-hook.sh +21 -0
  138. package/packages/terminal-app/views/terminal/types.ts +26 -0
  139. package/packages/terminal-app/views/terminal/ui/MultiTerminalApp.tsx +615 -0
  140. package/packages/terminal-app/views/terminal/ui/SplitDragHandle.tsx +91 -0
  141. package/packages/terminal-app/views/terminal/ui/SplitPaneRenderer.tsx +112 -0
  142. package/packages/terminal-app/views/terminal/ui/TerminalPane.tsx +478 -0
  143. package/packages/terminal-app/views/terminal/ui/TerminalTabBar.tsx +131 -0
  144. package/pnpm-workspace.yaml +9 -0
  145. package/postcss.config.mjs +7 -0
  146. package/public/file.svg +1 -0
  147. package/public/globe.svg +1 -0
  148. package/public/icons/code-server.svg +6 -0
  149. package/public/icons/default.svg +5 -0
  150. package/public/icons/dusk/1password.svg +1 -0
  151. package/public/icons/dusk/activity_monitor.svg +1 -0
  152. package/public/icons/dusk/app_store.svg +1 -0
  153. package/public/icons/dusk/atom.svg +1 -0
  154. package/public/icons/dusk/brave.svg +1 -0
  155. package/public/icons/dusk/calculator.svg +1 -0
  156. package/public/icons/dusk/calendar.svg +1 -0
  157. package/public/icons/dusk/chrome.svg +1 -0
  158. package/public/icons/dusk/chrome2.svg +1 -0
  159. package/public/icons/dusk/dashboard.svg +13 -0
  160. package/public/icons/dusk/discord.svg +1 -0
  161. package/public/icons/dusk/dropbox.svg +1 -0
  162. package/public/icons/dusk/electron.svg +1 -0
  163. package/public/icons/dusk/figma.svg +1 -0
  164. package/public/icons/dusk/finder.svg +1 -0
  165. package/public/icons/dusk/finder2.svg +1 -0
  166. package/public/icons/dusk/finder3.svg +1 -0
  167. package/public/icons/dusk/firefox.svg +1 -0
  168. package/public/icons/dusk/framer.svg +1 -0
  169. package/public/icons/dusk/gimp.svg +1 -0
  170. package/public/icons/dusk/github_desktop.svg +1 -0
  171. package/public/icons/dusk/hyper.svg +1 -0
  172. package/public/icons/dusk/hyper3.svg +1 -0
  173. package/public/icons/dusk/intellij.svg +1 -0
  174. package/public/icons/dusk/iterm2.svg +1 -0
  175. package/public/icons/dusk/itunes.svg +1 -0
  176. package/public/icons/dusk/mail.svg +1 -0
  177. package/public/icons/dusk/messenger.svg +1 -0
  178. package/public/icons/dusk/mongodb.svg +1 -0
  179. package/public/icons/dusk/notes.svg +1 -0
  180. package/public/icons/dusk/notion.svg +1 -0
  181. package/public/icons/dusk/obs.svg +1 -0
  182. package/public/icons/dusk/pages.svg +1 -0
  183. package/public/icons/dusk/photos.svg +1 -0
  184. package/public/icons/dusk/postman.svg +1 -0
  185. package/public/icons/dusk/preview.svg +1 -0
  186. package/public/icons/dusk/reminders.svg +1 -0
  187. package/public/icons/dusk/safari.svg +1 -0
  188. package/public/icons/dusk/sequel_pro.svg +1 -0
  189. package/public/icons/dusk/sketch.svg +1 -0
  190. package/public/icons/dusk/skype.svg +1 -0
  191. package/public/icons/dusk/slack.svg +1 -0
  192. package/public/icons/dusk/slack2.svg +1 -0
  193. package/public/icons/dusk/spotify.svg +1 -0
  194. package/public/icons/dusk/steam.svg +1 -0
  195. package/public/icons/dusk/system_preferences.svg +1 -0
  196. package/public/icons/dusk/tableplus.svg +1 -0
  197. package/public/icons/dusk/teams.svg +1 -0
  198. package/public/icons/dusk/telegram.svg +1 -0
  199. package/public/icons/dusk/terminal.svg +1 -0
  200. package/public/icons/dusk/todoist.svg +1 -0
  201. package/public/icons/dusk/trash.svg +1 -0
  202. package/public/icons/dusk/trello.svg +1 -0
  203. package/public/icons/dusk/vivaldi.svg +1 -0
  204. package/public/icons/dusk/vlc.svg +1 -0
  205. package/public/icons/dusk/vscode.svg +1 -0
  206. package/public/icons/dusk/whatsapp.svg +1 -0
  207. package/public/icons/dusk/xeyes.svg +1 -0
  208. package/public/icons/dusk/zoom.svg +1 -0
  209. package/public/icons/files.svg +5 -0
  210. package/public/icons/pwa/icon-192.png +0 -0
  211. package/public/icons/pwa/icon-512.png +0 -0
  212. package/public/icons/settings.svg +14 -0
  213. package/public/icons/terminal.svg +5 -0
  214. package/public/icons/text-editor.svg +7 -0
  215. package/public/manifest.json +38 -0
  216. package/public/next.svg +1 -0
  217. package/public/sw.js +41 -0
  218. package/public/vercel.svg +1 -0
  219. package/public/wallpapers/default.svg +10 -0
  220. package/public/window.svg +1 -0
  221. package/scripts/generate-pwa-icons.mjs +33 -0
  222. package/scripts/install-nats.sh +37 -0
  223. package/sentry.client.config.ts +21 -0
  224. package/sentry.edge.config.ts +12 -0
  225. package/sentry.server.config.ts +12 -0
  226. package/src/app/api/files/download/route.ts +81 -0
  227. package/src/app/api/files/download-zip/route.ts +102 -0
  228. package/src/app/api/files/upload/route.ts +58 -0
  229. package/src/app/api/webhooks/workos/route.ts +98 -0
  230. package/src/app/auth/callback/route.ts +16 -0
  231. package/src/app/auth/logout/route.ts +15 -0
  232. package/src/app/desktop/desktop-shell.tsx +110 -0
  233. package/src/app/desktop/layout.tsx +8 -0
  234. package/src/app/desktop/page.tsx +24 -0
  235. package/src/app/favicon.ico +0 -0
  236. package/src/app/globals.css +7 -0
  237. package/src/app/layout.tsx +64 -0
  238. package/src/app/offline/page.tsx +83 -0
  239. package/src/app/page.tsx +5 -0
  240. package/src/app/standalone/[appId]/page.tsx +28 -0
  241. package/src/app/standalone/layout.tsx +10 -0
  242. package/src/components/app-icon.tsx +55 -0
  243. package/src/components/apps/_echo/schema.ts +14 -0
  244. package/src/components/apps/_echo/service/index.ts +42 -0
  245. package/src/components/apps/app-manifest.ts +97 -0
  246. package/src/components/apps/app-registry.ts +55 -0
  247. package/src/components/apps/dev3000/Dev3000App.tsx +224 -0
  248. package/src/components/apps/dev3000/ErrorsPanel.tsx +160 -0
  249. package/src/components/apps/dev3000/Sidebar.tsx +41 -0
  250. package/src/components/apps/dev3000/TimelineLog.tsx +173 -0
  251. package/src/components/apps/dev3000/dev3000-context.tsx +29 -0
  252. package/src/components/apps/dev3000/index.ts +4 -0
  253. package/src/components/apps/dev3000/schema.ts +48 -0
  254. package/src/components/apps/dev3000/service/index.ts +520 -0
  255. package/src/components/apps/dev3000/service/runtime +1 -0
  256. package/src/components/apps/dev3000/types.ts +15 -0
  257. package/src/components/apps/dev3000/use-message-buffer.ts +46 -0
  258. package/src/components/apps/files/ContextMenu.tsx +151 -0
  259. package/src/components/apps/files/DeleteConfirmDialog.tsx +78 -0
  260. package/src/components/apps/files/FileItem.tsx +128 -0
  261. package/src/components/apps/files/FilesApp.tsx +509 -0
  262. package/src/components/apps/files/FilesListView.tsx +201 -0
  263. package/src/components/apps/files/FilesToolbar.tsx +117 -0
  264. package/src/components/apps/files/GridView.tsx +90 -0
  265. package/src/components/apps/files/InlineInput.tsx +131 -0
  266. package/src/components/apps/files/UploadOverlay.tsx +61 -0
  267. package/src/components/apps/files/schema.ts +49 -0
  268. package/src/components/apps/files/service/index.ts +227 -0
  269. package/src/components/apps/files/service/runtime +1 -0
  270. package/src/components/apps/files/use-files.ts +201 -0
  271. package/src/components/apps/files/use-upload.ts +105 -0
  272. package/src/components/apps/nats-viewer/ActiveSubs.tsx +34 -0
  273. package/src/components/apps/nats-viewer/MessageLog.tsx +247 -0
  274. package/src/components/apps/nats-viewer/NatsViewer.tsx +209 -0
  275. package/src/components/apps/nats-viewer/PublishPanel.tsx +113 -0
  276. package/src/components/apps/nats-viewer/RequestPanel.tsx +167 -0
  277. package/src/components/apps/nats-viewer/Sidebar.tsx +62 -0
  278. package/src/components/apps/nats-viewer/SubjectCatalog.tsx +64 -0
  279. package/src/components/apps/nats-viewer/SubscribeInput.tsx +59 -0
  280. package/src/components/apps/nats-viewer/index.ts +5 -0
  281. package/src/components/apps/nats-viewer/nats-viewer-context.tsx +31 -0
  282. package/src/components/apps/nats-viewer/types.ts +7 -0
  283. package/src/components/apps/nats-viewer/use-message-buffer.ts +55 -0
  284. package/src/components/apps/settings/Settings.tsx +492 -0
  285. package/src/components/apps/terminal/schema.ts +82 -0
  286. package/src/components/apps/terminal/service/index.ts +189 -0
  287. package/src/components/apps/terminal/service/runtime +1 -0
  288. package/src/components/apps/terminal/service/session.ts +296 -0
  289. package/src/components/apps/terminal/service/shell-hooks/bashrc-hook.sh +21 -0
  290. package/src/components/apps/terminal/types.ts +26 -0
  291. package/src/components/apps/terminal/ui/MultiTerminalApp.tsx +617 -0
  292. package/src/components/apps/terminal/ui/SplitDragHandle.tsx +91 -0
  293. package/src/components/apps/terminal/ui/SplitPaneRenderer.tsx +112 -0
  294. package/src/components/apps/terminal/ui/TerminalPane.tsx +476 -0
  295. package/src/components/apps/terminal/ui/TerminalTabBar.tsx +131 -0
  296. package/src/components/desktop/AnimatedBackground.tsx +69 -0
  297. package/src/components/desktop/Desktop.tsx +79 -0
  298. package/src/components/desktop/DesktopBackground.tsx +16 -0
  299. package/src/components/desktop/DesktopIcon.tsx +49 -0
  300. package/src/components/desktop/ShortcutViewer.tsx +136 -0
  301. package/src/components/desktop/WindowRenderer.tsx +34 -0
  302. package/src/components/desktop/WindowSwitcher.tsx +42 -0
  303. package/src/components/notifications/NotificationCenter.tsx +153 -0
  304. package/src/components/notifications/NotificationToasts.tsx +66 -0
  305. package/src/components/notifications/OrphanSessionToast.tsx +293 -0
  306. package/src/components/os-primitives/collapsible-sidebar.tsx +226 -0
  307. package/src/components/os-primitives/dialog.tsx +76 -0
  308. package/src/components/os-primitives/empty-state.tsx +43 -0
  309. package/src/components/os-primitives/index.ts +21 -0
  310. package/src/components/os-primitives/list-view.tsx +155 -0
  311. package/src/components/os-primitives/property-panel.tsx +108 -0
  312. package/src/components/os-primitives/section-header.tsx +19 -0
  313. package/src/components/os-primitives/sidebar-nav.tsx +110 -0
  314. package/src/components/os-primitives/split-pane.tsx +146 -0
  315. package/src/components/os-primitives/status-badge.tsx +10 -0
  316. package/src/components/os-primitives/status-bar.tsx +100 -0
  317. package/src/components/os-primitives/toolbar.tsx +152 -0
  318. package/src/components/taskbar/AppLauncher.tsx +114 -0
  319. package/src/components/taskbar/RunningApps.tsx +71 -0
  320. package/src/components/taskbar/SystemTray.tsx +134 -0
  321. package/src/components/taskbar/Taskbar.tsx +45 -0
  322. package/src/components/taskbar/UserMenu.tsx +138 -0
  323. package/src/components/taskbar/WorkspaceSwitcher.tsx +9 -0
  324. package/src/components/ui/ContextMenu.tsx +130 -0
  325. package/src/components/ui/badge.tsx +39 -0
  326. package/src/components/ui/button.tsx +102 -0
  327. package/src/components/ui/command.tsx +165 -0
  328. package/src/components/ui/dropdown-menu.tsx +233 -0
  329. package/src/components/ui/input.tsx +48 -0
  330. package/src/components/ui/note.tsx +55 -0
  331. package/src/components/ui/separator.tsx +25 -0
  332. package/src/components/ui/spinner.tsx +42 -0
  333. package/src/components/ui/switch.tsx +36 -0
  334. package/src/components/ui/theme-provider.tsx +24 -0
  335. package/src/components/ui/theme-switcher.tsx +51 -0
  336. package/src/components/ui/tooltip.tsx +62 -0
  337. package/src/components/window/MobileWindowStack.tsx +218 -0
  338. package/src/components/window/SnapPreview.tsx +37 -0
  339. package/src/components/window/StandaloneFrame.tsx +170 -0
  340. package/src/components/window/Window.tsx +423 -0
  341. package/src/components/window/WindowContent.tsx +14 -0
  342. package/src/components/window/WindowControlsOverlay.tsx +89 -0
  343. package/src/components/window/WindowFrame.tsx +124 -0
  344. package/src/lib/auth/index.ts +27 -0
  345. package/src/lib/auth/roles.ts +50 -0
  346. package/src/lib/auth/types.ts +32 -0
  347. package/src/lib/auth/use-auth.ts +53 -0
  348. package/src/lib/auth/webhook-handler.ts +87 -0
  349. package/src/lib/auth/workos.ts +67 -0
  350. package/src/lib/constants.ts +1 -0
  351. package/src/lib/desktop/dedup.ts +57 -0
  352. package/src/lib/desktop/schema.ts +55 -0
  353. package/src/lib/files/filename-validation.ts +41 -0
  354. package/src/lib/files/safe-path.ts +49 -0
  355. package/src/lib/hooks/use-desktop-nats.ts +438 -0
  356. package/src/lib/hooks/use-is-mobile.ts +23 -0
  357. package/src/lib/hooks/use-launch-app.ts +79 -0
  358. package/src/lib/hooks/use-nats-notifications.ts +84 -0
  359. package/src/lib/hooks/use-nats.ts +60 -0
  360. package/src/lib/hooks/use-visual-viewport.ts +72 -0
  361. package/src/lib/icons/resolve-window-icon.ts +10 -0
  362. package/src/lib/keyboard/defaults.ts +146 -0
  363. package/src/lib/keyboard/types.ts +52 -0
  364. package/src/lib/keyboard/use-global-keybinds.ts +231 -0
  365. package/src/lib/nats-client.ts +255 -0
  366. package/src/lib/nats.ts +35 -0
  367. package/src/lib/notifications/schema.ts +12 -0
  368. package/src/lib/service-loader.ts +171 -0
  369. package/src/lib/subjects.ts +64 -0
  370. package/src/lib/utils.ts +6 -0
  371. package/src/lib/ws-bridge.ts +288 -0
  372. package/src/lib/ws-protocol.ts +53 -0
  373. package/src/lib/ws-server.ts +167 -0
  374. package/src/middleware.ts +57 -0
  375. package/src/stores/desktop-store.ts +112 -0
  376. package/src/stores/keybind-store.ts +66 -0
  377. package/src/stores/notification-store.ts +271 -0
  378. package/src/stores/theme-store.ts +25 -0
  379. package/src/stores/window-store.ts +294 -0
  380. package/src/theme/animations.css +68 -0
  381. package/src/theme/base.css +123 -0
  382. package/src/theme/controls.css +35 -0
  383. package/src/theme/design-tokens.css +276 -0
  384. package/src/theme/index.css +23 -0
  385. package/src/theme/menus.css +45 -0
  386. package/src/theme/status.css +41 -0
  387. package/src/theme/surfaces.css +94 -0
  388. package/src/theme/tailwind-map.css +138 -0
  389. package/src/theme/taskbar.css +25 -0
  390. package/src/theme/terminal.css +55 -0
  391. package/src/theme/typography.css +26 -0
  392. package/src/theme/utilities.css +156 -0
  393. package/src/theme/window.css +103 -0
  394. package/src/types/desktop-entry.ts +12 -0
  395. package/src/types/use-descendants.d.ts +13 -0
  396. package/src/types/window.ts +28 -0
  397. package/src/types.d.ts +9 -0
  398. package/tauri/Cargo.lock +5464 -0
  399. package/tauri/Cargo.toml +19 -0
  400. package/tauri/build.rs +3 -0
  401. package/tauri/capabilities/default.json +36 -0
  402. package/tauri/icons/128x128.png +0 -0
  403. package/tauri/icons/128x128@2x.png +0 -0
  404. package/tauri/icons/32x32.png +0 -0
  405. package/tauri/icons/icon.png +0 -0
  406. package/tauri/src/main.rs +396 -0
  407. package/tauri/tauri.conf.json +23 -0
  408. package/tsconfig.json +43 -0
@@ -0,0 +1,165 @@
1
+ 'use client';
2
+
3
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
4
+ import { Command as CommandPrimitive } from 'cmdk';
5
+ import { Search } from 'lucide-react';
6
+ import * as React from 'react';
7
+ import { cn } from '@/lib/utils';
8
+
9
+ const Command = React.forwardRef<
10
+ React.ComponentRef<typeof CommandPrimitive>,
11
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive>
12
+ >(({ className, ...props }, ref) => (
13
+ <CommandPrimitive
14
+ ref={ref}
15
+ className={cn('flex h-full w-full flex-col overflow-hidden rounded-xl bg-background-100 text-gray-1000', className)}
16
+ {...props}
17
+ />
18
+ ));
19
+ Command.displayName = CommandPrimitive.displayName;
20
+
21
+ function CommandDialog({
22
+ children,
23
+ open,
24
+ onOpenChange,
25
+ ...props
26
+ }: React.ComponentProps<typeof DialogPrimitive.Root> & React.ComponentPropsWithoutRef<typeof CommandPrimitive>) {
27
+ return (
28
+ <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
29
+ <DialogPrimitive.Portal>
30
+ <DialogPrimitive.Overlay
31
+ className="fixed inset-0 z-[9999] bg-black/40 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0"
32
+ cmdk-overlay=""
33
+ />
34
+ <DialogPrimitive.Content
35
+ className="fixed left-[50%] top-[20%] z-[9999] w-full max-w-lg translate-x-[-50%] overflow-hidden rounded-xl shadow-lg"
36
+ style={{
37
+ background: 'var(--khal-menu-bg)',
38
+ border: '1px solid var(--khal-menu-border)',
39
+ boxShadow: 'var(--khal-menu-shadow)',
40
+ backdropFilter: 'blur(24px)',
41
+ WebkitBackdropFilter: 'blur(24px)',
42
+ }}
43
+ cmdk-dialog=""
44
+ aria-describedby={undefined}
45
+ >
46
+ <DialogPrimitive.Title className="sr-only">Command palette</DialogPrimitive.Title>
47
+ <Command
48
+ className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-gray-700 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3"
49
+ {...props}
50
+ >
51
+ {children}
52
+ </Command>
53
+ </DialogPrimitive.Content>
54
+ </DialogPrimitive.Portal>
55
+ </DialogPrimitive.Root>
56
+ );
57
+ }
58
+
59
+ const CommandInput = React.forwardRef<
60
+ React.ComponentRef<typeof CommandPrimitive.Input>,
61
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
62
+ >(({ className, ...props }, ref) => (
63
+ <div
64
+ className="flex items-center px-3"
65
+ style={{ borderBottom: '1px solid var(--khal-border-default)' }}
66
+ cmdk-input-wrapper=""
67
+ >
68
+ <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
69
+ <CommandPrimitive.Input
70
+ ref={ref}
71
+ className={cn(
72
+ 'flex h-10 w-full rounded-md bg-transparent py-3 text-copy-13 outline-none',
73
+ 'placeholder:text-gray-700',
74
+ 'disabled:cursor-not-allowed disabled:opacity-50',
75
+ className
76
+ )}
77
+ {...props}
78
+ />
79
+ </div>
80
+ ));
81
+ CommandInput.displayName = CommandPrimitive.Input.displayName;
82
+
83
+ const CommandList = React.forwardRef<
84
+ React.ComponentRef<typeof CommandPrimitive.List>,
85
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
86
+ >(({ className, ...props }, ref) => (
87
+ <CommandPrimitive.List
88
+ ref={ref}
89
+ className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
90
+ {...props}
91
+ />
92
+ ));
93
+ CommandList.displayName = CommandPrimitive.List.displayName;
94
+
95
+ const CommandEmpty = React.forwardRef<
96
+ React.ComponentRef<typeof CommandPrimitive.Empty>,
97
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
98
+ >((props, ref) => (
99
+ <CommandPrimitive.Empty ref={ref} className="py-6 text-center text-copy-13 text-gray-700" {...props} />
100
+ ));
101
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
102
+
103
+ const CommandGroup = React.forwardRef<
104
+ React.ComponentRef<typeof CommandPrimitive.Group>,
105
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
106
+ >(({ className, ...props }, ref) => (
107
+ <CommandPrimitive.Group
108
+ ref={ref}
109
+ className={cn(
110
+ 'overflow-hidden p-1 text-gray-1000',
111
+ '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-label-12 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-gray-700',
112
+ className
113
+ )}
114
+ {...props}
115
+ />
116
+ ));
117
+ CommandGroup.displayName = CommandPrimitive.Group.displayName;
118
+
119
+ const CommandSeparator = React.forwardRef<
120
+ React.ComponentRef<typeof CommandPrimitive.Separator>,
121
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
122
+ >(({ className, ...props }, ref) => (
123
+ <CommandPrimitive.Separator ref={ref} className={cn('-mx-1 h-px bg-gray-alpha-400', className)} {...props} />
124
+ ));
125
+ CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
126
+
127
+ const CommandItem = React.forwardRef<
128
+ React.ComponentRef<typeof CommandPrimitive.Item>,
129
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> & {
130
+ prefix?: React.ReactNode;
131
+ callback?: () => void;
132
+ }
133
+ >(({ className, prefix, callback, onSelect, children, ...props }, ref) => (
134
+ <CommandPrimitive.Item
135
+ ref={ref}
136
+ onSelect={onSelect ?? callback}
137
+ className={cn(
138
+ 'relative flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-copy-13 outline-none select-none',
139
+ 'data-[selected=true]:text-gray-1000',
140
+ 'data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50',
141
+ className
142
+ )}
143
+ {...props}
144
+ >
145
+ {prefix && <span className="inline-flex shrink-0">{prefix}</span>}
146
+ {children}
147
+ </CommandPrimitive.Item>
148
+ ));
149
+ CommandItem.displayName = CommandPrimitive.Item.displayName;
150
+
151
+ const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
152
+ return <span className={cn('ml-auto text-label-12 tracking-widest text-gray-700', className)} {...props} />;
153
+ };
154
+
155
+ export {
156
+ Command,
157
+ CommandDialog,
158
+ CommandInput,
159
+ CommandList,
160
+ CommandEmpty,
161
+ CommandGroup,
162
+ CommandItem,
163
+ CommandSeparator,
164
+ CommandShortcut,
165
+ };
@@ -0,0 +1,233 @@
1
+ 'use client';
2
+
3
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
4
+ import * as React from 'react';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ const DropdownMenu = DropdownMenuPrimitive.Root;
8
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
9
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group;
10
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
11
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
12
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
13
+
14
+ const DropdownMenuSubTrigger = React.forwardRef<
15
+ React.ComponentRef<typeof DropdownMenuPrimitive.SubTrigger>,
16
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
17
+ inset?: boolean;
18
+ }
19
+ >(({ className, inset, children, ...props }, ref) => (
20
+ <DropdownMenuPrimitive.SubTrigger
21
+ ref={ref}
22
+ className={cn(
23
+ 'flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-copy-13 outline-none select-none',
24
+ 'focus:bg-[var(--khal-menu-hover)]',
25
+ 'data-[state=open]:bg-[var(--khal-menu-hover)]',
26
+ inset && 'pl-8',
27
+ className
28
+ )}
29
+ style={{ color: 'var(--khal-text-primary)' }}
30
+ {...props}
31
+ >
32
+ {children}
33
+ </DropdownMenuPrimitive.SubTrigger>
34
+ ));
35
+ DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
36
+
37
+ const DropdownMenuSubContent = React.forwardRef<
38
+ React.ComponentRef<typeof DropdownMenuPrimitive.SubContent>,
39
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
40
+ >(({ className, ...props }, ref) => (
41
+ <DropdownMenuPrimitive.SubContent
42
+ ref={ref}
43
+ className={cn(
44
+ 'z-[9999] min-w-[8rem] overflow-hidden rounded-xl p-1',
45
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
46
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
47
+ 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
48
+ className
49
+ )}
50
+ style={{
51
+ background: 'var(--khal-menu-bg)',
52
+ border: '1px solid var(--khal-menu-border)',
53
+ boxShadow: 'var(--khal-menu-shadow)',
54
+ color: 'var(--khal-text-primary)',
55
+ }}
56
+ {...props}
57
+ />
58
+ ));
59
+ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
60
+
61
+ const DropdownMenuContent = React.forwardRef<
62
+ React.ComponentRef<typeof DropdownMenuPrimitive.Content>,
63
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
64
+ >(({ className, sideOffset = 4, ...props }, ref) => (
65
+ <DropdownMenuPrimitive.Portal>
66
+ <DropdownMenuPrimitive.Content
67
+ ref={ref}
68
+ sideOffset={sideOffset}
69
+ className={cn(
70
+ 'z-[9999] min-w-[8rem] overflow-hidden rounded-xl p-1',
71
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
72
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
73
+ 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
74
+ className
75
+ )}
76
+ style={{
77
+ background: 'var(--khal-menu-bg)',
78
+ border: '1px solid var(--khal-menu-border)',
79
+ boxShadow: 'var(--khal-menu-shadow)',
80
+ color: 'var(--khal-text-primary)',
81
+ }}
82
+ {...props}
83
+ />
84
+ </DropdownMenuPrimitive.Portal>
85
+ ));
86
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
87
+
88
+ const DropdownMenuItem = React.forwardRef<
89
+ React.ComponentRef<typeof DropdownMenuPrimitive.Item>,
90
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
91
+ inset?: boolean;
92
+ prefix?: React.ReactNode;
93
+ suffix?: React.ReactNode;
94
+ }
95
+ >(({ className, inset, prefix, suffix, children, ...props }, ref) => (
96
+ <DropdownMenuPrimitive.Item
97
+ ref={ref}
98
+ className={cn(
99
+ 'relative flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-copy-13 outline-none select-none transition-colors',
100
+ 'focus:bg-[var(--khal-menu-hover)]',
101
+ 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
102
+ inset && 'pl-8',
103
+ className
104
+ )}
105
+ style={{ color: 'var(--khal-text-primary)' }}
106
+ {...props}
107
+ >
108
+ {prefix && <span className="inline-flex shrink-0">{prefix}</span>}
109
+ {children}
110
+ {suffix && (
111
+ <span className="ml-auto inline-flex shrink-0" style={{ color: 'var(--khal-text-muted)' }}>
112
+ {suffix}
113
+ </span>
114
+ )}
115
+ </DropdownMenuPrimitive.Item>
116
+ ));
117
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
118
+
119
+ const DropdownMenuCheckboxItem = React.forwardRef<
120
+ React.ComponentRef<typeof DropdownMenuPrimitive.CheckboxItem>,
121
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
122
+ >(({ className, children, checked, ...props }, ref) => (
123
+ <DropdownMenuPrimitive.CheckboxItem
124
+ ref={ref}
125
+ className={cn(
126
+ 'relative flex cursor-default items-center rounded-lg py-1.5 pl-8 pr-2 text-copy-13 outline-none select-none transition-colors',
127
+ 'focus:bg-[var(--khal-menu-hover)]',
128
+ 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
129
+ className
130
+ )}
131
+ style={{ color: 'var(--khal-text-primary)' }}
132
+ checked={checked}
133
+ {...props}
134
+ >
135
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
136
+ <DropdownMenuPrimitive.ItemIndicator>
137
+ <svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
138
+ <path
139
+ d="M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3354 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.5553 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z"
140
+ fill="currentColor"
141
+ fillRule="evenodd"
142
+ clipRule="evenodd"
143
+ ></path>
144
+ </svg>
145
+ </DropdownMenuPrimitive.ItemIndicator>
146
+ </span>
147
+ {children}
148
+ </DropdownMenuPrimitive.CheckboxItem>
149
+ ));
150
+ DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
151
+
152
+ const DropdownMenuRadioItem = React.forwardRef<
153
+ React.ComponentRef<typeof DropdownMenuPrimitive.RadioItem>,
154
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
155
+ >(({ className, children, ...props }, ref) => (
156
+ <DropdownMenuPrimitive.RadioItem
157
+ ref={ref}
158
+ className={cn(
159
+ 'relative flex cursor-default items-center rounded-lg py-1.5 pl-8 pr-2 text-copy-13 outline-none select-none transition-colors',
160
+ 'focus:bg-[var(--khal-menu-hover)]',
161
+ 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
162
+ className
163
+ )}
164
+ style={{ color: 'var(--khal-text-primary)' }}
165
+ {...props}
166
+ >
167
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
168
+ <DropdownMenuPrimitive.ItemIndicator>
169
+ <svg width="8" height="8" viewBox="0 0 8 8" fill="none">
170
+ <circle cx="4" cy="4" r="4" fill="currentColor" />
171
+ </svg>
172
+ </DropdownMenuPrimitive.ItemIndicator>
173
+ </span>
174
+ {children}
175
+ </DropdownMenuPrimitive.RadioItem>
176
+ ));
177
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
178
+
179
+ const DropdownMenuLabel = React.forwardRef<
180
+ React.ComponentRef<typeof DropdownMenuPrimitive.Label>,
181
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
182
+ inset?: boolean;
183
+ }
184
+ >(({ className, inset, ...props }, ref) => (
185
+ <DropdownMenuPrimitive.Label
186
+ ref={ref}
187
+ className={cn('px-2 py-1.5 text-label-12 font-semibold', inset && 'pl-8', className)}
188
+ style={{ color: 'var(--khal-text-secondary)' }}
189
+ {...props}
190
+ />
191
+ ));
192
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
193
+
194
+ const DropdownMenuSeparator = React.forwardRef<
195
+ React.ComponentRef<typeof DropdownMenuPrimitive.Separator>,
196
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
197
+ >(({ className, ...props }, ref) => (
198
+ <DropdownMenuPrimitive.Separator
199
+ ref={ref}
200
+ className={cn('-mx-1 my-1 h-px', className)}
201
+ style={{ background: 'var(--khal-border-default)' }}
202
+ {...props}
203
+ />
204
+ ));
205
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
206
+
207
+ const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
208
+ return (
209
+ <span
210
+ className={cn('ml-auto text-label-12 tracking-widest', className)}
211
+ style={{ color: 'var(--khal-text-muted)' }}
212
+ {...props}
213
+ />
214
+ );
215
+ };
216
+
217
+ export {
218
+ DropdownMenu,
219
+ DropdownMenuTrigger,
220
+ DropdownMenuContent,
221
+ DropdownMenuItem,
222
+ DropdownMenuCheckboxItem,
223
+ DropdownMenuRadioItem,
224
+ DropdownMenuLabel,
225
+ DropdownMenuSeparator,
226
+ DropdownMenuShortcut,
227
+ DropdownMenuGroup,
228
+ DropdownMenuPortal,
229
+ DropdownMenuSub,
230
+ DropdownMenuSubContent,
231
+ DropdownMenuSubTrigger,
232
+ DropdownMenuRadioGroup,
233
+ };
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { cn } from '@/lib/utils';
5
+
6
+ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
7
+ /** Label above the input (geistcn compat) */
8
+ label?: string;
9
+ /** Size variant (geistcn compat) */
10
+ size?: 'small' | 'medium' | 'large';
11
+ /** HTML input type (named typeName for geistcn compat, also accepts type) */
12
+ typeName?: string;
13
+ }
14
+
15
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
16
+ ({ className, label, size, typeName, type, ...props }, ref) => {
17
+ const input = (
18
+ <input
19
+ type={typeName ?? type}
20
+ className={cn(
21
+ 'flex w-full rounded-md border border-gray-alpha-400 bg-background-100 px-3 text-copy-13 text-gray-1000 transition-colors',
22
+ 'file:border-0 file:bg-transparent file:text-sm file:font-medium',
23
+ 'placeholder:text-gray-700',
24
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-700 focus-visible:ring-offset-1',
25
+ 'disabled:cursor-not-allowed disabled:opacity-50',
26
+ size === 'small' ? 'h-8' : size === 'large' ? 'h-11' : 'h-9',
27
+ className
28
+ )}
29
+ ref={ref}
30
+ {...props}
31
+ />
32
+ );
33
+
34
+ if (label) {
35
+ return (
36
+ <div className="flex flex-col gap-1.5">
37
+ <label className="text-label-13 text-gray-900">{label}</label>
38
+ {input}
39
+ </div>
40
+ );
41
+ }
42
+
43
+ return input;
44
+ }
45
+ );
46
+ Input.displayName = 'Input';
47
+
48
+ export { Input };
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+ import { AlertCircle, AlertTriangle, CheckCircle, Info } from 'lucide-react';
5
+ import * as React from 'react';
6
+ import { cn } from '@/lib/utils';
7
+
8
+ const noteVariants = cva('flex items-start gap-2 rounded-md border p-3 text-copy-13', {
9
+ variants: {
10
+ type: {
11
+ default: 'border-gray-alpha-400 bg-gray-alpha-100 text-gray-900',
12
+ error: 'border-red-300 bg-red-100 text-red-900',
13
+ warning: 'border-amber-300 bg-amber-100 text-amber-900',
14
+ success: 'border-green-300 bg-green-100 text-green-900',
15
+ },
16
+ size: {
17
+ default: 'p-3',
18
+ small: 'p-2 text-label-12',
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ type: 'default',
23
+ size: 'default',
24
+ },
25
+ });
26
+
27
+ const iconMap = {
28
+ default: Info,
29
+ error: AlertCircle,
30
+ warning: AlertTriangle,
31
+ success: CheckCircle,
32
+ } as const;
33
+
34
+ interface NoteProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof noteVariants> {
35
+ type?: 'default' | 'error' | 'warning' | 'success';
36
+ label?: boolean | string;
37
+ action?: React.ReactNode;
38
+ disabled?: boolean;
39
+ fill?: boolean;
40
+ }
41
+
42
+ function Note({ className, type = 'default', size, label, action, children, ...props }: NoteProps) {
43
+ const Icon = iconMap[type];
44
+ const showLabel = label !== false;
45
+
46
+ return (
47
+ <div className={cn(noteVariants({ type, size }), className)} role="alert" {...props}>
48
+ {showLabel && <Icon className="h-4 w-4 shrink-0 mt-0.5" />}
49
+ <div className="flex-1 min-w-0">{children}</div>
50
+ {action && <div className="shrink-0">{action}</div>}
51
+ </div>
52
+ );
53
+ }
54
+
55
+ export { Note };
@@ -0,0 +1,25 @@
1
+ 'use client';
2
+
3
+ import * as SeparatorPrimitive from '@radix-ui/react-separator';
4
+ import * as React from 'react';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ const Separator = React.forwardRef<
8
+ React.ComponentRef<typeof SeparatorPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
10
+ >(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => (
11
+ <SeparatorPrimitive.Root
12
+ ref={ref}
13
+ decorative={decorative}
14
+ orientation={orientation}
15
+ className={cn(
16
+ 'shrink-0 bg-gray-alpha-400',
17
+ orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
18
+ className
19
+ )}
20
+ {...props}
21
+ />
22
+ ));
23
+ Separator.displayName = SeparatorPrimitive.Root.displayName;
24
+
25
+ export { Separator };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { cn } from '@/lib/utils';
5
+
6
+ const sizeMap = {
7
+ sm: 16,
8
+ md: 20,
9
+ lg: 24,
10
+ } as const;
11
+
12
+ interface SpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
13
+ size?: keyof typeof sizeMap;
14
+ }
15
+
16
+ function Spinner({ size = 'md', className, ...props }: SpinnerProps) {
17
+ const px = sizeMap[size];
18
+ return (
19
+ <div
20
+ role="status"
21
+ aria-label="Loading"
22
+ className={cn('inline-flex items-center justify-center', className)}
23
+ {...props}
24
+ >
25
+ <svg width={px} height={px} viewBox="0 0 20 20" fill="none" className="animate-spin">
26
+ <circle cx="10" cy="10" r="8" stroke="currentColor" strokeWidth="2" strokeLinecap="round" opacity="0.25" />
27
+ <circle
28
+ cx="10"
29
+ cy="10"
30
+ r="8"
31
+ stroke="currentColor"
32
+ strokeWidth="2"
33
+ strokeLinecap="round"
34
+ strokeDasharray="50.26"
35
+ strokeDashoffset="37.7"
36
+ />
37
+ </svg>
38
+ </div>
39
+ );
40
+ }
41
+
42
+ export { Spinner };
@@ -0,0 +1,36 @@
1
+ 'use client';
2
+
3
+ import * as SwitchPrimitive from '@radix-ui/react-switch';
4
+ import * as React from 'react';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ interface ToggleProps extends Omit<React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>, 'onChange'> {
8
+ onChange?: (checked: boolean) => void;
9
+ }
10
+
11
+ const Toggle = React.forwardRef<React.ComponentRef<typeof SwitchPrimitive.Root>, ToggleProps>(
12
+ ({ className, onChange, onCheckedChange, ...props }, ref) => (
13
+ <SwitchPrimitive.Root
14
+ className={cn(
15
+ 'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors',
16
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-700 focus-visible:ring-offset-2 focus-visible:ring-offset-background-100',
17
+ 'disabled:cursor-not-allowed disabled:opacity-50',
18
+ 'data-[state=checked]:bg-gray-1000 data-[state=unchecked]:bg-gray-alpha-400',
19
+ className
20
+ )}
21
+ onCheckedChange={onCheckedChange ?? onChange}
22
+ {...props}
23
+ ref={ref}
24
+ >
25
+ <SwitchPrimitive.Thumb
26
+ className={cn(
27
+ 'pointer-events-none block h-4 w-4 rounded-full bg-white shadow-lg ring-0 transition-transform',
28
+ 'data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0'
29
+ )}
30
+ />
31
+ </SwitchPrimitive.Root>
32
+ )
33
+ );
34
+ Toggle.displayName = 'Toggle';
35
+
36
+ export { Toggle };
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import { ThemeProvider as NextThemesProvider } from 'next-themes';
4
+ import { useEffect } from 'react';
5
+ import { useThemeStore } from '@/stores/theme-store';
6
+
7
+ function ReduceMotionSync() {
8
+ const reduceMotion = useThemeStore((s) => s.reduceMotion);
9
+
10
+ useEffect(() => {
11
+ document.documentElement.setAttribute('data-reduce-motion', String(reduceMotion));
12
+ }, [reduceMotion]);
13
+
14
+ return null;
15
+ }
16
+
17
+ export function ThemeProvider({ children, ...props }: React.ComponentProps<typeof NextThemesProvider>) {
18
+ return (
19
+ <NextThemesProvider {...props}>
20
+ <ReduceMotionSync />
21
+ {children}
22
+ </NextThemesProvider>
23
+ );
24
+ }
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+
3
+ import { Monitor, Moon, Sun } from 'lucide-react';
4
+ import { useTheme } from 'next-themes';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ const themes = [
8
+ { value: 'system', label: 'System', icon: Monitor },
9
+ { value: 'light', label: 'Light', icon: Sun },
10
+ { value: 'dark', label: 'Dark', icon: Moon },
11
+ ] as const;
12
+
13
+ interface ThemeSwitcherProps {
14
+ small?: boolean;
15
+ className?: string;
16
+ onThemeSwitch?: (theme: string) => void;
17
+ disabled?: boolean;
18
+ }
19
+
20
+ function ThemeSwitcher({ small, className, onThemeSwitch, disabled }: ThemeSwitcherProps) {
21
+ const { theme, setTheme } = useTheme();
22
+
23
+ return (
24
+ <fieldset
25
+ className={cn('inline-flex items-center gap-1 rounded-lg bg-gray-alpha-100 p-1', className)}
26
+ disabled={disabled}
27
+ >
28
+ {themes.map(({ value, label, icon: Icon }) => (
29
+ <button
30
+ key={value}
31
+ type="button"
32
+ role="radio"
33
+ aria-checked={theme === value}
34
+ onClick={() => {
35
+ setTheme(value);
36
+ onThemeSwitch?.(value);
37
+ }}
38
+ className={cn(
39
+ 'inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-label-12 transition-colors cursor-pointer',
40
+ theme === value ? 'bg-background-100 text-gray-1000 shadow-sm' : 'text-gray-700 hover:text-gray-1000'
41
+ )}
42
+ >
43
+ <Icon className={small ? 'h-3.5 w-3.5' : 'h-4 w-4'} />
44
+ {!small && label}
45
+ </button>
46
+ ))}
47
+ </fieldset>
48
+ );
49
+ }
50
+
51
+ export { ThemeSwitcher };