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,131 @@
1
+ 'use client';
2
+
3
+ import type { TerminalTab } from '../types';
4
+
5
+ interface TerminalTabBarProps {
6
+ tabs: TerminalTab[];
7
+ activeTabId: string;
8
+ onTabClick: (tabId: string) => void;
9
+ onTabClose: (tabId: string) => void;
10
+ onNewTab: () => void;
11
+ }
12
+
13
+ /**
14
+ * Tab bar component for multi-tab terminal.
15
+ * macOS-style pill tabs with --khal-* CSS tokens.
16
+ *
17
+ * The outer container does NOT have data-no-drag so empty
18
+ * space in the tab bar area acts as a window drag handle.
19
+ * Only interactive elements (tabs, buttons) have data-no-drag.
20
+ */
21
+ export function TerminalTabBar({ tabs, activeTabId, onTabClick, onTabClose, onNewTab }: TerminalTabBarProps) {
22
+ const getTabTitle = (tab: TerminalTab) => {
23
+ if (tab.cwd) {
24
+ const parts = tab.cwd.split('/');
25
+ return parts[parts.length - 1] || 'root';
26
+ }
27
+ return tab.title || 'bash';
28
+ };
29
+
30
+ // Reserve space for the floating window controls (glass controls on the right)
31
+ const prControls = '100px';
32
+
33
+ return (
34
+ <div
35
+ className="terminal-tab-bar flex items-center gap-1 px-2 py-1.5"
36
+ style={{
37
+ paddingRight: prControls,
38
+ minHeight: '36px',
39
+ }}
40
+ >
41
+ {tabs.map((tab) => {
42
+ const isActive = tab.id === activeTabId;
43
+ return (
44
+ <div
45
+ data-no-drag
46
+ key={tab.id}
47
+ className="flex items-center gap-1.5 rounded-md px-2.5 py-1 cursor-pointer select-none transition-all duration-150"
48
+ style={
49
+ isActive
50
+ ? {
51
+ background: 'var(--khal-surface-raised)',
52
+ color: 'var(--khal-text-primary)',
53
+ }
54
+ : {
55
+ background: 'transparent',
56
+ color: 'var(--khal-text-muted)',
57
+ }
58
+ }
59
+ onMouseEnter={(e) => {
60
+ if (!isActive) {
61
+ const el = e.currentTarget as HTMLElement;
62
+ el.style.color = 'var(--khal-text-secondary)';
63
+ el.style.background = 'var(--khal-accent-subtle)';
64
+ }
65
+ }}
66
+ onMouseLeave={(e) => {
67
+ if (!isActive) {
68
+ const el = e.currentTarget as HTMLElement;
69
+ el.style.color = 'var(--khal-text-muted)';
70
+ el.style.background = 'transparent';
71
+ }
72
+ }}
73
+ onClick={() => onTabClick(tab.id)}
74
+ >
75
+ <span className="text-xs max-w-[120px] truncate">{getTabTitle(tab)}</span>
76
+ {tabs.length > 1 && (
77
+ <button
78
+ data-no-drag
79
+ className="flex h-4 w-4 items-center justify-center rounded-sm opacity-0 transition-opacity group-hover:opacity-100"
80
+ style={{
81
+ color: 'var(--khal-text-muted)',
82
+ opacity: isActive ? 0.6 : 0,
83
+ }}
84
+ onMouseEnter={(e) => {
85
+ const el = e.currentTarget as HTMLElement;
86
+ el.style.opacity = '1';
87
+ el.style.color = 'rgb(239, 68, 68)';
88
+ }}
89
+ onMouseLeave={(e) => {
90
+ const el = e.currentTarget as HTMLElement;
91
+ el.style.opacity = isActive ? '0.6' : '0';
92
+ el.style.color = 'var(--khal-text-muted)';
93
+ }}
94
+ onClick={(e) => {
95
+ e.stopPropagation();
96
+ onTabClose(tab.id);
97
+ }}
98
+ aria-label="Close tab"
99
+ >
100
+ <svg width="8" height="8" viewBox="0 0 8 8" fill="none" stroke="currentColor" strokeWidth="1.5">
101
+ <path d="M1 1l6 6M7 1l-6 6" />
102
+ </svg>
103
+ </button>
104
+ )}
105
+ </div>
106
+ );
107
+ })}
108
+ <button
109
+ data-no-drag
110
+ className="flex h-5 w-5 items-center justify-center rounded-md transition-colors"
111
+ style={{ color: 'var(--khal-text-muted)' }}
112
+ onMouseEnter={(e) => {
113
+ const el = e.currentTarget as HTMLElement;
114
+ el.style.color = 'var(--khal-text-primary)';
115
+ el.style.background = 'var(--khal-accent-subtle)';
116
+ }}
117
+ onMouseLeave={(e) => {
118
+ const el = e.currentTarget as HTMLElement;
119
+ el.style.color = 'var(--khal-text-muted)';
120
+ el.style.background = '';
121
+ }}
122
+ onClick={onNewTab}
123
+ aria-label="New tab"
124
+ >
125
+ <svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.5">
126
+ <path d="M5 1v8M1 5h8" />
127
+ </svg>
128
+ </button>
129
+ </div>
130
+ );
131
+ }
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * AnimatedBackground - Lightweight CSS-only animated desktop background.
5
+ *
6
+ * Renders three large, soft, blurred gradient orbs that slowly drift using
7
+ * CSS @keyframes and transforms. Colors are read from CSS custom properties
8
+ * (--khal-bg-anim-1/2/3) which change per theme concept.
9
+ *
10
+ * Animation is disabled when:
11
+ * - The user's OS prefers reduced motion (prefers-reduced-motion: reduce)
12
+ * - The "Reduce motion" toggle is enabled in Settings (data-reduce-motion="true" on <html>)
13
+ *
14
+ * No canvas, no requestAnimationFrame, no JS animation loops.
15
+ */
16
+ export function AnimatedBackground() {
17
+ return (
18
+ <div className="khal-animated-bg" aria-hidden="true">
19
+ <div
20
+ className="khal-orb"
21
+ style={{
22
+ position: 'absolute',
23
+ width: '60vw',
24
+ height: '60vw',
25
+ borderRadius: '50%',
26
+ background: 'var(--khal-bg-anim-1)',
27
+ filter: 'blur(80px)',
28
+ opacity: 0.6,
29
+ top: '-20%',
30
+ left: '-10%',
31
+ animation: 'khal-drift-1 30s ease-in-out infinite alternate',
32
+ willChange: 'transform',
33
+ }}
34
+ />
35
+ <div
36
+ className="khal-orb"
37
+ style={{
38
+ position: 'absolute',
39
+ width: '50vw',
40
+ height: '50vw',
41
+ borderRadius: '50%',
42
+ background: 'var(--khal-bg-anim-2)',
43
+ filter: 'blur(90px)',
44
+ opacity: 0.5,
45
+ bottom: '-20%',
46
+ right: '-10%',
47
+ animation: 'khal-drift-2 40s ease-in-out infinite alternate',
48
+ willChange: 'transform',
49
+ }}
50
+ />
51
+ <div
52
+ className="khal-orb"
53
+ style={{
54
+ position: 'absolute',
55
+ width: '45vw',
56
+ height: '45vw',
57
+ borderRadius: '50%',
58
+ background: 'var(--khal-bg-anim-3)',
59
+ filter: 'blur(100px)',
60
+ opacity: 0.4,
61
+ top: '30%',
62
+ right: '20%',
63
+ animation: 'khal-drift-3 50s ease-in-out infinite alternate',
64
+ willChange: 'transform',
65
+ }}
66
+ />
67
+ </div>
68
+ );
69
+ }
@@ -0,0 +1,79 @@
1
+ 'use client';
2
+
3
+ import { useCallback } from 'react';
4
+ import {
5
+ ContextMenu,
6
+ ContextMenuContent,
7
+ ContextMenuItem,
8
+ ContextMenuSeparator,
9
+ ContextMenuTrigger,
10
+ } from '@/components/ui/ContextMenu';
11
+ import { useKhalAuth } from '@/lib/auth/use-auth';
12
+ import { useIsMobile } from '@/lib/hooks/use-is-mobile';
13
+ import { useLaunchApp } from '@/lib/hooks/use-launch-app';
14
+ import { useFilteredDesktopApps } from '@/stores/desktop-store';
15
+ import { DesktopIcon } from './DesktopIcon';
16
+
17
+ export function Desktop() {
18
+ const auth = useKhalAuth();
19
+ const permissions = auth?.permissions ?? [];
20
+ const desktopIcons = useFilteredDesktopApps(permissions);
21
+ const launchApp = useLaunchApp();
22
+ const isMobile = useIsMobile();
23
+
24
+ const launchById = useCallback(
25
+ (id: string) => {
26
+ const entry = desktopIcons.find((a) => a.id === id);
27
+ if (entry) launchApp(entry);
28
+ },
29
+ [desktopIcons, launchApp]
30
+ );
31
+
32
+ // Only show context menu items the user has permission for
33
+ const hasPermission = useCallback((perm: string) => permissions.includes(perm), [permissions]);
34
+
35
+ const desktopContent = (
36
+ <>
37
+ <nav
38
+ className={
39
+ isMobile
40
+ ? 'grid auto-rows-max grid-cols-4 gap-2 p-4 pb-16'
41
+ : 'grid auto-rows-max grid-cols-[repeat(auto-fill,5rem)] gap-1 p-4 pb-16'
42
+ }
43
+ aria-label="Desktop applications"
44
+ >
45
+ {desktopIcons.map((entry) => (
46
+ <DesktopIcon key={entry.id} entry={entry} onLaunch={() => launchApp(entry)} isMobile={isMobile} />
47
+ ))}
48
+ </nav>
49
+ </>
50
+ );
51
+
52
+ if (isMobile) {
53
+ return (
54
+ <main className="fixed inset-0 overflow-hidden" aria-label="Desktop">
55
+ {desktopContent}
56
+ </main>
57
+ );
58
+ }
59
+
60
+ return (
61
+ <ContextMenu>
62
+ <ContextMenuTrigger asChild>
63
+ <main className="fixed inset-0 overflow-hidden" aria-label="Desktop">
64
+ {desktopContent}
65
+ </main>
66
+ </ContextMenuTrigger>
67
+ <ContextMenuContent className="w-[200px] z-[9999]">
68
+ {hasPermission('files') && <ContextMenuItem onClick={() => launchById('files')}>Files</ContextMenuItem>}
69
+ {hasPermission('terminal') && (
70
+ <ContextMenuItem onClick={() => launchById('terminal')}>Open Terminal</ContextMenuItem>
71
+ )}
72
+ {(hasPermission('files') || hasPermission('terminal')) && hasPermission('settings') && <ContextMenuSeparator />}
73
+ {hasPermission('settings') && (
74
+ <ContextMenuItem onClick={() => launchById('settings')}>Settings</ContextMenuItem>
75
+ )}
76
+ </ContextMenuContent>
77
+ </ContextMenu>
78
+ );
79
+ }
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+
3
+ import { AnimatedBackground } from './AnimatedBackground';
4
+
5
+ /**
6
+ * Desktop background — renders the theme gradient + animated CSS orbs.
7
+ * The animated layer sits on top of the gradient but behind all windows and taskbar.
8
+ */
9
+ export function DesktopBackground() {
10
+ return (
11
+ <div className="fixed inset-0 z-0 pointer-events-none">
12
+ <div className="absolute inset-0" style={{ background: 'var(--khal-bg-desktop)' }} />
13
+ <AnimatedBackground />
14
+ </div>
15
+ );
16
+ }
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+
3
+ import { AppIcon } from '@/components/app-icon';
4
+ import type { DesktopEntry } from '@/types/desktop-entry';
5
+
6
+ interface DesktopIconProps {
7
+ entry: DesktopEntry;
8
+ onLaunch: () => void;
9
+ isMobile?: boolean;
10
+ }
11
+
12
+ export function DesktopIcon({ entry, onLaunch, isMobile }: DesktopIconProps) {
13
+ const iconSize = isMobile ? 36 : 32;
14
+
15
+ return (
16
+ <button
17
+ className={
18
+ isMobile
19
+ ? 'flex flex-col items-center gap-2 rounded-xl p-3 text-center transition-colors active:bg-[var(--khal-desktop-icon-hover)]'
20
+ : 'flex w-20 flex-col items-center gap-1.5 rounded-lg p-2 text-center transition-colors hover:bg-[var(--khal-desktop-icon-hover)] focus-visible:bg-[var(--khal-desktop-icon-hover)]'
21
+ }
22
+ onClick={isMobile ? onLaunch : undefined}
23
+ onDoubleClick={isMobile ? undefined : onLaunch}
24
+ onKeyDown={
25
+ isMobile
26
+ ? undefined
27
+ : (e) => {
28
+ if (e.key === 'Enter' || e.key === ' ') {
29
+ e.preventDefault();
30
+ onLaunch();
31
+ }
32
+ }
33
+ }
34
+ aria-label={`Open ${entry.name}`}
35
+ >
36
+ <div
37
+ className={
38
+ isMobile
39
+ ? 'flex h-14 w-14 items-center justify-center drop-shadow-lg'
40
+ : 'flex h-12 w-12 items-center justify-center drop-shadow-lg'
41
+ }
42
+ aria-hidden="true"
43
+ >
44
+ <AppIcon appId={entry.component || entry.id} size={iconSize} />
45
+ </div>
46
+ <span className="w-full truncate text-label-12 text-gray-1000 dark:text-gray-1000">{entry.name}</span>
47
+ </button>
48
+ );
49
+ }
@@ -0,0 +1,136 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+ import type { ShortcutCategory } from '@/lib/keyboard/types';
5
+ import { comboToSymbols } from '@/lib/keyboard/types';
6
+ import { useKeybindStore } from '@/stores/keybind-store';
7
+
8
+ interface ShortcutViewerProps {
9
+ visible: boolean;
10
+ onClose: () => void;
11
+ }
12
+
13
+ const CATEGORY_LABELS: Record<ShortcutCategory, string> = {
14
+ window: 'Window',
15
+ workspace: 'Workspace',
16
+ launcher: 'Launcher',
17
+ terminal: 'Terminal',
18
+ system: 'System',
19
+ };
20
+
21
+ const CATEGORY_ORDER: ShortcutCategory[] = ['window', 'launcher', 'terminal', 'system', 'workspace'];
22
+
23
+ export function ShortcutViewer({ visible, onClose }: ShortcutViewerProps) {
24
+ const { definitions, getBinding } = useKeybindStore();
25
+
26
+ useEffect(() => {
27
+ if (!visible) return;
28
+
29
+ const handleEscape = (e: KeyboardEvent) => {
30
+ if (e.key === 'Escape') {
31
+ e.preventDefault();
32
+ e.stopPropagation();
33
+ onClose();
34
+ }
35
+ };
36
+
37
+ window.addEventListener('keydown', handleEscape, { capture: true });
38
+ return () => window.removeEventListener('keydown', handleEscape, { capture: true });
39
+ }, [visible, onClose]);
40
+
41
+ if (!visible) return null;
42
+
43
+ // Group shortcuts by category
44
+ const byCategory = definitions.reduce(
45
+ (acc, def) => {
46
+ if (!acc[def.category]) acc[def.category] = [];
47
+ acc[def.category].push(def);
48
+ return acc;
49
+ },
50
+ {} as Record<ShortcutCategory, typeof definitions>
51
+ );
52
+
53
+ return (
54
+ <div
55
+ className="fixed inset-0 z-[10000] flex items-center justify-center backdrop-blur-sm"
56
+ style={{ background: 'rgba(0,0,0,0.5)' }}
57
+ onClick={onClose}
58
+ >
59
+ <div
60
+ className="max-h-[80vh] w-[700px] overflow-y-auto rounded-xl"
61
+ style={{
62
+ background: 'var(--khal-surface-raised)',
63
+ border: '1px solid var(--khal-border-default)',
64
+ boxShadow: 'var(--khal-shadow-xl)',
65
+ }}
66
+ onClick={(e) => e.stopPropagation()}
67
+ >
68
+ <div
69
+ className="sticky top-0 px-6 py-4"
70
+ style={{
71
+ background: 'var(--khal-surface-raised)',
72
+ borderBottom: '1px solid var(--khal-border-default)',
73
+ }}
74
+ >
75
+ <h2 className="text-copy-18 font-semibold" style={{ color: 'var(--khal-text-primary)' }}>
76
+ Keyboard Shortcuts
77
+ </h2>
78
+ <p className="text-copy-13" style={{ color: 'var(--khal-text-muted)' }}>
79
+ Press Esc to close
80
+ </p>
81
+ </div>
82
+
83
+ <div className="px-6 py-4">
84
+ {CATEGORY_ORDER.map((category) => {
85
+ const shortcuts = byCategory[category];
86
+ if (!shortcuts || shortcuts.length === 0) return null;
87
+
88
+ return (
89
+ <div key={category} className="mb-6 last:mb-0">
90
+ <h3 className="mb-3 text-copy-14 font-semibold" style={{ color: 'var(--khal-text-secondary)' }}>
91
+ {CATEGORY_LABELS[category]}
92
+ </h3>
93
+ <div className="space-y-2">
94
+ {shortcuts.map((def) => {
95
+ const binding = getBinding(def.id);
96
+ if (!binding) return null;
97
+
98
+ return (
99
+ <div
100
+ key={def.id}
101
+ className="flex items-center justify-between rounded-lg px-4 py-2.5"
102
+ style={{ background: 'var(--khal-surface-sunken)' }}
103
+ >
104
+ <div className="flex flex-col">
105
+ <span className="text-copy-13 font-medium" style={{ color: 'var(--khal-text-primary)' }}>
106
+ {def.label}
107
+ </span>
108
+ {def.description && (
109
+ <span className="text-copy-12" style={{ color: 'var(--khal-text-muted)' }}>
110
+ {def.description}
111
+ </span>
112
+ )}
113
+ </div>
114
+ <kbd
115
+ className="ml-4 flex-shrink-0 rounded-md px-3 py-1.5 font-mono text-copy-13"
116
+ style={{
117
+ background: 'var(--khal-surface-default)',
118
+ color: 'var(--khal-text-secondary)',
119
+ boxShadow: 'var(--khal-shadow-sm)',
120
+ border: '1px solid var(--khal-border-subtle)',
121
+ }}
122
+ >
123
+ {comboToSymbols(binding)}
124
+ </kbd>
125
+ </div>
126
+ );
127
+ })}
128
+ </div>
129
+ </div>
130
+ );
131
+ })}
132
+ </div>
133
+ </div>
134
+ </div>
135
+ );
136
+ }
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import { APP_COMPONENTS } from '@/components/apps/app-registry';
4
+ import { MobileWindowStack } from '@/components/window/MobileWindowStack';
5
+ import { Window } from '@/components/window/Window';
6
+ import { useIsMobile } from '@/lib/hooks/use-is-mobile';
7
+ import { useWindowStore } from '@/stores/window-store';
8
+
9
+ export function WindowRenderer() {
10
+ const windowsByWorkspace = useWindowStore((s) => s.windowsByWorkspace);
11
+ const activeWorkspaceId = useWindowStore((s) => s.activeWorkspaceId);
12
+ const windows = activeWorkspaceId ? windowsByWorkspace[activeWorkspaceId] || [] : [];
13
+ const isMobile = useIsMobile();
14
+
15
+ if (isMobile) {
16
+ return <MobileWindowStack windows={windows} />;
17
+ }
18
+
19
+ return (
20
+ <>
21
+ {windows.map((win) => {
22
+ const AppComponent = APP_COMPONENTS[win.appId];
23
+ if (AppComponent) {
24
+ return (
25
+ <Window key={win.id} window={win}>
26
+ <AppComponent windowId={win.id} meta={win.meta} />
27
+ </Window>
28
+ );
29
+ }
30
+ return null;
31
+ })}
32
+ </>
33
+ );
34
+ }
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { AppIconWithFallback } from '@/components/app-icon';
4
+ import type { WindowState } from '@/types/window';
5
+
6
+ interface WindowSwitcherProps {
7
+ visible: boolean;
8
+ windows: WindowState[];
9
+ selectedIndex: number;
10
+ }
11
+
12
+ function WindowSwitcherItem({ win, isSelected }: { win: WindowState; isSelected: boolean }) {
13
+ return (
14
+ <div
15
+ className={`flex flex-col items-center gap-2 rounded-lg px-3 py-3 transition-colors ${
16
+ isSelected ? 'bg-blue-600 text-white' : 'text-gray-1000'
17
+ }`}
18
+ style={{ width: 120 }}
19
+ >
20
+ <div className="flex h-12 w-12 items-center justify-center">
21
+ <AppIconWithFallback appId={win.appId} title={win.title} size={40} className="object-contain" />
22
+ </div>
23
+ <span className="w-full truncate text-center text-copy-13">{win.title}</span>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ export function WindowSwitcher({ visible, windows, selectedIndex }: WindowSwitcherProps) {
29
+ if (!visible || windows.length === 0) return null;
30
+
31
+ return (
32
+ <div className="fixed inset-0 z-[10000] flex items-center justify-center">
33
+ <div className="rounded-xl border border-gray-alpha-200 bg-background-200/90 p-3 shadow-2xl backdrop-blur-xl">
34
+ <div className="flex gap-1">
35
+ {windows.map((win, i) => (
36
+ <WindowSwitcherItem key={win.id} win={win} isSelected={i === selectedIndex} />
37
+ ))}
38
+ </div>
39
+ </div>
40
+ </div>
41
+ );
42
+ }