crewswarm 0.8.1-beta

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 (362) hide show
  1. package/.env.example +155 -0
  2. package/LICENSE +21 -0
  3. package/README.md +316 -0
  4. package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js +1 -0
  5. package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js.br +0 -0
  6. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +1 -0
  7. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  8. package/apps/dashboard/dist/assets/components-CSUb80ze.js +1 -0
  9. package/apps/dashboard/dist/assets/components-CSUb80ze.js.br +0 -0
  10. package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js +1 -0
  11. package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js.br +0 -0
  12. package/apps/dashboard/dist/assets/index-CF0aJRtC.css +1 -0
  13. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  14. package/apps/dashboard/dist/assets/index-Px49zu76.js +2 -0
  15. package/apps/dashboard/dist/assets/index-Px49zu76.js.br +0 -0
  16. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js +1 -0
  17. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
  18. package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js +1 -0
  19. package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js.br +0 -0
  20. package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js +1 -0
  21. package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js.br +0 -0
  22. package/apps/dashboard/dist/assets/tab-benchmarks-tab-DfCuAClu.js +1 -0
  23. package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js +1 -0
  24. package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js.br +0 -0
  25. package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js +1 -0
  26. package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js.br +0 -0
  27. package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js +1 -0
  28. package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js.br +0 -0
  29. package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js +1 -0
  30. package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js.br +0 -0
  31. package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js +1 -0
  32. package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js.br +0 -0
  33. package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js +1 -0
  34. package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js.br +0 -0
  35. package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js +1 -0
  36. package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js.br +0 -0
  37. package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js +1 -0
  38. package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js.br +0 -0
  39. package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js +1 -0
  40. package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js.br +0 -0
  41. package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js +1 -0
  42. package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js.br +0 -0
  43. package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js +1 -0
  44. package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js.br +0 -0
  45. package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js +1 -0
  46. package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js.br +0 -0
  47. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js +1 -0
  48. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js.br +0 -0
  49. package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js +1 -0
  50. package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js.br +0 -0
  51. package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js +1 -0
  52. package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js.br +0 -0
  53. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js +1 -0
  54. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
  55. package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js +1 -0
  56. package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js.br +0 -0
  57. package/apps/dashboard/dist/favicon.png +0 -0
  58. package/apps/dashboard/dist/index.html +6466 -0
  59. package/apps/dashboard/dist/index.html.br +0 -0
  60. package/apps/dashboard/dist/index.html.gz +0 -0
  61. package/apps/dashboard/dist/signup.html +446 -0
  62. package/apps/dashboard/index.html +6442 -0
  63. package/apps/dashboard/package.json +15 -0
  64. package/apps/dashboard/src/app.js +2823 -0
  65. package/apps/dashboard/src/app.js.br +0 -0
  66. package/apps/dashboard/src/app.js.gz +0 -0
  67. package/apps/dashboard/src/chat/chat-actions.js +1847 -0
  68. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  69. package/apps/dashboard/src/chat/unified-messages.js +327 -0
  70. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  71. package/apps/dashboard/src/cli-process.js +208 -0
  72. package/apps/dashboard/src/cli-process.js.br +0 -0
  73. package/apps/dashboard/src/cli-process.js.gz +0 -0
  74. package/apps/dashboard/src/components/active-tasks-panel.js +175 -0
  75. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  76. package/apps/dashboard/src/core/api.js +18 -0
  77. package/apps/dashboard/src/core/api.js.br +0 -0
  78. package/apps/dashboard/src/core/dom.js +220 -0
  79. package/apps/dashboard/src/core/dom.js.br +0 -0
  80. package/apps/dashboard/src/core/state.js +91 -0
  81. package/apps/dashboard/src/core/state.js.br +0 -0
  82. package/apps/dashboard/src/core/task-manager.js +134 -0
  83. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  84. package/apps/dashboard/src/orchestration-status.js +127 -0
  85. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  86. package/apps/dashboard/src/setup-wizard.js +555 -0
  87. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  88. package/apps/dashboard/src/styles.css +2085 -0
  89. package/apps/dashboard/src/styles.css.br +0 -0
  90. package/apps/dashboard/src/styles.css.gz +0 -0
  91. package/apps/dashboard/src/tabs/agents-tab.js +2237 -0
  92. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  93. package/apps/dashboard/src/tabs/benchmarks-tab.js +229 -0
  94. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  95. package/apps/dashboard/src/tabs/comms-tab.js +955 -0
  96. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  97. package/apps/dashboard/src/tabs/contacts-tab.js +654 -0
  98. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  99. package/apps/dashboard/src/tabs/engines-tab.js +175 -0
  100. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  101. package/apps/dashboard/src/tabs/memory-tab.js +182 -0
  102. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  103. package/apps/dashboard/src/tabs/models-tab.js +441 -0
  104. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  105. package/apps/dashboard/src/tabs/pm-loop-tab.js +185 -0
  106. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  107. package/apps/dashboard/src/tabs/projects-tab.js +663 -0
  108. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  109. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  110. package/apps/dashboard/src/tabs/prompts-tab.js +160 -0
  111. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  112. package/apps/dashboard/src/tabs/services-tab.js +202 -0
  113. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  114. package/apps/dashboard/src/tabs/settings-tab.js +803 -0
  115. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  116. package/apps/dashboard/src/tabs/skills-tab.js +284 -0
  117. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  118. package/apps/dashboard/src/tabs/spending-tab.js +173 -0
  119. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  120. package/apps/dashboard/src/tabs/swarm-chat-tab.js +660 -0
  121. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  122. package/apps/dashboard/src/tabs/swarm-tab.js +538 -0
  123. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  124. package/apps/dashboard/src/tabs/usage-tab.js +390 -0
  125. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  126. package/apps/dashboard/src/tabs/waves-tab.js +238 -0
  127. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  128. package/apps/dashboard/src/tabs/workflows-tab.js +747 -0
  129. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  130. package/apps/vibe/.crew/agent-memory/pipeline.json +249 -0
  131. package/apps/vibe/.crew/cost.json +17 -0
  132. package/apps/vibe/.crew/json-parse-metrics.jsonl +22 -0
  133. package/apps/vibe/.crew/pipeline-metrics.jsonl +22 -0
  134. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +5 -0
  135. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +5 -0
  136. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +1 -0
  137. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +5 -0
  138. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +5 -0
  139. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +5 -0
  140. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +5 -0
  141. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +5 -0
  142. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +5 -0
  143. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +5 -0
  144. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +5 -0
  145. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +5 -0
  146. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +5 -0
  147. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +5 -0
  148. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +5 -0
  149. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +5 -0
  150. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +5 -0
  151. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +5 -0
  152. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +2 -0
  153. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +5 -0
  154. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +5 -0
  155. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +5 -0
  156. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +5 -0
  157. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +5 -0
  158. package/apps/vibe/.crew/sandbox.json +7 -0
  159. package/apps/vibe/.crew/session.json +285 -0
  160. package/apps/vibe/.crew/training-data.jsonl +0 -0
  161. package/apps/vibe/.github/workflows/studio-quality.yml +37 -0
  162. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +12 -0
  163. package/apps/vibe/.studio-data/project-messages/general.jsonl +54 -0
  164. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +10 -0
  165. package/apps/vibe/ARCHITECTURE.md +3393 -0
  166. package/apps/vibe/QUICK-REFERENCE.md +211 -0
  167. package/apps/vibe/README.md +76 -0
  168. package/apps/vibe/ROADMAP.md +41 -0
  169. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +35 -0
  170. package/apps/vibe/VISUAL-GUIDE.md +378 -0
  171. package/apps/vibe/capture-demo.mjs +160 -0
  172. package/apps/vibe/capture-vibe-assets.mjs +71 -0
  173. package/apps/vibe/capture-vibe-video.mjs +260 -0
  174. package/apps/vibe/check-buttons.js +41 -0
  175. package/apps/vibe/diagnose.html +106 -0
  176. package/apps/vibe/fix-buttons.js +103 -0
  177. package/apps/vibe/index.html +3401 -0
  178. package/apps/vibe/package-lock.json +920 -0
  179. package/apps/vibe/package.json +31 -0
  180. package/apps/vibe/public/favicon.png +0 -0
  181. package/apps/vibe/scripts/studio-pty-host.py +117 -0
  182. package/apps/vibe/server.mjs +1835 -0
  183. package/apps/vibe/src/main.js +2846 -0
  184. package/apps/vibe/src/register-all-languages.js +98 -0
  185. package/apps/vibe/start-studio.sh +11 -0
  186. package/apps/vibe/test/accessibility-tests.js +77 -0
  187. package/apps/vibe/test/browser-performance-audit.mjs +205 -0
  188. package/apps/vibe/test/performance-tests.js +120 -0
  189. package/apps/vibe/test/security-tests.js +213 -0
  190. package/apps/vibe/tests/e2e.local.mjs +54 -0
  191. package/apps/vibe/tests/server.smoke.mjs +106 -0
  192. package/apps/vibe/update_website.mjs +74 -0
  193. package/apps/vibe/vite.config.js +19 -0
  194. package/apps/vibe/watch-server.mjs +108 -0
  195. package/contrib/openclaw-plugin/README.md +199 -0
  196. package/contrib/openclaw-plugin/index.ts +306 -0
  197. package/contrib/openclaw-plugin/openclaw.plugin.json +41 -0
  198. package/contrib/openclaw-plugin/package.json +27 -0
  199. package/contrib/openclaw-plugin/skills/crewswarm/SKILL.md +88 -0
  200. package/crew-lead.mjs +649 -0
  201. package/engines/claude-code.json +36 -0
  202. package/engines/codex.json +37 -0
  203. package/engines/crew-cli.json +42 -0
  204. package/engines/cursor.json +40 -0
  205. package/engines/docker-sandbox.json +38 -0
  206. package/engines/gemini-cli.json +75 -0
  207. package/engines/opencode.json +31 -0
  208. package/gateway-bridge.mjs +1575 -0
  209. package/install.sh +738 -0
  210. package/lib/agent-registry.mjs +232 -0
  211. package/lib/agents/daemon.mjs +121 -0
  212. package/lib/agents/dispatch.mjs +225 -0
  213. package/lib/agents/permissions.mjs +90 -0
  214. package/lib/agents/platform-formatting.mjs +102 -0
  215. package/lib/agents/registry.mjs +81 -0
  216. package/lib/agents/tool-instructions.mjs +257 -0
  217. package/lib/agents/validation.mjs +75 -0
  218. package/lib/approval/policy-manager.mjs +221 -0
  219. package/lib/autoharness/index.mjs +391 -0
  220. package/lib/bridges/cli-executor.mjs +332 -0
  221. package/lib/bridges/gateway-ws.mjs +345 -0
  222. package/lib/bridges/integration.mjs +229 -0
  223. package/lib/bridges/rag-helper.mjs +90 -0
  224. package/lib/browser/opencode-passthrough-filter.js +44 -0
  225. package/lib/browser/passthrough-stderr.js +109 -0
  226. package/lib/chat/autonomous-mentions.mjs +373 -0
  227. package/lib/chat/history.mjs +82 -0
  228. package/lib/chat/mention-routing-intent.mjs +136 -0
  229. package/lib/chat/participants.mjs +95 -0
  230. package/lib/chat/project-messages-rag.mjs +265 -0
  231. package/lib/chat/project-messages.mjs +479 -0
  232. package/lib/chat/shared-chat-prompt-overlay.mjs +52 -0
  233. package/lib/chat/thread-binding.mjs +34 -0
  234. package/lib/chat/unified-history.mjs +223 -0
  235. package/lib/chat/unified-wrapper.mjs +41 -0
  236. package/lib/cli-process-tracker.mjs +228 -0
  237. package/lib/collections/index.mjs +433 -0
  238. package/lib/contacts/identity-linker.mjs +248 -0
  239. package/lib/contacts/index.mjs +341 -0
  240. package/lib/crew-judge/PROMPT.md +93 -0
  241. package/lib/crew-judge/judge.mjs +260 -0
  242. package/lib/crew-lead/agent-manager.mjs +125 -0
  243. package/lib/crew-lead/background.mjs +270 -0
  244. package/lib/crew-lead/brain.mjs +110 -0
  245. package/lib/crew-lead/chat-handler.mjs +2603 -0
  246. package/lib/crew-lead/chat-handler.mjs.bak +1274 -0
  247. package/lib/crew-lead/classifier.mjs +83 -0
  248. package/lib/crew-lead/http-server.mjs +4824 -0
  249. package/lib/crew-lead/intent.mjs +102 -0
  250. package/lib/crew-lead/interval-manager.mjs +41 -0
  251. package/lib/crew-lead/llm-caller.mjs +544 -0
  252. package/lib/crew-lead/prompts.mjs +392 -0
  253. package/lib/crew-lead/retry-manager.mjs +118 -0
  254. package/lib/crew-lead/tools.mjs +318 -0
  255. package/lib/crew-lead/wave-dispatcher.mjs +798 -0
  256. package/lib/crew-lead/waves-config.json +73 -0
  257. package/lib/crew-lead/waves-loader.mjs +110 -0
  258. package/lib/crew-lead/ws-router.mjs +428 -0
  259. package/lib/dispatch/parsers.mjs +299 -0
  260. package/lib/domain-planning/detector.mjs +196 -0
  261. package/lib/domain-planning/prompts/crew-pm-cli.md +96 -0
  262. package/lib/domain-planning/prompts/crew-pm-core.md +122 -0
  263. package/lib/domain-planning/prompts/crew-pm-frontend.md +111 -0
  264. package/lib/engines/crew-cli-sandbox.mjs +422 -0
  265. package/lib/engines/crew-cli.mjs +155 -0
  266. package/lib/engines/cursor-launcher.mjs +110 -0
  267. package/lib/engines/engine-registry.mjs +253 -0
  268. package/lib/engines/llm-direct.mjs +184 -0
  269. package/lib/engines/opencode.mjs +256 -0
  270. package/lib/engines/ouroboros.mjs +114 -0
  271. package/lib/engines/rt-envelope.mjs +1643 -0
  272. package/lib/engines/rt-envelope.mjs.backup-current +870 -0
  273. package/lib/engines/runners.mjs +1367 -0
  274. package/lib/gemini-cli-passthrough-noise.mjs +37 -0
  275. package/lib/integrations/code-search.mjs +259 -0
  276. package/lib/integrations/greptile.mjs +148 -0
  277. package/lib/integrations/multimodal.mjs +313 -0
  278. package/lib/integrations/telegram-streaming.mjs +153 -0
  279. package/lib/integrations/tts.mjs +312 -0
  280. package/lib/integrations/twitter-links.mjs +294 -0
  281. package/lib/memory/shared-adapter.mjs +296 -0
  282. package/lib/pipeline/manager.mjs +539 -0
  283. package/lib/preferences/extractor.mjs +347 -0
  284. package/lib/project-dir.mjs +20 -0
  285. package/lib/runtime/config.mjs +388 -0
  286. package/lib/runtime/dlq.mjs +170 -0
  287. package/lib/runtime/log-rotation.mjs +82 -0
  288. package/lib/runtime/logger.mjs +58 -0
  289. package/lib/runtime/memory.mjs +421 -0
  290. package/lib/runtime/paths.mjs +76 -0
  291. package/lib/runtime/project-dir.mjs +127 -0
  292. package/lib/runtime/spending.mjs +204 -0
  293. package/lib/runtime/startup-guard.mjs +291 -0
  294. package/lib/runtime/task-lease.mjs +234 -0
  295. package/lib/runtime/telemetry-schema.mjs +208 -0
  296. package/lib/runtime/telemetry.mjs +101 -0
  297. package/lib/runtime/utils.mjs +64 -0
  298. package/lib/skills/index.mjs +265 -0
  299. package/lib/tools/browser.mjs +135 -0
  300. package/lib/tools/executor.mjs +913 -0
  301. package/lib/types.d.ts +57 -0
  302. package/package.json +106 -0
  303. package/pm-loop.mjs +1626 -0
  304. package/prompts/coder-back.md +27 -0
  305. package/prompts/coder-front.md +27 -0
  306. package/prompts/coder.md +28 -0
  307. package/prompts/copywriter.md +17 -0
  308. package/prompts/fixer.md +39 -0
  309. package/prompts/frontend.md +23 -0
  310. package/prompts/github.md +24 -0
  311. package/prompts/main.md +39 -0
  312. package/prompts/pm-cli.md +95 -0
  313. package/prompts/pm-core.md +121 -0
  314. package/prompts/pm-frontend.md +110 -0
  315. package/prompts/pm.md +234 -0
  316. package/prompts/qa.md +44 -0
  317. package/prompts/security.md +19 -0
  318. package/scripts/build-crew-chat.sh +28 -0
  319. package/scripts/build-llms-full.mjs +52 -0
  320. package/scripts/chatmock-login.sh +16 -0
  321. package/scripts/chatmock-serve.sh +16 -0
  322. package/scripts/check-dashboard.mjs +88 -0
  323. package/scripts/crew-scribe.mjs +326 -0
  324. package/scripts/dashboard-helpers.mjs +391 -0
  325. package/scripts/dashboard-validation.mjs +198 -0
  326. package/scripts/dashboard.mjs +9717 -0
  327. package/scripts/dlq-replay.mjs +61 -0
  328. package/scripts/doctor.mjs +196 -0
  329. package/scripts/file-lock.mjs +186 -0
  330. package/scripts/fresh-machine-smoke.sh +323 -0
  331. package/scripts/generate-changelog.mjs +227 -0
  332. package/scripts/generate-openapi.mjs +334 -0
  333. package/scripts/health-check.mjs +229 -0
  334. package/scripts/install-docker.sh +213 -0
  335. package/scripts/mcp-server.mjs +1625 -0
  336. package/scripts/opencrew-rt-daemon.mjs +568 -0
  337. package/scripts/openswitchctl +646 -0
  338. package/scripts/refactor-configs.mjs +39 -0
  339. package/scripts/release-check.sh +46 -0
  340. package/scripts/resolve-node-bin.sh +25 -0
  341. package/scripts/restart-all-from-repo.sh +329 -0
  342. package/scripts/restart-crew-lead.sh +98 -0
  343. package/scripts/restart-dashboard.sh +104 -0
  344. package/scripts/restart-service.sh +274 -0
  345. package/scripts/run-accessibility-audit.mjs +356 -0
  346. package/scripts/run-integration-bounded.mjs +188 -0
  347. package/scripts/run-scheduled-pipeline.mjs +230 -0
  348. package/scripts/run.mjs +41 -0
  349. package/scripts/scan-skills.mjs +79 -0
  350. package/scripts/setup-firewall.sh +128 -0
  351. package/scripts/smoke-dispatch.mjs +149 -0
  352. package/scripts/smoke.sh +163 -0
  353. package/scripts/start-crew.mjs +328 -0
  354. package/scripts/start.mjs +146 -0
  355. package/scripts/swiftbar-restart-service.sh +19 -0
  356. package/scripts/sync-agents.mjs +152 -0
  357. package/scripts/sync-prompts.mjs +79 -0
  358. package/scripts/validate-config.mjs +337 -0
  359. package/scripts/wow.mjs +89 -0
  360. package/telegram-bridge.mjs +2421 -0
  361. package/unified-orchestrator.mjs +519 -0
  362. package/whatsapp-bridge.mjs +1481 -0
@@ -0,0 +1,3393 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>crewswarm vibe</title>
7
+ <link rel="icon" type="image/png" href="./favicon.png" />
8
+ <script>
9
+ (() => {
10
+ const root = document.documentElement;
11
+ const storedTheme = localStorage.getItem("theme");
12
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
13
+ const resolvedTheme =
14
+ storedTheme === "light" || storedTheme === "dark"
15
+ ? storedTheme
16
+ : prefersDark
17
+ ? "dark"
18
+ : "light";
19
+
20
+ root.classList.toggle("dark", resolvedTheme === "dark");
21
+ root.setAttribute("data-theme", storedTheme || "system");
22
+ root.style.colorScheme = resolvedTheme;
23
+ })();
24
+ </script>
25
+ <!-- System font stack only to avoid CORS when dashboard (4319) and studio (3333) both load Inter from Google -->
26
+ <style>
27
+ /* crewswarm Brand Colors (from dashboard) */
28
+ :root {
29
+ /* Backgrounds */
30
+ --bg: #060a10;
31
+ --bg-0: #040810;
32
+ --bg-1: #0d1420;
33
+ --bg-2: #111827;
34
+ --bg-card: #0d1420;
35
+ --bg-card2: #111827;
36
+ --bg-hover: #141e2e;
37
+ --surface-2:#16202e;
38
+
39
+ /* Borders */
40
+ --border: rgba(255,255,255,0.07);
41
+ --border-hi:rgba(56,189,248,0.35);
42
+
43
+ /* Text */
44
+ --text: #f0f6ff;
45
+ --text-1: #f0f6ff;
46
+ --text-2: #8b9db3;
47
+ --text-3: #4a5568;
48
+
49
+ /* Accent */
50
+ --accent: #38bdf8;
51
+ --accent2: #818cf8;
52
+ --purple: #818cf8;
53
+
54
+ /* Semantic colors */
55
+ --green: #34d399;
56
+ --red: #f87171;
57
+ --yellow: #fbbf24;
58
+ --amber: #f59e0b;
59
+ --green-hi: #22c55e;
60
+ --red-hi: #ef4444;
61
+ --sky: #38bdf8;
62
+ --blue: #4a7ab5;
63
+ --teal: #0f766e;
64
+ --warning: #f59e0b;
65
+ --radius: 10px;
66
+ }
67
+
68
+ html {
69
+ color-scheme: dark;
70
+ }
71
+
72
+ html.dark {
73
+ color-scheme: dark;
74
+ }
75
+
76
+ html:not(.dark) {
77
+ color-scheme: light;
78
+ --bg: #f4f7fb;
79
+ --bg-0: #eef3f9;
80
+ --bg-1: #f8fbff;
81
+ --bg-2: #ffffff;
82
+ --bg-card: rgba(255, 255, 255, 0.96);
83
+ --bg-card2: #ffffff;
84
+ --bg-hover: #eef4fb;
85
+ --surface-2:#e6edf6;
86
+ --border: rgba(15, 23, 42, 0.10);
87
+ --border-hi:rgba(14, 165, 233, 0.30);
88
+ --text: #0f172a;
89
+ --text-1: #0f172a;
90
+ --text-2: #475569;
91
+ --text-3: #64748b;
92
+ --accent: #0ea5e9;
93
+ --accent2: #4f46e5;
94
+ --purple: #4f46e5;
95
+ --green: #10b981;
96
+ --red: #ef4444;
97
+ --yellow: #f59e0b;
98
+ --amber: #d97706;
99
+ --green-hi: #059669;
100
+ --red-hi: #dc2626;
101
+ --sky: #0284c7;
102
+ --blue: #2563eb;
103
+ --teal: #0f766e;
104
+ --warning: #d97706;
105
+ }
106
+
107
+ * {
108
+ margin: 0;
109
+ padding: 0;
110
+ box-sizing: border-box;
111
+ }
112
+
113
+ body {
114
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
115
+ background: var(--bg);
116
+ color: var(--text);
117
+ overflow: hidden;
118
+ height: 100vh;
119
+ transition: background-color 0.2s ease, color 0.2s ease;
120
+ }
121
+
122
+ #app {
123
+ display: grid;
124
+ --sidebar-width: 250px;
125
+ --chat-width: 400px;
126
+ --output-height: 220px;
127
+ grid-template-columns: var(--sidebar-width) 8px minmax(100px, 1fr) 8px var(--chat-width);
128
+ grid-template-rows: 50px minmax(100px, 1fr) 8px var(--output-height) 30px;
129
+ height: 100vh;
130
+ overflow: hidden;
131
+ }
132
+
133
+ /* Titlebar */
134
+ #titlebar {
135
+ grid-column: 1 / -1;
136
+ background: var(--bg-card);
137
+ display: flex;
138
+ align-items: center;
139
+ padding: 0 20px;
140
+ border-bottom: 1px solid var(--border);
141
+ gap: 12px;
142
+ }
143
+
144
+ #titlebar .brand-section {
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 10px;
148
+ }
149
+
150
+ #titlebar .brand-icon {
151
+ width: 24px;
152
+ height: 24px;
153
+ object-fit: contain;
154
+ }
155
+
156
+ #titlebar .brand-name {
157
+ font-size: 15px;
158
+ font-weight: 800;
159
+ color: var(--text);
160
+ letter-spacing: 0.06em;
161
+ text-transform: uppercase;
162
+ }
163
+
164
+ #titlebar .brand-name span {
165
+ color: var(--accent);
166
+ }
167
+
168
+ #titlebar .project-picker {
169
+ margin-left: 20px;
170
+ display: flex;
171
+ align-items: center;
172
+ gap: 8px;
173
+ }
174
+
175
+ #titlebar select {
176
+ background: var(--bg-2);
177
+ border: 1px solid var(--border);
178
+ color: var(--text-2);
179
+ padding: 6px 12px;
180
+ border-radius: 6px;
181
+ font-size: 13px;
182
+ font-family: inherit;
183
+ cursor: pointer;
184
+ outline: none;
185
+ }
186
+
187
+ #titlebar select:focus {
188
+ border-color: var(--accent);
189
+ }
190
+
191
+ #titlebar .status-indicator {
192
+ margin-left: auto;
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 6px;
196
+ font-size: 12px;
197
+ color: var(--text-3);
198
+ }
199
+
200
+ #titlebar .status-dot {
201
+ width: 8px;
202
+ height: 8px;
203
+ border-radius: 50%;
204
+ background: var(--green);
205
+ }
206
+
207
+ #titlebar .titlebar-actions {
208
+ display: flex;
209
+ align-items: center;
210
+ gap: 10px;
211
+ margin-left: 10px;
212
+ }
213
+
214
+ .icon-btn {
215
+ display: inline-flex;
216
+ align-items: center;
217
+ justify-content: center;
218
+ gap: 6px;
219
+ min-width: 34px;
220
+ min-height: 34px;
221
+ padding: 0 10px;
222
+ border-radius: 10px;
223
+ border: 1px solid var(--border);
224
+ background: linear-gradient(180deg, rgba(17, 24, 39, 0.96), rgba(13, 20, 32, 0.96));
225
+ color: var(--text-2);
226
+ cursor: pointer;
227
+ transition: border-color 0.18s ease, transform 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
228
+ }
229
+
230
+ .icon-btn:hover,
231
+ .icon-btn:focus-visible {
232
+ border-color: var(--border-hi);
233
+ color: var(--text);
234
+ box-shadow: 0 10px 30px rgba(56, 189, 248, 0.12);
235
+ transform: translateY(-1px);
236
+ outline: none;
237
+ }
238
+
239
+ .icon-btn .icon-btn-label {
240
+ font-size: 12px;
241
+ font-weight: 600;
242
+ letter-spacing: 0.01em;
243
+ }
244
+
245
+ .theme-toggle {
246
+ min-width: 42px;
247
+ padding: 0 12px;
248
+ }
249
+
250
+ .theme-toggle-icon {
251
+ position: relative;
252
+ width: 18px;
253
+ height: 18px;
254
+ display: inline-flex;
255
+ align-items: center;
256
+ justify-content: center;
257
+ color: currentColor;
258
+ }
259
+
260
+ .theme-toggle-icon svg {
261
+ position: absolute;
262
+ inset: 0;
263
+ width: 18px;
264
+ height: 18px;
265
+ fill: none;
266
+ stroke: currentColor;
267
+ stroke-width: 1.8;
268
+ stroke-linecap: round;
269
+ stroke-linejoin: round;
270
+ transition: opacity 0.2s ease, transform 0.2s ease;
271
+ }
272
+
273
+ .theme-toggle-icon .moon-icon {
274
+ opacity: 0;
275
+ transform: scale(0.8) rotate(-18deg);
276
+ }
277
+
278
+ html.dark .theme-toggle-icon .sun-icon {
279
+ opacity: 0;
280
+ transform: scale(0.8) rotate(18deg);
281
+ }
282
+
283
+ html.dark .theme-toggle-icon .moon-icon {
284
+ opacity: 1;
285
+ transform: scale(1) rotate(0deg);
286
+ }
287
+
288
+ html:not(.dark) .theme-toggle-icon .sun-icon {
289
+ opacity: 1;
290
+ transform: scale(1) rotate(0deg);
291
+ }
292
+
293
+ html:not(.dark) .theme-toggle-icon .moon-icon {
294
+ opacity: 0;
295
+ transform: scale(0.8) rotate(-18deg);
296
+ }
297
+
298
+ html:not(.dark) .icon-btn {
299
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(241, 245, 249, 0.98));
300
+ box-shadow: 0 6px 18px rgba(148, 163, 184, 0.12);
301
+ }
302
+
303
+ #settings-panel {
304
+ position: fixed;
305
+ top: 66px;
306
+ right: 24px;
307
+ width: min(360px, calc(100vw - 32px));
308
+ padding: 18px;
309
+ border-radius: 18px;
310
+ border: 1px solid rgba(56, 189, 248, 0.18);
311
+ background:
312
+ radial-gradient(circle at top right, rgba(56, 189, 248, 0.14), transparent 42%),
313
+ linear-gradient(180deg, rgba(13, 20, 32, 0.98), rgba(6, 10, 16, 0.98));
314
+ box-shadow: 0 28px 80px rgba(2, 8, 20, 0.55);
315
+ backdrop-filter: blur(18px);
316
+ z-index: 950;
317
+ opacity: 0;
318
+ visibility: hidden;
319
+ transform: translateY(-10px) scale(0.98);
320
+ transform-origin: top right;
321
+ transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
322
+ }
323
+
324
+ #settings-panel.visible {
325
+ opacity: 1;
326
+ visibility: visible;
327
+ transform: translateY(0) scale(1);
328
+ }
329
+
330
+ html:not(.dark) #settings-panel {
331
+ border-color: rgba(14, 165, 233, 0.18);
332
+ background:
333
+ radial-gradient(circle at top right, rgba(14, 165, 233, 0.12), transparent 42%),
334
+ linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(241, 245, 249, 0.98));
335
+ box-shadow: 0 28px 80px rgba(148, 163, 184, 0.24);
336
+ }
337
+
338
+ .settings-panel-header {
339
+ display: flex;
340
+ align-items: flex-start;
341
+ justify-content: space-between;
342
+ gap: 16px;
343
+ margin-bottom: 18px;
344
+ }
345
+
346
+ .settings-panel-header h3 {
347
+ font-size: 15px;
348
+ font-weight: 700;
349
+ color: var(--text);
350
+ margin-bottom: 4px;
351
+ }
352
+
353
+ .settings-panel-header p {
354
+ font-size: 12px;
355
+ line-height: 1.5;
356
+ color: var(--text-2);
357
+ }
358
+
359
+ .settings-panel-close {
360
+ width: 34px;
361
+ height: 34px;
362
+ padding: 0;
363
+ }
364
+
365
+ .settings-panel-section {
366
+ padding: 14px;
367
+ border-radius: 14px;
368
+ border: 1px solid rgba(255, 255, 255, 0.06);
369
+ background: rgba(17, 24, 39, 0.76);
370
+ }
371
+
372
+ .settings-panel-section-label {
373
+ display: block;
374
+ margin-bottom: 6px;
375
+ font-size: 11px;
376
+ font-weight: 700;
377
+ letter-spacing: 0.08em;
378
+ text-transform: uppercase;
379
+ color: var(--text-3);
380
+ }
381
+
382
+ .settings-panel-section strong {
383
+ display: block;
384
+ font-size: 14px;
385
+ color: var(--text);
386
+ margin-bottom: 6px;
387
+ }
388
+
389
+ .settings-panel-section p {
390
+ font-size: 12px;
391
+ line-height: 1.6;
392
+ color: var(--text-2);
393
+ }
394
+
395
+ .settings-dashboard-link {
396
+ display: inline-flex;
397
+ align-items: center;
398
+ justify-content: center;
399
+ gap: 8px;
400
+ width: 100%;
401
+ margin-top: 16px;
402
+ padding: 12px 14px;
403
+ border-radius: 12px;
404
+ border: 1px solid rgba(56, 189, 248, 0.34);
405
+ background: linear-gradient(135deg, rgba(56, 189, 248, 0.18), rgba(129, 140, 248, 0.16));
406
+ color: var(--text);
407
+ font-size: 13px;
408
+ font-weight: 700;
409
+ text-decoration: none;
410
+ transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
411
+ }
412
+
413
+ .settings-dashboard-link:hover,
414
+ .settings-dashboard-link:focus-visible {
415
+ transform: translateY(-1px);
416
+ box-shadow: 0 16px 40px rgba(56, 189, 248, 0.18);
417
+ border-color: rgba(56, 189, 248, 0.5);
418
+ outline: none;
419
+ }
420
+
421
+ #shortcuts-panel {
422
+ position: fixed;
423
+ top: 66px;
424
+ left: 24px;
425
+ width: min(380px, calc(100vw - 32px));
426
+ max-height: min(76vh, 720px);
427
+ padding: 18px;
428
+ border-radius: 20px;
429
+ border: 1px solid rgba(56, 189, 248, 0.2);
430
+ background:
431
+ radial-gradient(circle at top left, rgba(56, 189, 248, 0.18), transparent 38%),
432
+ linear-gradient(180deg, rgba(13, 20, 32, 0.98), rgba(6, 10, 16, 0.98));
433
+ box-shadow: 0 28px 80px rgba(2, 8, 20, 0.5);
434
+ backdrop-filter: blur(18px);
435
+ z-index: 960;
436
+ overflow: auto;
437
+ pointer-events: auto;
438
+ transform-origin: top left;
439
+ transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
440
+ }
441
+
442
+ #shortcuts-panel:not(.hidden) {
443
+ opacity: 1;
444
+ visibility: visible;
445
+ transform: translateY(0) scale(1);
446
+ }
447
+
448
+ html:not(.dark) #shortcuts-panel {
449
+ border-color: rgba(14, 165, 233, 0.2);
450
+ background:
451
+ radial-gradient(circle at top left, rgba(14, 165, 233, 0.14), transparent 38%),
452
+ linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(241, 245, 249, 0.98));
453
+ box-shadow: 0 28px 80px rgba(148, 163, 184, 0.2);
454
+ }
455
+
456
+ #shortcuts-panel.hidden {
457
+ opacity: 0;
458
+ visibility: hidden;
459
+ pointer-events: none;
460
+ transform: translateY(-10px) scale(0.98);
461
+ }
462
+
463
+
464
+
465
+ .shortcuts-panel-header {
466
+ display: flex;
467
+ align-items: flex-start;
468
+ justify-content: space-between;
469
+ gap: 16px;
470
+ margin-bottom: 16px;
471
+ }
472
+
473
+ .shortcuts-panel-header h3 {
474
+ font-size: 16px;
475
+ font-weight: 800;
476
+ color: var(--text);
477
+ margin-bottom: 6px;
478
+ }
479
+
480
+ .shortcuts-panel-header p {
481
+ font-size: 12px;
482
+ line-height: 1.55;
483
+ color: var(--text-2);
484
+ }
485
+
486
+ .shortcuts-panel-close {
487
+ width: 34px;
488
+ height: 34px;
489
+ padding: 0;
490
+ flex-shrink: 0;
491
+ position: relative;
492
+ z-index: 1;
493
+ }
494
+
495
+ .shortcuts-callout {
496
+ display: flex;
497
+ align-items: center;
498
+ gap: 10px;
499
+ margin-bottom: 16px;
500
+ padding: 12px 14px;
501
+ border-radius: 14px;
502
+ border: 1px solid rgba(56, 189, 248, 0.24);
503
+ background: rgba(56, 189, 248, 0.08);
504
+ color: var(--text);
505
+ font-size: 12px;
506
+ line-height: 1.5;
507
+ }
508
+
509
+ .shortcuts-callout strong {
510
+ color: var(--accent);
511
+ white-space: nowrap;
512
+ }
513
+
514
+ .shortcuts-grid {
515
+ display: grid;
516
+ grid-template-columns: repeat(2, minmax(0, 1fr));
517
+ gap: 10px;
518
+ margin-bottom: 18px;
519
+ }
520
+
521
+ .shortcut-card {
522
+ padding: 12px;
523
+ border-radius: 14px;
524
+ border: 1px solid var(--border);
525
+ background: rgba(17, 24, 39, 0.7);
526
+ }
527
+
528
+ html:not(.dark) .shortcut-card {
529
+ background: rgba(255, 255, 255, 0.8);
530
+ }
531
+
532
+ .shortcut-card kbd {
533
+ display: inline-flex;
534
+ align-items: center;
535
+ justify-content: center;
536
+ min-width: 32px;
537
+ padding: 4px 8px;
538
+ border-radius: 9px;
539
+ border: 1px solid rgba(56, 189, 248, 0.22);
540
+ background: rgba(56, 189, 248, 0.1);
541
+ color: var(--text);
542
+ font-size: 11px;
543
+ font-weight: 700;
544
+ letter-spacing: 0.04em;
545
+ text-transform: uppercase;
546
+ box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.05);
547
+ }
548
+
549
+ .shortcut-card kbd + kbd {
550
+ margin-left: 6px;
551
+ }
552
+
553
+ .shortcut-card strong {
554
+ display: block;
555
+ margin-top: 10px;
556
+ margin-bottom: 4px;
557
+ font-size: 13px;
558
+ color: var(--text);
559
+ }
560
+
561
+ .shortcut-card p {
562
+ font-size: 12px;
563
+ line-height: 1.5;
564
+ color: var(--text-2);
565
+ }
566
+
567
+ .shortcuts-practices {
568
+ padding: 14px;
569
+ border-radius: 16px;
570
+ border: 1px solid var(--border);
571
+ background: rgba(17, 24, 39, 0.56);
572
+ }
573
+
574
+ html:not(.dark) .shortcuts-practices {
575
+ background: rgba(255, 255, 255, 0.74);
576
+ }
577
+
578
+ .shortcuts-practices h4 {
579
+ font-size: 12px;
580
+ font-weight: 800;
581
+ letter-spacing: 0.08em;
582
+ text-transform: uppercase;
583
+ color: var(--text-3);
584
+ margin-bottom: 10px;
585
+ }
586
+
587
+ .shortcuts-practices ul {
588
+ list-style: none;
589
+ display: grid;
590
+ gap: 8px;
591
+ }
592
+
593
+ .shortcuts-practices li {
594
+ font-size: 12px;
595
+ line-height: 1.55;
596
+ color: var(--text-2);
597
+ padding-left: 14px;
598
+ position: relative;
599
+ }
600
+
601
+ .shortcuts-practices li::before {
602
+ content: "";
603
+ position: absolute;
604
+ left: 0;
605
+ top: 0.55em;
606
+ width: 6px;
607
+ height: 6px;
608
+ border-radius: 999px;
609
+ background: var(--accent);
610
+ box-shadow: 0 0 0 4px rgba(56, 189, 248, 0.12);
611
+ }
612
+
613
+ /* Sidebar - File Tree */
614
+ #sidebar {
615
+ grid-row: 2;
616
+ grid-column: 1;
617
+ background: var(--bg-card);
618
+ border-right: 1px solid var(--border);
619
+ overflow-y: auto;
620
+ padding: 12px 0;
621
+ min-width: 0;
622
+ }
623
+
624
+ #sidebar h3 {
625
+ font-size: 11px;
626
+ text-transform: uppercase;
627
+ color: var(--text-3);
628
+ padding: 8px 16px;
629
+ font-weight: 600;
630
+ letter-spacing: 0.5px;
631
+ }
632
+
633
+ .file-tree {
634
+ list-style: none;
635
+ }
636
+
637
+ .file-tree li {
638
+ padding: 6px 16px;
639
+ cursor: pointer;
640
+ font-size: 13px;
641
+ display: flex;
642
+ align-items: center;
643
+ transition: background 0.1s;
644
+ color: var(--text-2);
645
+ }
646
+
647
+ .file-tree li:hover {
648
+ background: var(--bg-hover);
649
+ color: var(--text);
650
+ }
651
+
652
+ .file-tree li.active {
653
+ background: var(--surface-2);
654
+ color: var(--accent);
655
+ font-weight: 500;
656
+ }
657
+
658
+ .file-tree .icon {
659
+ margin-right: 6px;
660
+ opacity: 0.7;
661
+ }
662
+
663
+ /* Editor Panel */
664
+ #editor-panel {
665
+ grid-row: 2;
666
+ grid-column: 3;
667
+ display: flex;
668
+ flex-direction: column;
669
+ background: var(--bg);
670
+ min-height: 0;
671
+ min-width: 0;
672
+ overflow: hidden;
673
+ }
674
+
675
+ #editor-tabs {
676
+ display: flex;
677
+ background: var(--bg-card);
678
+ border-bottom: 1px solid var(--border);
679
+ padding: 0 8px;
680
+ gap: 2px;
681
+ }
682
+
683
+ .editor-tab {
684
+ padding: 8px 16px;
685
+ font-size: 13px;
686
+ background: transparent;
687
+ border: none;
688
+ color: var(--text-3);
689
+ cursor: pointer;
690
+ border-radius: 0;
691
+ transition: all 0.1s;
692
+ display: flex;
693
+ align-items: center;
694
+ gap: 8px;
695
+ }
696
+
697
+ .editor-tab:hover {
698
+ background: var(--bg-hover);
699
+ color: var(--text-2);
700
+ }
701
+
702
+ .editor-tab.active {
703
+ background: var(--bg);
704
+ color: var(--accent);
705
+ font-weight: 500;
706
+ }
707
+
708
+ .editor-tab .close {
709
+ opacity: 0.5;
710
+ font-size: 16px;
711
+ line-height: 1;
712
+ color: var(--text-3);
713
+ }
714
+
715
+ .editor-tab .close:hover {
716
+ opacity: 1;
717
+ color: var(--red);
718
+ }
719
+
720
+ #editor-container {
721
+ flex: 1;
722
+ min-height: 0;
723
+ height: 100%;
724
+ overflow: hidden;
725
+ }
726
+
727
+ #editor-toolbar {
728
+ display: flex;
729
+ align-items: center;
730
+ gap: 8px;
731
+ padding: 8px 12px;
732
+ border-bottom: 1px solid var(--border);
733
+ background: color-mix(in srgb, var(--bg-card) 92%, var(--bg) 8%);
734
+ flex-wrap: wrap;
735
+ }
736
+
737
+ .editor-toolbar-group {
738
+ display: flex;
739
+ align-items: center;
740
+ gap: 8px;
741
+ }
742
+
743
+ .editor-toolbar-spacer {
744
+ flex: 1;
745
+ }
746
+
747
+ .editor-toolbar-btn {
748
+ border: 1px solid var(--border);
749
+ background: rgba(255, 255, 255, 0.04);
750
+ color: var(--text-2);
751
+ padding: 6px 10px;
752
+ border-radius: 8px;
753
+ font-size: 12px;
754
+ font-weight: 600;
755
+ cursor: pointer;
756
+ transition:
757
+ background 0.12s ease,
758
+ border-color 0.12s ease,
759
+ color 0.12s ease,
760
+ transform 0.12s ease;
761
+ }
762
+
763
+ .editor-toolbar-btn:hover {
764
+ background: rgba(255, 255, 255, 0.08);
765
+ border-color: var(--border-hi);
766
+ color: var(--text-1);
767
+ transform: translateY(-1px);
768
+ }
769
+
770
+ .editor-toolbar-btn:focus-visible {
771
+ outline: none;
772
+ border-color: var(--border-hi);
773
+ box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.14);
774
+ }
775
+
776
+ .editor-toolbar-btn:disabled {
777
+ opacity: 0.45;
778
+ cursor: not-allowed;
779
+ transform: none;
780
+ }
781
+
782
+ .editor-toolbar-hint {
783
+ font-size: 11px;
784
+ color: var(--text-3);
785
+ white-space: nowrap;
786
+ }
787
+
788
+ #editor-status {
789
+ display: none;
790
+ align-items: center;
791
+ gap: 8px;
792
+ min-height: 36px;
793
+ padding: 8px 14px;
794
+ border-bottom: 1px solid var(--border);
795
+ background: color-mix(in srgb, var(--bg-card) 88%, var(--accent) 12%);
796
+ color: var(--text-1);
797
+ font-size: 12px;
798
+ line-height: 1.4;
799
+ }
800
+
801
+ #editor-status.visible {
802
+ display: flex;
803
+ }
804
+
805
+ #editor-status[data-tone="success"] {
806
+ background: color-mix(in srgb, var(--bg-card) 82%, var(--green) 18%);
807
+ color: var(--green-hi);
808
+ }
809
+
810
+ #editor-status[data-tone="warning"] {
811
+ background: color-mix(in srgb, var(--bg-card) 80%, var(--yellow) 20%);
812
+ color: var(--warning);
813
+ }
814
+
815
+ #editor-status[data-tone="error"] {
816
+ background: color-mix(in srgb, var(--bg-card) 80%, var(--red) 20%);
817
+ color: var(--red);
818
+ }
819
+
820
+ #editor-status strong {
821
+ font-size: 11px;
822
+ letter-spacing: 0.04em;
823
+ text-transform: uppercase;
824
+ }
825
+
826
+ html:not(.dark) #editor-container,
827
+ html:not(.dark) .monaco-editor,
828
+ html:not(.dark) .monaco-editor-background,
829
+ html:not(.dark) .monaco-editor .margin,
830
+ html:not(.dark) .monaco-diff-editor,
831
+ html:not(.dark) .monaco-diff-editor .editor,
832
+ html:not(.dark) .monaco-diff-editor .margin {
833
+ background: #f8fbff !important;
834
+ }
835
+
836
+ html:not(.dark) .monaco-editor,
837
+ html:not(.dark) .monaco-editor .view-lines,
838
+ html:not(.dark) .monaco-editor .inputarea,
839
+ html:not(.dark) .monaco-diff-editor {
840
+ color: #0f172a !important;
841
+ }
842
+
843
+ #editor-workspace {
844
+ flex: 1;
845
+ min-height: 0;
846
+ display: flex;
847
+ flex-direction: column;
848
+ overflow: hidden;
849
+ position: relative;
850
+ }
851
+
852
+ #editor-main {
853
+ display: flex;
854
+ flex-direction: column;
855
+ flex: 1;
856
+ min-height: 0;
857
+ overflow: hidden;
858
+ }
859
+
860
+ #bottom-terminal-panel {
861
+ flex: 0 0 0;
862
+ min-height: 0;
863
+ display: flex;
864
+ flex-direction: column;
865
+ overflow: hidden;
866
+ border-top: 1px solid rgba(56, 189, 248, 0.12);
867
+ background:
868
+ linear-gradient(180deg, rgba(3, 7, 14, 0.96), rgba(6, 10, 16, 0.98));
869
+ transition:
870
+ flex-basis 220ms ease,
871
+ min-height 220ms ease,
872
+ border-color 220ms ease;
873
+ }
874
+
875
+ html:not(.dark) #bottom-terminal-panel {
876
+ border-top-color: rgba(14, 165, 233, 0.12);
877
+ background:
878
+ linear-gradient(180deg, rgba(248, 250, 252, 0.98), rgba(241, 245, 249, 0.98));
879
+ }
880
+
881
+ #bottom-terminal-panel.visible {
882
+ flex-basis: 30%;
883
+ min-height: 180px;
884
+ border-top-color: rgba(56, 189, 248, 0.2);
885
+ }
886
+
887
+ #bottom-terminal-panel.dragging {
888
+ transition: none;
889
+ }
890
+
891
+ #terminal-resizer {
892
+ height: 8px;
893
+ flex-shrink: 0;
894
+ cursor: row-resize;
895
+ position: relative;
896
+ background: linear-gradient(180deg, transparent, rgba(56, 189, 248, 0.08), transparent);
897
+ display: none;
898
+ }
899
+
900
+ #bottom-terminal-panel.visible #terminal-resizer {
901
+ display: block;
902
+ }
903
+
904
+ #terminal-resizer::after {
905
+ content: "";
906
+ position: absolute;
907
+ left: 50%;
908
+ top: 50%;
909
+ width: 72px;
910
+ height: 3px;
911
+ border-radius: 999px;
912
+ transform: translate(-50%, -50%);
913
+ background: rgba(139, 157, 179, 0.5);
914
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.04);
915
+ }
916
+
917
+ #bottom-terminal-header {
918
+ display: flex;
919
+ align-items: center;
920
+ justify-content: space-between;
921
+ gap: 12px;
922
+ padding: 12px 14px;
923
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
924
+ background: rgba(13, 20, 32, 0.92);
925
+ }
926
+
927
+ .bottom-terminal-meta {
928
+ min-width: 0;
929
+ display: flex;
930
+ flex-direction: column;
931
+ gap: 3px;
932
+ }
933
+
934
+ .bottom-terminal-meta strong {
935
+ font-size: 11px;
936
+ letter-spacing: 0.08em;
937
+ text-transform: uppercase;
938
+ color: var(--text);
939
+ }
940
+
941
+ .bottom-terminal-meta span {
942
+ font-size: 11px;
943
+ color: var(--text-3);
944
+ }
945
+
946
+ .bottom-terminal-actions {
947
+ display: flex;
948
+ align-items: center;
949
+ gap: 8px;
950
+ flex-shrink: 0;
951
+ }
952
+
953
+ #terminal-instance {
954
+ flex: 1;
955
+ min-height: 0;
956
+ padding: 10px 12px 12px;
957
+ overflow: hidden;
958
+ }
959
+
960
+ #terminal-instance .xterm {
961
+ height: 100%;
962
+ position: relative;
963
+ user-select: none;
964
+ -webkit-user-select: none;
965
+ }
966
+
967
+ #terminal-instance .xterm-viewport {
968
+ border-radius: 12px;
969
+ overflow-y: auto;
970
+ }
971
+
972
+ #terminal-instance .xterm-screen {
973
+ position: relative;
974
+ }
975
+
976
+ /* Let xterm manage its own measurement node.
977
+ Forcing the char-measure element to 1px breaks column sizing and pushes
978
+ prompts/input off-screen in the embedded shell. */
979
+
980
+ /* Chat Panel */
981
+ #chat-panel {
982
+ grid-row: 2;
983
+ grid-column: 5;
984
+ background: var(--bg-card);
985
+ border-left: 1px solid var(--border);
986
+ display: flex;
987
+ flex-direction: column;
988
+ min-height: 0;
989
+ min-width: 0;
990
+ overflow: hidden;
991
+ }
992
+
993
+ .panel-resizer {
994
+ position: relative;
995
+ background:
996
+ linear-gradient(180deg, transparent, rgba(56, 189, 248, 0.08), transparent);
997
+ user-select: none;
998
+ touch-action: none;
999
+ transition: background 0.18s ease;
1000
+ }
1001
+
1002
+ .panel-resizer::after {
1003
+ content: "";
1004
+ position: absolute;
1005
+ inset: 50% auto auto 50%;
1006
+ transform: translate(-50%, -50%);
1007
+ border-radius: 999px;
1008
+ background: rgba(139, 157, 179, 0.5);
1009
+ box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.04);
1010
+ }
1011
+
1012
+ .panel-resizer:hover,
1013
+ .panel-resizer:focus-visible,
1014
+ .panel-resizer.dragging {
1015
+ background:
1016
+ linear-gradient(180deg, transparent, rgba(56, 189, 248, 0.18), transparent);
1017
+ outline: none;
1018
+ }
1019
+
1020
+ .panel-resizer.vertical {
1021
+ cursor: col-resize;
1022
+ }
1023
+
1024
+ .panel-resizer.vertical::after {
1025
+ width: 3px;
1026
+ height: 88px;
1027
+ }
1028
+
1029
+ .panel-resizer.horizontal {
1030
+ grid-column: 1 / -1;
1031
+ cursor: row-resize;
1032
+ background:
1033
+ linear-gradient(90deg, transparent, rgba(56, 189, 248, 0.08), transparent);
1034
+ }
1035
+
1036
+ .panel-resizer.horizontal::after {
1037
+ width: 96px;
1038
+ height: 3px;
1039
+ }
1040
+
1041
+ .panel-resizer.horizontal:hover,
1042
+ .panel-resizer.horizontal:focus-visible,
1043
+ .panel-resizer.horizontal.dragging {
1044
+ background:
1045
+ linear-gradient(90deg, transparent, rgba(56, 189, 248, 0.18), transparent);
1046
+ }
1047
+
1048
+ #sidebar-resizer {
1049
+ grid-row: 2;
1050
+ grid-column: 2;
1051
+ }
1052
+
1053
+ #chat-resizer {
1054
+ grid-row: 2;
1055
+ grid-column: 4;
1056
+ }
1057
+
1058
+ #chat-header {
1059
+ padding: 14px 16px;
1060
+ border-bottom: 1px solid var(--border);
1061
+ display: flex;
1062
+ flex-direction: column;
1063
+ gap: 8px;
1064
+ }
1065
+
1066
+ #chat-header .title-row {
1067
+ display: flex;
1068
+ align-items: center;
1069
+ justify-content: space-between;
1070
+ }
1071
+
1072
+ #chat-header h3 {
1073
+ font-size: 13px;
1074
+ font-weight: 600;
1075
+ color: var(--text);
1076
+ margin: 0;
1077
+ }
1078
+
1079
+ #chat-header .controls {
1080
+ display: flex;
1081
+ gap: 8px;
1082
+ align-items: center;
1083
+ }
1084
+
1085
+ #chat-header select {
1086
+ background: var(--bg-2);
1087
+ border: 1px solid var(--border);
1088
+ color: var(--text-2);
1089
+ padding: 4px 8px;
1090
+ border-radius: 6px;
1091
+ font-size: 11px;
1092
+ font-family: inherit;
1093
+ cursor: pointer;
1094
+ outline: none;
1095
+ }
1096
+
1097
+ #chat-header select:focus {
1098
+ border-color: var(--accent);
1099
+ }
1100
+
1101
+ #chat-header button {
1102
+ background: var(--accent);
1103
+ border: none;
1104
+ color: #0a0a12;
1105
+ padding: 4px 10px;
1106
+ border-radius: 6px;
1107
+ font-size: 11px;
1108
+ font-weight: 600;
1109
+ cursor: pointer;
1110
+ transition: opacity 0.1s;
1111
+ }
1112
+
1113
+ #chat-header button:hover {
1114
+ opacity: 0.9;
1115
+ }
1116
+
1117
+ #chat-header button.secondary {
1118
+ background: transparent;
1119
+ border: 1px solid var(--border);
1120
+ color: var(--text-2);
1121
+ }
1122
+
1123
+ #chat-header .agent-status {
1124
+ display: flex;
1125
+ gap: 8px;
1126
+ font-size: 11px;
1127
+ color: var(--text-3);
1128
+ }
1129
+
1130
+ #chat-messages {
1131
+ flex: 1;
1132
+ min-height: 0;
1133
+ overflow-y: auto;
1134
+ overflow-x: hidden;
1135
+ padding: 16px;
1136
+ display: flex;
1137
+ flex-direction: column;
1138
+ gap: 12px;
1139
+ }
1140
+
1141
+ .message {
1142
+ display: flex;
1143
+ flex-direction: column;
1144
+ gap: 4px;
1145
+ }
1146
+
1147
+ .message-header {
1148
+ font-size: 11px;
1149
+ font-weight: 600;
1150
+ color: var(--text-3);
1151
+ }
1152
+
1153
+ .message-content {
1154
+ background: var(--bg-2);
1155
+ padding: 10px 14px;
1156
+ border-radius: var(--radius);
1157
+ font-size: 13px;
1158
+ line-height: 1.6;
1159
+ color: var(--text);
1160
+ }
1161
+
1162
+ .message.user .message-content {
1163
+ background: var(--accent);
1164
+ color: #0a0a12;
1165
+ font-weight: 500;
1166
+ }
1167
+
1168
+ .message.agent .message-content {
1169
+ background: var(--bg-2);
1170
+ border: 1px solid var(--border);
1171
+ }
1172
+
1173
+ .message-transcript {
1174
+ margin-top: 2px;
1175
+ align-self: stretch;
1176
+ background: color-mix(in srgb, var(--bg-card2) 88%, transparent);
1177
+ border: 1px solid var(--border);
1178
+ border-radius: 10px;
1179
+ overflow: hidden;
1180
+ }
1181
+
1182
+ .message-transcript summary {
1183
+ cursor: pointer;
1184
+ list-style: none;
1185
+ padding: 8px 12px;
1186
+ font-size: 11px;
1187
+ font-weight: 600;
1188
+ color: var(--text-3);
1189
+ background: color-mix(in srgb, var(--bg-2) 75%, transparent);
1190
+ }
1191
+
1192
+ .message-transcript summary::-webkit-details-marker {
1193
+ display: none;
1194
+ }
1195
+
1196
+ .message-transcript pre {
1197
+ margin: 0;
1198
+ padding: 12px;
1199
+ white-space: pre-wrap;
1200
+ word-break: break-word;
1201
+ color: var(--text-2);
1202
+ font-size: 12px;
1203
+ line-height: 1.6;
1204
+ }
1205
+
1206
+ #chat-input-container {
1207
+ border-top: 1px solid var(--border);
1208
+ padding: 12px;
1209
+ background: var(--bg-card2);
1210
+ flex-shrink: 0;
1211
+ }
1212
+
1213
+ #chat-input {
1214
+ width: 100%;
1215
+ background: var(--bg-2);
1216
+ border: 1px solid var(--border);
1217
+ color: var(--text);
1218
+ padding: 10px 12px;
1219
+ border-radius: var(--radius);
1220
+ font-size: 13px;
1221
+ font-family: inherit;
1222
+ resize: none;
1223
+ outline: none;
1224
+ transition: border-color 0.1s;
1225
+ }
1226
+
1227
+ #chat-input:focus {
1228
+ border-color: var(--accent);
1229
+ }
1230
+
1231
+ /* Terminal Panel */
1232
+ #terminal {
1233
+ grid-row: 4;
1234
+ grid-column: 1 / -1;
1235
+ background: var(--bg-0);
1236
+ border-top: 1px solid var(--border);
1237
+ overflow-y: auto;
1238
+ padding: 12px;
1239
+ font-family: 'SF Mono', Menlo, Monaco, 'Courier New', monospace;
1240
+ font-size: 12px;
1241
+ line-height: 1.6;
1242
+ min-height: 100px;
1243
+ }
1244
+
1245
+ /* Status Bar */
1246
+ #status-bar {
1247
+ grid-row: 5;
1248
+ grid-column: 1 / -1;
1249
+ background: var(--bg-card);
1250
+ border-top: 1px solid var(--border);
1251
+ display: flex;
1252
+ align-items: center;
1253
+ justify-content: space-between;
1254
+ padding: 0 16px;
1255
+ font-size: 11px;
1256
+ color: var(--text-3);
1257
+ }
1258
+
1259
+ #status-bar a {
1260
+ color: var(--accent);
1261
+ text-decoration: none;
1262
+ display: flex;
1263
+ align-items: center;
1264
+ gap: 6px;
1265
+ padding: 4px 8px;
1266
+ border-radius: 4px;
1267
+ transition: background 0.1s;
1268
+ }
1269
+
1270
+ #status-bar a:hover {
1271
+ background: var(--bg-2);
1272
+ }
1273
+
1274
+ #terminal-header {
1275
+ display: flex;
1276
+ align-items: center;
1277
+ justify-content: space-between;
1278
+ padding: 0 0 8px 0;
1279
+ border-bottom: 1px solid var(--border);
1280
+ margin-bottom: 12px;
1281
+ }
1282
+
1283
+ #terminal-title {
1284
+ font-size: 11px;
1285
+ text-transform: uppercase;
1286
+ color: var(--text-3);
1287
+ font-weight: 600;
1288
+ }
1289
+
1290
+ .terminal-actions {
1291
+ display: flex;
1292
+ align-items: center;
1293
+ gap: 10px;
1294
+ }
1295
+
1296
+ .terminal-btn {
1297
+ border: 1px solid rgba(56, 189, 248, 0.2);
1298
+ background:
1299
+ linear-gradient(135deg, rgba(56, 189, 248, 0.14), rgba(129, 140, 248, 0.12));
1300
+ color: var(--text);
1301
+ padding: 7px 12px;
1302
+ border-radius: 999px;
1303
+ font-size: 11px;
1304
+ font-weight: 700;
1305
+ letter-spacing: 0.03em;
1306
+ cursor: pointer;
1307
+ transition:
1308
+ transform 180ms ease,
1309
+ border-color 180ms ease,
1310
+ box-shadow 180ms ease,
1311
+ background 180ms ease;
1312
+ box-shadow: 0 10px 24px rgba(0, 0, 0, 0.22);
1313
+ }
1314
+
1315
+ .terminal-btn:hover {
1316
+ transform: translateY(-1px);
1317
+ border-color: rgba(56, 189, 248, 0.42);
1318
+ box-shadow: 0 14px 28px rgba(0, 0, 0, 0.28);
1319
+ }
1320
+
1321
+ .terminal-line {
1322
+ margin-bottom: 4px;
1323
+ white-space: pre-wrap;
1324
+ word-wrap: break-word;
1325
+ }
1326
+
1327
+ .terminal-line.info {
1328
+ color: var(--sky);
1329
+ }
1330
+
1331
+ .terminal-line.success {
1332
+ color: var(--green);
1333
+ }
1334
+
1335
+ .terminal-line.error {
1336
+ color: var(--red);
1337
+ }
1338
+
1339
+ .terminal-line.warning {
1340
+ color: var(--yellow);
1341
+ }
1342
+
1343
+ .terminal-trace {
1344
+ margin: 10px 0 14px;
1345
+ border: 1px solid var(--border);
1346
+ border-radius: 10px;
1347
+ background: color-mix(in srgb, var(--bg-card2) 82%, transparent);
1348
+ overflow: hidden;
1349
+ }
1350
+
1351
+ .terminal-trace summary {
1352
+ cursor: pointer;
1353
+ list-style: none;
1354
+ padding: 9px 12px;
1355
+ font-size: 11px;
1356
+ font-weight: 700;
1357
+ color: var(--text-2);
1358
+ background: color-mix(in srgb, var(--bg-2) 75%, transparent);
1359
+ border-bottom: 1px solid var(--border);
1360
+ }
1361
+
1362
+ .terminal-trace summary::-webkit-details-marker {
1363
+ display: none;
1364
+ }
1365
+
1366
+ .terminal-trace-body {
1367
+ margin: 0;
1368
+ padding: 12px;
1369
+ white-space: pre-wrap;
1370
+ word-break: break-word;
1371
+ color: var(--text);
1372
+ font-size: 11px;
1373
+ line-height: 1.6;
1374
+ }
1375
+
1376
+ /* Scrollbars */
1377
+ ::-webkit-scrollbar {
1378
+ width: 10px;
1379
+ height: 10px;
1380
+ }
1381
+
1382
+ ::-webkit-scrollbar-track {
1383
+ background: var(--bg-0);
1384
+ }
1385
+
1386
+ ::-webkit-scrollbar-thumb {
1387
+ background: var(--bg-2);
1388
+ border-radius: 5px;
1389
+ }
1390
+
1391
+ ::-webkit-scrollbar-thumb:hover {
1392
+ background: var(--surface-2);
1393
+ }
1394
+
1395
+ /* Loading state */
1396
+ .loading {
1397
+ display: flex;
1398
+ align-items: center;
1399
+ justify-content: center;
1400
+ height: 100%;
1401
+ color: var(--text-3);
1402
+ font-size: 13px;
1403
+ }
1404
+
1405
+ /* Inline Chat (Cmd+K) */
1406
+ #inline-chat-overlay {
1407
+ position: fixed;
1408
+ inset: 0;
1409
+ display: none;
1410
+ z-index: 1000;
1411
+ pointer-events: none;
1412
+ }
1413
+
1414
+ #inline-chat-overlay.visible {
1415
+ display: block;
1416
+ }
1417
+
1418
+ #inline-chat-box {
1419
+ position: fixed;
1420
+ top: 0;
1421
+ left: 0;
1422
+ width: min(420px, calc(100vw - 24px));
1423
+ max-height: min(420px, calc(100vh - 24px));
1424
+ display: flex;
1425
+ flex-direction: column;
1426
+ gap: 12px;
1427
+ padding: 14px;
1428
+ border-radius: 18px;
1429
+ border: 1px solid rgba(255, 255, 255, 0.14);
1430
+ background:
1431
+ linear-gradient(180deg, rgba(17, 24, 39, 0.78), rgba(13, 20, 32, 0.92));
1432
+ box-shadow:
1433
+ 0 22px 60px rgba(0, 0, 0, 0.45),
1434
+ 0 0 0 1px rgba(56, 189, 248, 0.08) inset;
1435
+ backdrop-filter: blur(24px) saturate(1.2);
1436
+ -webkit-backdrop-filter: blur(24px) saturate(1.2);
1437
+ pointer-events: auto;
1438
+ opacity: 0;
1439
+ transform: translate3d(0, 18px, 0) scale(0.98);
1440
+ transform-origin: top left;
1441
+ transition:
1442
+ opacity 180ms ease,
1443
+ transform 240ms cubic-bezier(0.22, 1, 0.36, 1);
1444
+ overflow: hidden;
1445
+ }
1446
+
1447
+ #inline-chat-overlay.visible #inline-chat-box {
1448
+ opacity: 1;
1449
+ transform: translate3d(0, 0, 0) scale(1);
1450
+ }
1451
+
1452
+ .inline-chat-header {
1453
+ display: flex;
1454
+ align-items: center;
1455
+ justify-content: space-between;
1456
+ gap: 12px;
1457
+ }
1458
+
1459
+ .inline-chat-title {
1460
+ display: flex;
1461
+ flex-direction: column;
1462
+ gap: 3px;
1463
+ min-width: 0;
1464
+ }
1465
+
1466
+ .inline-chat-title strong {
1467
+ font-size: 13px;
1468
+ color: var(--text);
1469
+ letter-spacing: 0.02em;
1470
+ }
1471
+
1472
+ .inline-chat-title span {
1473
+ font-size: 11px;
1474
+ color: var(--text-2);
1475
+ }
1476
+
1477
+ .inline-chat-close {
1478
+ width: 28px;
1479
+ height: 28px;
1480
+ border: 1px solid var(--border);
1481
+ border-radius: 999px;
1482
+ background: rgba(255, 255, 255, 0.04);
1483
+ color: var(--text-2);
1484
+ cursor: pointer;
1485
+ flex-shrink: 0;
1486
+ }
1487
+
1488
+ .inline-chat-controls {
1489
+ display: grid;
1490
+ grid-template-columns: 1fr auto;
1491
+ gap: 10px;
1492
+ align-items: center;
1493
+ }
1494
+
1495
+ #inline-chat-model {
1496
+ min-width: 0;
1497
+ background: rgba(6, 10, 16, 0.55);
1498
+ border: 1px solid var(--border);
1499
+ color: var(--text);
1500
+ padding: 9px 12px;
1501
+ border-radius: 12px;
1502
+ font-size: 12px;
1503
+ font-family: inherit;
1504
+ outline: none;
1505
+ }
1506
+
1507
+ .inline-chat-shortcut {
1508
+ font-size: 11px;
1509
+ color: var(--text-3);
1510
+ white-space: nowrap;
1511
+ }
1512
+
1513
+ #inline-chat-input {
1514
+ width: 100%;
1515
+ min-height: 88px;
1516
+ max-height: 140px;
1517
+ background: rgba(6, 10, 16, 0.45);
1518
+ border: 1px solid rgba(255, 255, 255, 0.08);
1519
+ color: var(--text);
1520
+ padding: 12px 14px;
1521
+ font-size: 13px;
1522
+ line-height: 1.5;
1523
+ font-family: inherit;
1524
+ border-radius: 14px;
1525
+ resize: vertical;
1526
+ outline: none;
1527
+ }
1528
+
1529
+ #inline-chat-input:focus,
1530
+ #inline-chat-model:focus {
1531
+ border-color: var(--border-hi);
1532
+ box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.14);
1533
+ }
1534
+
1535
+ #inline-chat-response {
1536
+ display: none;
1537
+ padding: 0;
1538
+ border-radius: 14px;
1539
+ background: rgba(129, 140, 248, 0.08);
1540
+ border: 1px solid rgba(129, 140, 248, 0.18);
1541
+ overflow-y: auto;
1542
+ }
1543
+
1544
+ #inline-chat-response.visible {
1545
+ display: block;
1546
+ }
1547
+
1548
+ .inline-chat-answer {
1549
+ min-height: 72px;
1550
+ padding: 14px 16px;
1551
+ font-size: 14px;
1552
+ line-height: 1.65;
1553
+ color: var(--text-1);
1554
+ white-space: pre-wrap;
1555
+ }
1556
+
1557
+ .inline-chat-answer[data-empty="true"] {
1558
+ color: var(--text-3);
1559
+ }
1560
+
1561
+ .inline-chat-transcript {
1562
+ border-top: 1px solid rgba(129, 140, 248, 0.16);
1563
+ }
1564
+
1565
+ .inline-chat-transcript summary {
1566
+ padding: 10px 16px;
1567
+ font-size: 12px;
1568
+ color: var(--text-2);
1569
+ cursor: pointer;
1570
+ list-style: none;
1571
+ user-select: none;
1572
+ }
1573
+
1574
+ .inline-chat-transcript summary::-webkit-details-marker {
1575
+ display: none;
1576
+ }
1577
+
1578
+ .inline-chat-transcript summary::before {
1579
+ content: "+";
1580
+ display: inline-block;
1581
+ width: 14px;
1582
+ margin-right: 6px;
1583
+ color: var(--text-3);
1584
+ }
1585
+
1586
+ .inline-chat-transcript[open] summary::before {
1587
+ content: "-";
1588
+ }
1589
+
1590
+ .inline-chat-transcript pre {
1591
+ margin: 0;
1592
+ padding: 0 16px 14px;
1593
+ font-size: 11px;
1594
+ line-height: 1.55;
1595
+ color: var(--text-2);
1596
+ white-space: pre-wrap;
1597
+ word-break: break-word;
1598
+ }
1599
+
1600
+ #inline-chat-box .actions {
1601
+ display: flex;
1602
+ align-items: center;
1603
+ justify-content: space-between;
1604
+ gap: 10px;
1605
+ }
1606
+
1607
+ .inline-chat-meta {
1608
+ font-size: 11px;
1609
+ color: var(--text-3);
1610
+ }
1611
+
1612
+ #inline-chat-box .action-buttons {
1613
+ display: flex;
1614
+ gap: 8px;
1615
+ }
1616
+
1617
+ #inline-chat-box button {
1618
+ background: var(--accent);
1619
+ border: none;
1620
+ color: #0a0a12;
1621
+ padding: 9px 14px;
1622
+ border-radius: 10px;
1623
+ font-size: 12px;
1624
+ font-weight: 700;
1625
+ cursor: pointer;
1626
+ transition:
1627
+ opacity 0.15s ease,
1628
+ transform 0.15s ease;
1629
+ }
1630
+
1631
+ #inline-chat-box button:hover {
1632
+ opacity: 0.94;
1633
+ transform: translateY(-1px);
1634
+ }
1635
+
1636
+ #inline-chat-box button.secondary {
1637
+ background: rgba(255, 255, 255, 0.04);
1638
+ border: 1px solid var(--border);
1639
+ color: var(--text-2);
1640
+ }
1641
+
1642
+ /* Diff Preview Modal */
1643
+ #diff-preview-overlay {
1644
+ position: fixed;
1645
+ top: 0;
1646
+ left: 0;
1647
+ right: 0;
1648
+ bottom: 0;
1649
+ background:
1650
+ radial-gradient(circle at top, rgba(56, 189, 248, 0.1), transparent 38%),
1651
+ rgba(6, 10, 16, 0.82);
1652
+ backdrop-filter: blur(14px);
1653
+ display: none;
1654
+ align-items: center;
1655
+ justify-content: center;
1656
+ padding: 24px;
1657
+ z-index: 1000;
1658
+ opacity: 0;
1659
+ transition: opacity 180ms ease;
1660
+ }
1661
+
1662
+ #diff-preview-overlay.visible {
1663
+ display: flex;
1664
+ opacity: 1;
1665
+ }
1666
+
1667
+ #diff-preview-box {
1668
+ background:
1669
+ linear-gradient(180deg, rgba(13, 20, 32, 0.96), rgba(6, 10, 16, 0.94));
1670
+ border: 1px solid rgba(129, 140, 248, 0.2);
1671
+ border-radius: 24px;
1672
+ width: min(1180px, 100%);
1673
+ min-height: min(720px, calc(100vh - 48px));
1674
+ max-height: calc(100vh - 48px);
1675
+ display: flex;
1676
+ flex-direction: column;
1677
+ overflow: hidden;
1678
+ box-shadow:
1679
+ 0 34px 90px rgba(0, 0, 0, 0.58),
1680
+ 0 0 0 1px rgba(56, 189, 248, 0.08) inset;
1681
+ transform: translateY(26px) scale(0.98);
1682
+ transition:
1683
+ transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
1684
+ opacity 180ms ease;
1685
+ }
1686
+
1687
+ #diff-preview-overlay.visible #diff-preview-box {
1688
+ transform: translateY(0) scale(1);
1689
+ }
1690
+
1691
+ #diff-preview-box .header {
1692
+ padding: 22px 24px 18px;
1693
+ border-bottom: 1px solid var(--border);
1694
+ display: flex;
1695
+ align-items: center;
1696
+ justify-content: space-between;
1697
+ gap: 20px;
1698
+ }
1699
+
1700
+ #diff-preview-box .header h3 {
1701
+ font-size: 16px;
1702
+ font-weight: 700;
1703
+ color: var(--text);
1704
+ }
1705
+
1706
+ #diff-preview-box .header .file-path {
1707
+ font-size: 12px;
1708
+ color: var(--text-3);
1709
+ margin-top: 4px;
1710
+ }
1711
+
1712
+ #diff-preview-box #diff-editor {
1713
+ flex: 1;
1714
+ min-height: 0;
1715
+ overflow: auto;
1716
+ background:
1717
+ linear-gradient(180deg, rgba(4, 8, 16, 0.45), rgba(4, 8, 16, 0.18));
1718
+ }
1719
+
1720
+ .diff-preview-toolbar {
1721
+ display: flex;
1722
+ align-items: center;
1723
+ gap: 10px;
1724
+ flex-wrap: wrap;
1725
+ justify-content: flex-end;
1726
+ }
1727
+
1728
+ .diff-preview-pill {
1729
+ display: inline-flex;
1730
+ align-items: center;
1731
+ gap: 8px;
1732
+ padding: 7px 12px;
1733
+ border-radius: 999px;
1734
+ font-size: 11px;
1735
+ font-weight: 700;
1736
+ color: var(--text-2);
1737
+ border: 1px solid rgba(255, 255, 255, 0.08);
1738
+ background: rgba(255, 255, 255, 0.03);
1739
+ }
1740
+
1741
+ .diff-preview-view-toggle {
1742
+ display: inline-flex;
1743
+ align-items: center;
1744
+ gap: 6px;
1745
+ padding: 4px;
1746
+ border-radius: 999px;
1747
+ background: rgba(255, 255, 255, 0.04);
1748
+ border: 1px solid rgba(255, 255, 255, 0.08);
1749
+ }
1750
+
1751
+ .diff-preview-view-toggle button {
1752
+ padding: 7px 12px;
1753
+ border-radius: 999px;
1754
+ background: transparent;
1755
+ border: none;
1756
+ color: var(--text-2);
1757
+ font-size: 11px;
1758
+ font-weight: 700;
1759
+ }
1760
+
1761
+ .diff-preview-view-toggle button.active {
1762
+ background: rgba(56, 189, 248, 0.14);
1763
+ color: var(--text);
1764
+ box-shadow: inset 0 0 0 1px rgba(56, 189, 248, 0.16);
1765
+ }
1766
+
1767
+ .diff-preview-close {
1768
+ width: 34px;
1769
+ height: 34px;
1770
+ border-radius: 999px;
1771
+ border: 1px solid var(--border);
1772
+ background: rgba(255, 255, 255, 0.04);
1773
+ color: var(--text-2);
1774
+ cursor: pointer;
1775
+ font-size: 18px;
1776
+ line-height: 1;
1777
+ }
1778
+
1779
+ .diff-preview-shell {
1780
+ display: flex;
1781
+ flex-direction: column;
1782
+ min-height: 100%;
1783
+ }
1784
+
1785
+ .diff-preview-summary {
1786
+ display: grid;
1787
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1788
+ gap: 12px;
1789
+ padding: 18px 24px;
1790
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
1791
+ background: rgba(255, 255, 255, 0.02);
1792
+ }
1793
+
1794
+ .diff-preview-stat {
1795
+ padding: 14px 16px;
1796
+ border-radius: 16px;
1797
+ border: 1px solid rgba(255, 255, 255, 0.06);
1798
+ background: rgba(6, 10, 16, 0.28);
1799
+ }
1800
+
1801
+ .diff-preview-stat .label {
1802
+ font-size: 11px;
1803
+ text-transform: uppercase;
1804
+ letter-spacing: 0.06em;
1805
+ color: var(--text-3);
1806
+ margin-bottom: 8px;
1807
+ }
1808
+
1809
+ .diff-preview-stat .value {
1810
+ font-size: 18px;
1811
+ font-weight: 800;
1812
+ color: var(--text);
1813
+ }
1814
+
1815
+ .diff-preview-render {
1816
+ padding: 18px 24px 24px;
1817
+ }
1818
+
1819
+ .diff-grid {
1820
+ display: grid;
1821
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
1822
+ gap: 18px;
1823
+ }
1824
+
1825
+ .diff-panel {
1826
+ min-width: 0;
1827
+ border: 1px solid rgba(255, 255, 255, 0.06);
1828
+ border-radius: 18px;
1829
+ background: rgba(6, 10, 16, 0.42);
1830
+ overflow: hidden;
1831
+ }
1832
+
1833
+ .diff-panel-title {
1834
+ padding: 12px 16px;
1835
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
1836
+ font-size: 11px;
1837
+ font-weight: 700;
1838
+ letter-spacing: 0.06em;
1839
+ text-transform: uppercase;
1840
+ color: var(--text-3);
1841
+ }
1842
+
1843
+ .diff-lines {
1844
+ font-family: 'SF Mono', Menlo, Monaco, 'Courier New', monospace;
1845
+ font-size: 12px;
1846
+ line-height: 1.6;
1847
+ }
1848
+
1849
+ .diff-line {
1850
+ display: grid;
1851
+ grid-template-columns: 48px minmax(0, 1fr);
1852
+ gap: 14px;
1853
+ padding: 8px 14px;
1854
+ border-bottom: 1px solid rgba(255, 255, 255, 0.04);
1855
+ white-space: pre-wrap;
1856
+ word-break: break-word;
1857
+ }
1858
+
1859
+ .diff-line:last-child {
1860
+ border-bottom: none;
1861
+ }
1862
+
1863
+ .diff-line-number {
1864
+ color: var(--text-3);
1865
+ user-select: none;
1866
+ text-align: right;
1867
+ }
1868
+
1869
+ .diff-line-code {
1870
+ min-width: 0;
1871
+ color: var(--text-1);
1872
+ }
1873
+
1874
+ .diff-line.addition {
1875
+ background: rgba(52, 211, 153, 0.12);
1876
+ }
1877
+
1878
+ .diff-line.deletion {
1879
+ background: rgba(248, 113, 113, 0.12);
1880
+ }
1881
+
1882
+ .diff-line.replacement {
1883
+ background: rgba(251, 191, 36, 0.12);
1884
+ }
1885
+
1886
+ .diff-line.placeholder {
1887
+ opacity: 0.4;
1888
+ }
1889
+
1890
+ .diff-inline {
1891
+ border: 1px solid rgba(255, 255, 255, 0.06);
1892
+ border-radius: 18px;
1893
+ overflow: hidden;
1894
+ background: rgba(6, 10, 16, 0.42);
1895
+ font-family: 'SF Mono', Menlo, Monaco, 'Courier New', monospace;
1896
+ font-size: 12px;
1897
+ line-height: 1.6;
1898
+ }
1899
+
1900
+ .diff-inline .diff-line-code::before {
1901
+ display: inline-block;
1902
+ width: 18px;
1903
+ margin-right: 10px;
1904
+ font-weight: 700;
1905
+ color: inherit;
1906
+ }
1907
+
1908
+ .diff-inline .diff-line.addition .diff-line-code::before {
1909
+ content: "+";
1910
+ }
1911
+
1912
+ .diff-inline .diff-line.deletion .diff-line-code::before {
1913
+ content: "-";
1914
+ }
1915
+
1916
+ .diff-inline .diff-line.replacement .diff-line-code::before {
1917
+ content: "±";
1918
+ }
1919
+
1920
+ .diff-inline .diff-line.equal .diff-line-code::before {
1921
+ content: " ";
1922
+ }
1923
+
1924
+ #diff-preview-box .actions {
1925
+ padding: 18px 24px 22px;
1926
+ border-top: 1px solid var(--border);
1927
+ display: flex;
1928
+ gap: 12px;
1929
+ justify-content: flex-end;
1930
+ }
1931
+
1932
+ #diff-preview-box button {
1933
+ background: var(--accent);
1934
+ border: none;
1935
+ color: #0a0a12;
1936
+ padding: 10px 20px;
1937
+ border-radius: 6px;
1938
+ font-size: 13px;
1939
+ font-weight: 600;
1940
+ cursor: pointer;
1941
+ transition: opacity 0.1s;
1942
+ }
1943
+
1944
+ #diff-preview-box button:hover {
1945
+ opacity: 0.9;
1946
+ }
1947
+
1948
+ #diff-preview-box button.danger {
1949
+ background: var(--red);
1950
+ color: white;
1951
+ }
1952
+
1953
+ #diff-preview-box button.secondary {
1954
+ background: transparent;
1955
+ border: 1px solid var(--border);
1956
+ color: var(--text-2);
1957
+ }
1958
+
1959
+ @media (max-width: 900px) {
1960
+ #app {
1961
+ grid-template-columns: 220px minmax(0, 1fr);
1962
+ grid-template-rows: 50px minmax(0, 1fr) minmax(180px, auto) 30px;
1963
+ }
1964
+
1965
+ #titlebar {
1966
+ padding: 0 12px;
1967
+ }
1968
+
1969
+ #titlebar .project-picker,
1970
+ #titlebar .status-indicator {
1971
+ display: none;
1972
+ }
1973
+
1974
+ #titlebar .titlebar-actions {
1975
+ margin-left: auto;
1976
+ }
1977
+
1978
+ .icon-btn .icon-btn-label {
1979
+ display: none;
1980
+ }
1981
+
1982
+ #settings-panel {
1983
+ top: 60px;
1984
+ right: 12px;
1985
+ left: 12px;
1986
+ width: auto;
1987
+ }
1988
+
1989
+ #shortcuts-panel {
1990
+ top: auto;
1991
+ bottom: 44px;
1992
+ left: 12px;
1993
+ right: 12px;
1994
+ width: auto;
1995
+ max-height: min(60vh, 560px);
1996
+ }
1997
+
1998
+ .shortcuts-grid {
1999
+ grid-template-columns: 1fr;
2000
+ }
2001
+
2002
+ #sidebar-resizer,
2003
+ #chat-resizer,
2004
+ #output-resizer {
2005
+ display: none;
2006
+ }
2007
+
2008
+ #editor-panel {
2009
+ grid-column: 2;
2010
+ }
2011
+
2012
+ #chat-panel {
2013
+ grid-row: 3;
2014
+ grid-column: 1 / -1;
2015
+ border-left: none;
2016
+ border-top: 1px solid var(--border);
2017
+ }
2018
+
2019
+ #terminal {
2020
+ display: none;
2021
+ }
2022
+
2023
+ #bottom-terminal-panel.visible {
2024
+ flex-basis: 42%;
2025
+ }
2026
+
2027
+ #diff-preview-overlay {
2028
+ padding: 14px;
2029
+ }
2030
+
2031
+ #diff-preview-box {
2032
+ min-height: calc(100vh - 28px);
2033
+ max-height: calc(100vh - 28px);
2034
+ border-radius: 20px;
2035
+ }
2036
+
2037
+ #diff-preview-box .header {
2038
+ align-items: flex-start;
2039
+ flex-direction: column;
2040
+ }
2041
+
2042
+ .diff-preview-toolbar {
2043
+ width: 100%;
2044
+ justify-content: space-between;
2045
+ }
2046
+
2047
+ .diff-preview-summary {
2048
+ grid-template-columns: 1fr;
2049
+ }
2050
+
2051
+ .diff-grid {
2052
+ grid-template-columns: 1fr;
2053
+ }
2054
+ }
2055
+ </style>
2056
+ </head>
2057
+ <body>
2058
+ <div id="app">
2059
+ <!-- Titlebar -->
2060
+ <div id="titlebar">
2061
+ <div class="brand-section">
2062
+ <img class="brand-icon" src="./favicon.png" alt="crewswarm" />
2063
+ <span class="brand-name">crew<span>swarm</span></span>
2064
+ <span style="font-size: 11px; color: var(--text-3); margin-left: 4px;">vibe</span>
2065
+ </div>
2066
+
2067
+ <div class="project-picker">
2068
+ <span style="font-size: 11px; color: var(--text-3);">Project:</span>
2069
+ <select id="projectSelector">
2070
+ <option value="">Loading...</option>
2071
+ </select>
2072
+ </div>
2073
+
2074
+ <div class="status-indicator">
2075
+ <span class="status-dot" id="statusDot"></span>
2076
+ <span id="statusText">RT Bus</span>
2077
+ </div>
2078
+
2079
+ <div class="status-indicator" style="margin-left: 10px;">
2080
+ <span class="status-dot" id="watchStatusDot" style="background: var(--yellow);"></span>
2081
+ <span id="watchStatusText">Watch Server</span>
2082
+ <button id="watchToggle" class="icon-btn" type="button" style="margin-left: 8px; font-size: 12px; padding: 2px 8px;">
2083
+ 🔄
2084
+ </button>
2085
+ </div>
2086
+
2087
+ <div class="titlebar-actions">
2088
+ <button
2089
+ id="theme-toggle"
2090
+ class="icon-btn theme-toggle"
2091
+ type="button"
2092
+ aria-label="Toggle color theme"
2093
+ aria-pressed="true"
2094
+ title="Toggle color theme"
2095
+ >
2096
+ <span class="theme-toggle-icon" aria-hidden="true">
2097
+ <svg class="sun-icon" viewBox="0 0 24 24">
2098
+ <circle cx="12" cy="12" r="4"></circle>
2099
+ <path d="M12 2.75v2.5M12 18.75v2.5M21.25 12h-2.5M5.25 12h-2.5M18.54 5.46l-1.77 1.77M7.23 16.77l-1.77 1.77M18.54 18.54l-1.77-1.77M7.23 7.23 5.46 5.46"></path>
2100
+ </svg>
2101
+ <svg class="moon-icon" viewBox="0 0 24 24">
2102
+ <path d="M20.5 14.5A8.5 8.5 0 0 1 9.5 3.5a8.5 8.5 0 1 0 11 11Z"></path>
2103
+ </svg>
2104
+ </span>
2105
+ <span class="icon-btn-label">Theme</span>
2106
+ </button>
2107
+ <button
2108
+ id="settings-toggle"
2109
+ class="icon-btn"
2110
+ type="button"
2111
+ aria-haspopup="dialog"
2112
+ aria-expanded="false"
2113
+ aria-controls="settings-panel"
2114
+ aria-label="Open settings panel"
2115
+ >
2116
+ <span aria-hidden="true">⚙️</span>
2117
+ <span class="icon-btn-label">Settings</span>
2118
+ </button>
2119
+ <button
2120
+ id="shortcuts-toggle"
2121
+ class="icon-btn"
2122
+ type="button"
2123
+ aria-haspopup="dialog"
2124
+ aria-expanded="true"
2125
+ aria-controls="shortcuts-panel"
2126
+ aria-label="Toggle keyboard shortcuts guide"
2127
+ >
2128
+ <span aria-hidden="true">⌨️</span>
2129
+ <span class="icon-btn-label">Shortcuts</span>
2130
+ </button>
2131
+ </div>
2132
+ </div>
2133
+
2134
+ <!-- Sidebar - File Tree -->
2135
+ <div id="sidebar">
2136
+ <h3>Explorer</h3>
2137
+ <ul class="file-tree" id="file-tree">
2138
+ <li class="loading">Loading files...</li>
2139
+ </ul>
2140
+ </div>
2141
+
2142
+ <div
2143
+ id="sidebar-resizer"
2144
+ class="panel-resizer vertical"
2145
+ role="separator"
2146
+ aria-label="Resize explorer panel"
2147
+ aria-orientation="vertical"
2148
+ tabindex="0"
2149
+ ></div>
2150
+
2151
+ <!-- Editor Panel -->
2152
+ <div id="editor-panel">
2153
+ <div id="editor-tabs"></div>
2154
+ <div id="editor-workspace">
2155
+ <div id="editor-main">
2156
+ <div id="editor-toolbar" aria-label="Editor actions">
2157
+ <div class="editor-toolbar-group" role="group" aria-label="History">
2158
+ <button id="editor-undo" class="editor-toolbar-btn" type="button">Undo</button>
2159
+ <button id="editor-redo" class="editor-toolbar-btn" type="button">Redo</button>
2160
+ </div>
2161
+ <div class="editor-toolbar-group" role="group" aria-label="Document">
2162
+ <button id="editor-save" class="editor-toolbar-btn" type="button">Save</button>
2163
+ <button id="editor-find" class="editor-toolbar-btn" type="button">Find</button>
2164
+ <button id="editor-replace" class="editor-toolbar-btn" type="button">Replace</button>
2165
+ <button id="editor-comment" class="editor-toolbar-btn" type="button">Comment</button>
2166
+ <button id="editor-format" class="editor-toolbar-btn" type="button">Format</button>
2167
+ </div>
2168
+ <div class="editor-toolbar-spacer" aria-hidden="true"></div>
2169
+ <div class="editor-toolbar-hint">Monaco actions</div>
2170
+ </div>
2171
+ <div id="editor-status" role="status" aria-live="polite"></div>
2172
+ <div id="editor-container"></div>
2173
+ </div>
2174
+ <div id="bottom-terminal-panel" aria-hidden="true">
2175
+ <div
2176
+ id="terminal-resizer"
2177
+ role="separator"
2178
+ aria-label="Resize terminal panel"
2179
+ aria-orientation="horizontal"
2180
+ tabindex="0"
2181
+ ></div>
2182
+ <div id="bottom-terminal-header">
2183
+ <div class="bottom-terminal-meta">
2184
+ <strong>Workspace Terminal</strong>
2185
+ <span id="bottom-terminal-status">Local shell for the selected project</span>
2186
+ </div>
2187
+ <div class="bottom-terminal-actions">
2188
+ <button id="clear-bottom-terminal" class="terminal-btn" type="button">Clear</button>
2189
+ <button id="close-bottom-terminal" class="terminal-btn" type="button">Hide</button>
2190
+ </div>
2191
+ </div>
2192
+ <div id="terminal-instance" tabindex="0" aria-label="Interactive terminal"></div>
2193
+ </div>
2194
+ </div>
2195
+ </div>
2196
+
2197
+ <div
2198
+ id="chat-resizer"
2199
+ class="panel-resizer vertical"
2200
+ role="separator"
2201
+ aria-label="Resize chat panel"
2202
+ aria-orientation="vertical"
2203
+ tabindex="0"
2204
+ ></div>
2205
+
2206
+ <!-- Chat Panel -->
2207
+ <div id="chat-panel">
2208
+ <div id="chat-header">
2209
+ <div class="title-row">
2210
+ <h3>Chat</h3>
2211
+ <div style="display: flex; gap: 8px; align-items: center;">
2212
+ <button id="toggle-bottom-terminal" class="secondary" type="button">Terminal</button>
2213
+ <button class="secondary" onclick="showNewProjectModal()">+ New Project</button>
2214
+ </div>
2215
+ </div>
2216
+ <div class="controls">
2217
+ <label for="chat-mode-selector" style="font-size: 11px; color: var(--text-3);">Mode:</label>
2218
+ <select id="chat-mode-selector" onchange="switchChatMode()">
2219
+ <option value="crew-lead">🧠 crew-lead (Smart Routing)</option>
2220
+ <optgroup label="───── Direct CLIs ─────">
2221
+ <option value="cli:opencode">⚡ OpenCode — Full workspace context</option>
2222
+ <option value="cli:cursor">🖱 Cursor CLI — Complex reasoning</option>
2223
+ <option value="cli:crew-cli">🔧 Crew CLI — TypeScript specialist</option>
2224
+ <option value="cli:codex">🟣 Codex CLI — OpenAI Codex</option>
2225
+ <option value="cli:gemini">✨ Gemini CLI — Google Gemini</option>
2226
+ <option value="cli:claude">🤖 Claude Code — Anthropic Claude</option>
2227
+ </optgroup>
2228
+ <optgroup label="───── Agents ─────" id="agentsOptgroup">
2229
+ <!-- Populated dynamically -->
2230
+ </optgroup>
2231
+ </select>
2232
+ </div>
2233
+ <div id="project-context-hint" style="display: none; font-size: 11px; color: var(--text-3); margin-top: 4px; padding: 6px 8px; background: var(--bg-2); border-radius: 4px; border: 1px solid var(--border);">
2234
+ <strong style="color: var(--accent);">📁</strong> <span id="project-context-name"></span>
2235
+ <div style="font-size: 10px; color: var(--text-3); margin-top: 2px; font-family: monospace;" id="project-context-path"></div>
2236
+ </div>
2237
+ </div>
2238
+ <div id="chat-messages"></div>
2239
+ <div id="chat-input-container">
2240
+ <textarea
2241
+ id="chat-input"
2242
+ placeholder="Ask the crew anything... (Enter to send, Shift+Enter for new line)"
2243
+ rows="3"
2244
+ ></textarea>
2245
+ </div>
2246
+ </div>
2247
+
2248
+ <div
2249
+ id="output-resizer"
2250
+ class="panel-resizer horizontal"
2251
+ role="separator"
2252
+ aria-label="Resize activity panel"
2253
+ aria-orientation="horizontal"
2254
+ tabindex="0"
2255
+ ></div>
2256
+
2257
+ <!-- Terminal Panel -->
2258
+ <div id="terminal">
2259
+ <div id="terminal-header">
2260
+ <div id="terminal-title">Activity Trace</div>
2261
+ <div class="terminal-actions">
2262
+ <button id="preview-diff-trigger" class="terminal-btn" type="button">Preview Diff</button>
2263
+ </div>
2264
+ </div>
2265
+ <div id="terminal-content"></div>
2266
+ </div>
2267
+
2268
+ <!-- Status Bar -->
2269
+ <div id="status-bar">
2270
+ <div>
2271
+ <span id="status-text">Ready</span>
2272
+ </div>
2273
+ <div style="display: flex; gap: 16px; align-items: center;">
2274
+ <span id="connection-status" style="display: flex; align-items: center; gap: 4px;">
2275
+ <span style="width: 6px; height: 6px; border-radius: 50%; background: var(--green);"></span>
2276
+ Connected
2277
+ </span>
2278
+ <a href="http://127.0.0.1:4319" target="_blank">
2279
+ ⚙️ Dashboard
2280
+ </a>
2281
+ </div>
2282
+ </div>
2283
+ </div>
2284
+
2285
+ <div id="settings-panel" role="dialog" aria-modal="false" aria-hidden="true" aria-labelledby="settings-panel-title">
2286
+ <div class="settings-panel-header">
2287
+ <div>
2288
+ <h3 id="settings-panel-title">Vibe Settings</h3>
2289
+ <p>Quick access to workspace controls and the main crewswarm dashboard.</p>
2290
+ </div>
2291
+ <button
2292
+ id="settings-close"
2293
+ class="icon-btn settings-panel-close"
2294
+ type="button"
2295
+ aria-label="Close settings panel"
2296
+ >
2297
+ ×
2298
+ </button>
2299
+ </div>
2300
+ <div class="settings-panel-section">
2301
+ <span class="settings-panel-section-label">Navigation</span>
2302
+ <strong>Main Dashboard</strong>
2303
+ <p>Open the dashboard for providers, services, agents, and runtime controls.</p>
2304
+ <a
2305
+ class="settings-dashboard-link"
2306
+ href="http://127.0.0.1:4319"
2307
+ target="_blank"
2308
+ rel="noreferrer"
2309
+ >
2310
+ Link to Main Dashboard
2311
+ </a>
2312
+ </div>
2313
+ </div>
2314
+
2315
+ <aside id="shortcuts-panel" class="hidden" role="complementary" aria-hidden="true" aria-labelledby="shortcuts-panel-title">
2316
+ <div class="shortcuts-panel-header">
2317
+ <div>
2318
+ <h3 id="shortcuts-panel-title">Keyboard Shortcuts Guide</h3>
2319
+ <p>Keep the core editing and navigation actions one keystroke away. Reopen this panel anytime from the title bar.</p>
2320
+ </div>
2321
+ <button
2322
+ id="shortcuts-close"
2323
+ class="icon-btn shortcuts-panel-close"
2324
+ type="button"
2325
+ aria-label="Hide keyboard shortcuts guide"
2326
+ >
2327
+ ×
2328
+ </button>
2329
+ </div>
2330
+
2331
+ <div class="shortcuts-callout">
2332
+ <strong>Tip</strong>
2333
+ <span>Prefer familiar conventions, avoid overriding browser defaults unless the payoff is clear, and always keep a clickable UI path alongside the shortcut.</span>
2334
+ </div>
2335
+
2336
+ <div class="shortcuts-grid" aria-label="Essential keyboard shortcuts">
2337
+ <article class="shortcut-card"><kbd>Cmd/Ctrl</kbd><kbd>K</kbd><strong>Inline AI</strong><p>Open the Monaco inline assistant from the current cursor position.</p></article>
2338
+ <article class="shortcut-card"><kbd>Enter</kbd><strong>Send chat</strong><p>Submit the main chat input without leaving the keyboard.</p></article>
2339
+ <article class="shortcut-card"><kbd>Shift</kbd><kbd>Enter</kbd><strong>New line</strong><p>Insert a line break in chat instead of sending immediately.</p></article>
2340
+ <article class="shortcut-card"><kbd>Esc</kbd><strong>Dismiss overlays</strong><p>Close inline chat, diff review, or any temporary modal state.</p></article>
2341
+ <article class="shortcut-card"><kbd>Cmd/Ctrl</kbd><kbd>Z</kbd><strong>Undo</strong><p>Use Monaco's built-in undo stack while editing the current file.</p></article>
2342
+ <article class="shortcut-card"><kbd>Cmd/Ctrl</kbd><kbd>Shift</kbd><kbd>Z</kbd><strong>Redo</strong><p>Replay an undone change in the active Monaco editor.</p></article>
2343
+ <article class="shortcut-card"><kbd>Cmd/Ctrl</kbd><kbd>/</kbd><strong>Toggle comment</strong><p>Comment the current line or selected block while editing.</p></article>
2344
+ <article class="shortcut-card"><kbd>Cmd/Ctrl</kbd><kbd>S</kbd><strong>Save file</strong><p>Commit the current edit to disk before switching context.</p></article>
2345
+ </div>
2346
+
2347
+ <section class="shortcuts-practices" aria-label="Keyboard shortcut design best practices">
2348
+ <h4>Best Practices</h4>
2349
+ <ul>
2350
+ <li>Reserve the easiest shortcuts for high-frequency actions such as opening files, saving, and invoking AI help.</li>
2351
+ <li>Match platform habits when possible so users can transfer muscle memory from editors like VS Code.</li>
2352
+ <li>Keep destructive commands out of single-key bindings unless there is a clear confirmation step.</li>
2353
+ <li>Expose every shortcut in tooltips, menus, or guides so discoverability does not depend on memory alone.</li>
2354
+ <li>Design shortcuts to work with one hand where practical, especially for actions users repeat dozens of times per session.</li>
2355
+ </ul>
2356
+ </section>
2357
+ </aside>
2358
+
2359
+ <!-- Inline Chat Overlay (Cmd+K) -->
2360
+ <div id="inline-chat-overlay">
2361
+ <div id="inline-chat-box">
2362
+ <div class="inline-chat-header">
2363
+ <div class="inline-chat-title">
2364
+ <strong>Ask Monaco inline</strong>
2365
+ <span id="inline-chat-context">Anchored to the current cursor position</span>
2366
+ </div>
2367
+ <button
2368
+ type="button"
2369
+ class="inline-chat-close"
2370
+ aria-label="Close inline chat"
2371
+ onclick="hideInlineChat()"
2372
+ >
2373
+ ×
2374
+ </button>
2375
+ </div>
2376
+ <div class="inline-chat-controls">
2377
+ <select id="inline-chat-model" aria-label="AI model">
2378
+ <option value="codex">Codex</option>
2379
+ <option value="gpt-5">GPT-5 (Unavailable in local mode)</option>
2380
+ <option value="gpt-5-mini">GPT-5 Mini (Unavailable in local mode)</option>
2381
+ </select>
2382
+ <div class="inline-chat-shortcut">Enter to send • Esc to close</div>
2383
+ </div>
2384
+ <textarea id="inline-chat-input" placeholder="Ask for a refactor, explanation, or quick edit suggestion..."></textarea>
2385
+ <div id="inline-chat-response" aria-live="polite">
2386
+ <div
2387
+ id="inline-chat-answer"
2388
+ class="inline-chat-answer"
2389
+ data-empty="true"
2390
+ >No response yet.</div>
2391
+ <details
2392
+ id="inline-chat-transcript"
2393
+ class="inline-chat-transcript"
2394
+ hidden
2395
+ >
2396
+ <summary>Show transcript</summary>
2397
+ <pre id="inline-chat-transcript-body"></pre>
2398
+ </details>
2399
+ </div>
2400
+ <div class="actions">
2401
+ <div class="inline-chat-meta" id="inline-chat-meta">No response yet</div>
2402
+ <div class="action-buttons">
2403
+ <button class="secondary" onclick="hideInlineChat()">Cancel</button>
2404
+ <button onclick="sendInlineChat()">Send</button>
2405
+ </div>
2406
+ </div>
2407
+ </div>
2408
+ </div>
2409
+
2410
+ <!-- Diff Preview Modal -->
2411
+ <div id="diff-preview-overlay" class="diff-preview-modal" aria-hidden="true">
2412
+ <div id="diff-preview-box" role="dialog" aria-modal="true" aria-labelledby="diff-preview-title">
2413
+ <div class="header">
2414
+ <div>
2415
+ <h3 id="diff-preview-title">Review Proposed Changes</h3>
2416
+ <div class="file-path" id="diff-file-path"></div>
2417
+ </div>
2418
+ <div class="diff-preview-toolbar">
2419
+ <div class="diff-preview-pill" id="diff-preview-badge">Sample agent patch</div>
2420
+ <div class="diff-preview-view-toggle" role="tablist" aria-label="Diff view mode">
2421
+ <button type="button" class="active" data-diff-view="side-by-side">Side by side</button>
2422
+ <button type="button" data-diff-view="inline">Inline</button>
2423
+ </div>
2424
+ <button type="button" class="diff-preview-close" aria-label="Close diff preview">×</button>
2425
+ </div>
2426
+ </div>
2427
+ <div id="diff-editor">
2428
+ <div class="diff-preview-shell">
2429
+ <div class="diff-preview-summary">
2430
+ <div class="diff-preview-stat">
2431
+ <div class="label">Additions</div>
2432
+ <div class="value" id="diff-summary-additions">0</div>
2433
+ </div>
2434
+ <div class="diff-preview-stat">
2435
+ <div class="label">Deletions</div>
2436
+ <div class="value" id="diff-summary-deletions">0</div>
2437
+ </div>
2438
+ <div class="diff-preview-stat">
2439
+ <div class="label">Substitutions</div>
2440
+ <div class="value" id="diff-summary-substitutions">0</div>
2441
+ </div>
2442
+ </div>
2443
+ <div style="padding:0 22px 16px;color:var(--text-2);font-size:12px;">
2444
+ Review first, then click <strong>Looks Good</strong> to write the proposed file content to disk.
2445
+ </div>
2446
+ <div class="diff-preview-render" id="diff-preview-render"></div>
2447
+ </div>
2448
+ </div>
2449
+ <div class="actions">
2450
+ <button class="secondary" onclick="rejectDiff()">Dismiss</button>
2451
+ <button onclick="acceptDiff()">Looks Good</button>
2452
+ </div>
2453
+ </div>
2454
+ </div>
2455
+
2456
+ <!-- New Project Modal -->
2457
+ <div id="new-project-overlay" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(6,10,16,0.9);backdrop-filter:blur(4px);display:none;align-items:center;justify-content:center;z-index:1000;">
2458
+ <div style="background:var(--bg-card);border:1px solid var(--border-hi);border-radius:var(--radius);width:500px;max-width:90vw;">
2459
+ <div style="padding:16px 20px;border-bottom:1px solid var(--border);">
2460
+ <h3 style="font-size:14px;font-weight:600;color:var(--text);margin:0;">Create New Project</h3>
2461
+ </div>
2462
+ <div style="padding:20px;">
2463
+ <div style="margin-bottom:16px;">
2464
+ <label for="new-project-name" style="display:block;font-size:12px;font-weight:600;color:var(--text-2);margin-bottom:6px;">Project Name</label>
2465
+ <input id="new-project-name" type="text" placeholder="My Awesome Project" style="width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);padding:10px 12px;border-radius:6px;font-size:13px;font-family:inherit;outline:none;">
2466
+ </div>
2467
+ <div style="margin-bottom:16px;">
2468
+ <label for="new-project-desc" style="display:block;font-size:12px;font-weight:600;color:var(--text-2);margin-bottom:6px;">Description</label>
2469
+ <textarea id="new-project-desc" placeholder="What are you building?" rows="3" style="width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);padding:10px 12px;border-radius:6px;font-size:13px;font-family:inherit;outline:none;resize:vertical;"></textarea>
2470
+ </div>
2471
+ <div>
2472
+ <label for="new-project-dir" style="display:block;font-size:12px;font-weight:600;color:var(--text-2);margin-bottom:6px;">Output Directory</label>
2473
+ <input id="new-project-dir" type="text" placeholder="/Users/you/projects/my-project" style="width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);padding:10px 12px;border-radius:6px;font-size:13px;font-family:inherit;outline:none;">
2474
+ </div>
2475
+ </div>
2476
+ <div style="padding:16px 20px;border-top:1px solid var(--border);display:flex;gap:12px;justify-content:flex-end;">
2477
+ <button class="secondary" onclick="hideNewProjectModal()" style="background:transparent;border:1px solid var(--border);color:var(--text-2);padding:10px 20px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">Cancel</button>
2478
+ <button onclick="createNewProject()" style="background:var(--accent);border:none;color:#0a0a12;padding:10px 20px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">Create Project</button>
2479
+ </div>
2480
+ </div>
2481
+ </div>
2482
+
2483
+ <script type="module" src="./src/main.js"></script>
2484
+ <script>
2485
+ window.addEventListener("DOMContentLoaded", () => {
2486
+ const root = document.documentElement;
2487
+ const toggleButton = document.getElementById("toggle-bottom-terminal");
2488
+ const themeToggle = document.getElementById("theme-toggle");
2489
+ const terminalPanel = document.getElementById("bottom-terminal-panel");
2490
+ const terminalMount = document.getElementById("terminal-instance");
2491
+ const terminalResizer = document.getElementById("terminal-resizer");
2492
+ const terminalCloseButton = document.getElementById("close-bottom-terminal");
2493
+ const terminalClearButton = document.getElementById("clear-bottom-terminal");
2494
+ const terminalStatus = document.getElementById("bottom-terminal-status");
2495
+ const editorWorkspace = document.getElementById("editor-workspace");
2496
+ const editorMain = document.getElementById("editor-main");
2497
+ const app = document.getElementById("app");
2498
+ const sidebarResizer = document.getElementById("sidebar-resizer");
2499
+ const chatResizer = document.getElementById("chat-resizer");
2500
+ const outputResizer = document.getElementById("output-resizer");
2501
+ const compactLayout = window.matchMedia("(max-width: 900px)");
2502
+ const systemColorScheme = window.matchMedia("(prefers-color-scheme: dark)");
2503
+ let bottomTerminal = null;
2504
+ let terminalReady = false;
2505
+ let terminalOpen = false;
2506
+ let terminalSessionId = null;
2507
+ let terminalSocket = null;
2508
+ let terminalProjectDir =
2509
+ typeof window.__studioGetCurrentProjectDir === "function"
2510
+ ? window.__studioGetCurrentProjectDir()
2511
+ : "apps/vibe";
2512
+ let dragState = null;
2513
+ let panelDragState = null;
2514
+
2515
+ function clamp(value, min, max) {
2516
+ return Math.min(Math.max(value, min), max);
2517
+ }
2518
+
2519
+ function getResolvedTheme() {
2520
+ const storedTheme = localStorage.getItem("theme");
2521
+ if (storedTheme === "light" || storedTheme === "dark") {
2522
+ return storedTheme;
2523
+ }
2524
+ return systemColorScheme.matches ? "dark" : "light";
2525
+ }
2526
+
2527
+ function getTerminalTheme(mode) {
2528
+ if (mode === "light") {
2529
+ return {
2530
+ background: "#f8fbff",
2531
+ foreground: "#0f172a",
2532
+ cursor: "#0ea5e9",
2533
+ black: "#e2e8f0",
2534
+ brightBlack: "#94a3b8",
2535
+ red: "#ef4444",
2536
+ brightRed: "#dc2626",
2537
+ green: "#10b981",
2538
+ brightGreen: "#059669",
2539
+ yellow: "#f59e0b",
2540
+ brightYellow: "#d97706",
2541
+ blue: "#2563eb",
2542
+ brightBlue: "#0ea5e9",
2543
+ magenta: "#4f46e5",
2544
+ brightMagenta: "#6366f1",
2545
+ cyan: "#0891b2",
2546
+ brightCyan: "#06b6d4",
2547
+ white: "#334155",
2548
+ brightWhite: "#0f172a",
2549
+ };
2550
+ }
2551
+
2552
+ return {
2553
+ background: "#040810",
2554
+ foreground: "#f0f6ff",
2555
+ cursor: "#38bdf8",
2556
+ black: "#060a10",
2557
+ brightBlack: "#4a5568",
2558
+ red: "#f87171",
2559
+ brightRed: "#ef4444",
2560
+ green: "#34d399",
2561
+ brightGreen: "#22c55e",
2562
+ yellow: "#fbbf24",
2563
+ brightYellow: "#f59e0b",
2564
+ blue: "#4a7ab5",
2565
+ brightBlue: "#38bdf8",
2566
+ magenta: "#818cf8",
2567
+ brightMagenta: "#a5b4fc",
2568
+ cyan: "#38bdf8",
2569
+ brightCyan: "#67e8f9",
2570
+ white: "#cbd5e1",
2571
+ brightWhite: "#f8fafc",
2572
+ };
2573
+ }
2574
+
2575
+ function applyTheme(nextTheme, source = "saved") {
2576
+ const mode = nextTheme === "dark" ? "dark" : "light";
2577
+ const storageValue = source === "system" ? null : mode;
2578
+
2579
+ root.classList.toggle("dark", mode === "dark");
2580
+ root.setAttribute("data-theme", storageValue || "system");
2581
+ root.style.colorScheme = mode;
2582
+
2583
+ if (storageValue) {
2584
+ localStorage.setItem("theme", storageValue);
2585
+ } else {
2586
+ localStorage.removeItem("theme");
2587
+ }
2588
+
2589
+ themeToggle?.setAttribute("aria-pressed", String(mode === "dark"));
2590
+ themeToggle?.setAttribute(
2591
+ "aria-label",
2592
+ mode === "dark" ? "Switch to light theme" : "Switch to dark theme",
2593
+ );
2594
+ themeToggle?.setAttribute(
2595
+ "title",
2596
+ mode === "dark" ? "Switch to light theme" : "Switch to dark theme",
2597
+ );
2598
+
2599
+ if (bottomTerminal) {
2600
+ bottomTerminal.options.theme = getTerminalTheme(mode);
2601
+ requestAnimationFrame(() => {
2602
+ fitBottomTerminal();
2603
+ bottomTerminal.refresh(0, bottomTerminal.rows - 1);
2604
+ });
2605
+ }
2606
+
2607
+ if (window.monaco?.editor?.setTheme) {
2608
+ window.monaco.editor.setTheme(mode === "dark" ? "vs-dark" : "vs");
2609
+ }
2610
+
2611
+ window.dispatchEvent(
2612
+ new CustomEvent("studio-themechange", {
2613
+ detail: { theme: mode, source },
2614
+ }),
2615
+ );
2616
+ }
2617
+
2618
+ function setHorizontalPanelWidths(sidebarWidth, chatWidth) {
2619
+ if (!app || compactLayout.matches) return;
2620
+ const totalWidth = app.clientWidth;
2621
+ const reserved = 16;
2622
+ const maxSidebar = Math.max(100, Math.floor((totalWidth - reserved) * 0.4));
2623
+ const maxChat = Math.max(100, Math.floor((totalWidth - reserved) * 0.45));
2624
+ const nextSidebar = clamp(sidebarWidth, 100, maxSidebar);
2625
+ const remainingForCenter = totalWidth - reserved - nextSidebar - 100;
2626
+ const nextChat = clamp(chatWidth, 100, Math.min(maxChat, remainingForCenter));
2627
+ app.style.setProperty("--sidebar-width", `${nextSidebar}px`);
2628
+ app.style.setProperty("--chat-width", `${nextChat}px`);
2629
+ }
2630
+
2631
+ function setOutputPanelHeight(height) {
2632
+ if (!app || compactLayout.matches) return;
2633
+ const totalHeight = app.clientHeight;
2634
+ const reserved = 88;
2635
+ const maxHeight = Math.max(100, totalHeight - reserved - 100);
2636
+ const nextHeight = clamp(height, 100, maxHeight);
2637
+ app.style.setProperty("--output-height", `${nextHeight}px`);
2638
+ }
2639
+
2640
+ function syncPanelLayout() {
2641
+ if (!app || compactLayout.matches) return;
2642
+ const computed = window.getComputedStyle(app);
2643
+ const sidebarWidth = parseFloat(computed.getPropertyValue("--sidebar-width")) || 250;
2644
+ const chatWidth = parseFloat(computed.getPropertyValue("--chat-width")) || 400;
2645
+ const outputHeight = parseFloat(computed.getPropertyValue("--output-height")) || 220;
2646
+ setHorizontalPanelWidths(sidebarWidth, chatWidth);
2647
+ setOutputPanelHeight(outputHeight);
2648
+ }
2649
+
2650
+ function fitBottomTerminal() {
2651
+ if (!bottomTerminal || !terminalOpen) return;
2652
+ const viewport = terminalMount?.querySelector(".xterm-viewport");
2653
+ const screen = terminalMount?.querySelector(".xterm-screen");
2654
+ if (!viewport || !screen) return;
2655
+ const style = window.getComputedStyle(terminalMount);
2656
+ const paddingX =
2657
+ parseFloat(style.paddingLeft || "0") + parseFloat(style.paddingRight || "0");
2658
+ const paddingY =
2659
+ parseFloat(style.paddingTop || "0") + parseFloat(style.paddingBottom || "0");
2660
+ const availableWidth = Math.max(terminalMount.clientWidth - paddingX, 120);
2661
+ const availableHeight = Math.max(terminalMount.clientHeight - paddingY, 72);
2662
+ const cellWidth = screen.clientWidth / Math.max(bottomTerminal.cols || 1, 1) || 9;
2663
+ const cellHeight = screen.clientHeight / Math.max(bottomTerminal.rows || 1, 1) || 18;
2664
+ const cols = clamp(Math.floor(availableWidth / Math.max(cellWidth, 7)), 20, 240);
2665
+ const rows = clamp(Math.floor(availableHeight / Math.max(cellHeight, 14)), 5, 80);
2666
+ bottomTerminal.resize(cols, rows);
2667
+ }
2668
+
2669
+ function ensureBottomTerminal() {
2670
+ if (terminalReady || !terminalMount) return;
2671
+ if (!window.Terminal) {
2672
+ throw new Error("Terminal UI failed to load");
2673
+ }
2674
+ bottomTerminal = new window.Terminal({
2675
+ cursorBlink: true,
2676
+ convertEol: true,
2677
+ fontFamily: "SF Mono, Menlo, Monaco, Consolas, monospace",
2678
+ fontSize: 13,
2679
+ lineHeight: 1.3,
2680
+ theme: getTerminalTheme(getResolvedTheme()),
2681
+ });
2682
+ bottomTerminal.open(terminalMount);
2683
+ bottomTerminal.onData((data) => {
2684
+ if (terminalSocket?.readyState === WebSocket.OPEN) {
2685
+ terminalSocket.send(JSON.stringify({ type: "input", data }));
2686
+ }
2687
+ });
2688
+ bottomTerminal.onResize(({ cols, rows }) => {
2689
+ if (terminalSocket?.readyState === WebSocket.OPEN) {
2690
+ terminalSocket.send(JSON.stringify({ type: "resize", cols, rows }));
2691
+ }
2692
+ });
2693
+ terminalReady = true;
2694
+ requestAnimationFrame(fitBottomTerminal);
2695
+ }
2696
+
2697
+ function updateTerminalStatus(text) {
2698
+ if (terminalStatus) {
2699
+ terminalStatus.textContent = text;
2700
+ }
2701
+ }
2702
+
2703
+ async function connectBottomTerminal() {
2704
+ ensureBottomTerminal();
2705
+ if (terminalSocket?.readyState === WebSocket.OPEN) {
2706
+ return;
2707
+ }
2708
+
2709
+ if (terminalSocket) {
2710
+ terminalSocket.close();
2711
+ terminalSocket = null;
2712
+ }
2713
+
2714
+ updateTerminalStatus(`Starting shell in ${terminalProjectDir}`);
2715
+ bottomTerminal?.clear();
2716
+ bottomTerminal?.writeln("Starting workspace shell...");
2717
+
2718
+ const response = await fetch(`${window.location.origin}/api/studio/terminal/start`, {
2719
+ method: "POST",
2720
+ headers: { "Content-Type": "application/json" },
2721
+ body: JSON.stringify({
2722
+ projectDir: terminalProjectDir,
2723
+ cols: bottomTerminal?.cols || 120,
2724
+ rows: bottomTerminal?.rows || 32,
2725
+ }),
2726
+ });
2727
+ const payload = await response.json();
2728
+ if (!response.ok) {
2729
+ throw new Error(payload.error || `HTTP ${response.status}`);
2730
+ }
2731
+
2732
+ terminalSessionId = payload.sessionId;
2733
+ terminalProjectDir = payload.cwd || terminalProjectDir;
2734
+ updateTerminalStatus(terminalProjectDir);
2735
+
2736
+ const socketProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
2737
+ const socketUrl = new URL(`${socketProtocol}//${window.location.host}/ws/studio/terminal`);
2738
+ socketUrl.searchParams.set("sessionId", terminalSessionId);
2739
+ terminalSocket = new WebSocket(socketUrl);
2740
+
2741
+ terminalSocket.addEventListener("message", (event) => {
2742
+ try {
2743
+ const message = JSON.parse(event.data);
2744
+ if (message.type === "output" && typeof message.data === "string") {
2745
+ bottomTerminal?.write(message.data);
2746
+ return;
2747
+ }
2748
+ if (message.type === "ready") {
2749
+ updateTerminalStatus(message.cwd || terminalProjectDir);
2750
+ requestAnimationFrame(fitBottomTerminal);
2751
+ return;
2752
+ }
2753
+ if (message.type === "exit") {
2754
+ bottomTerminal?.writeln(`\r\n[process exited ${message.exitCode}]`);
2755
+ updateTerminalStatus(`Shell exited in ${terminalProjectDir}`);
2756
+ terminalSocket = null;
2757
+ terminalSessionId = null;
2758
+ return;
2759
+ }
2760
+ if (message.type === "error") {
2761
+ bottomTerminal?.writeln(`\r\n[error] ${message.message}`);
2762
+ }
2763
+ } catch (error) {
2764
+ bottomTerminal?.writeln(`\r\n[terminal parse error] ${error.message}`);
2765
+ }
2766
+ });
2767
+
2768
+ terminalSocket.addEventListener("open", () => {
2769
+ requestAnimationFrame(() => {
2770
+ fitBottomTerminal();
2771
+ if (terminalSocket?.readyState === WebSocket.OPEN && bottomTerminal) {
2772
+ terminalSocket.send(
2773
+ JSON.stringify({
2774
+ type: "resize",
2775
+ cols: bottomTerminal.cols,
2776
+ rows: bottomTerminal.rows,
2777
+ }),
2778
+ );
2779
+ }
2780
+ bottomTerminal?.focus();
2781
+ });
2782
+ });
2783
+
2784
+ terminalSocket.addEventListener("close", () => {
2785
+ terminalSocket = null;
2786
+ terminalSessionId = null;
2787
+ });
2788
+ }
2789
+
2790
+ async function restartBottomTerminal(projectDir) {
2791
+ terminalProjectDir = projectDir || terminalProjectDir;
2792
+ if (terminalSessionId) {
2793
+ fetch(
2794
+ `${window.location.origin}/api/studio/terminal?sessionId=${encodeURIComponent(terminalSessionId)}`,
2795
+ { method: "DELETE" },
2796
+ ).catch(() => {});
2797
+ terminalSessionId = null;
2798
+ }
2799
+ if (terminalSocket) {
2800
+ terminalSocket.close();
2801
+ terminalSocket = null;
2802
+ }
2803
+ if (terminalOpen) {
2804
+ await connectBottomTerminal();
2805
+ } else {
2806
+ updateTerminalStatus(`Local shell for ${terminalProjectDir}`);
2807
+ }
2808
+ }
2809
+
2810
+ function setBottomTerminalOpen(nextOpen) {
2811
+ terminalOpen = Boolean(nextOpen);
2812
+ if (terminalOpen) {
2813
+ connectBottomTerminal().catch((error) => {
2814
+ bottomTerminal?.writeln(`\r\n[terminal error] ${error.message}`);
2815
+ updateTerminalStatus("Failed to start shell");
2816
+ });
2817
+ terminalPanel.classList.add("visible");
2818
+ terminalPanel.setAttribute("aria-hidden", "false");
2819
+ toggleButton.textContent = "Hide Terminal";
2820
+ requestAnimationFrame(() => {
2821
+ fitBottomTerminal();
2822
+ bottomTerminal?.focus();
2823
+ });
2824
+ return;
2825
+ }
2826
+ terminalPanel.classList.remove("visible");
2827
+ terminalPanel.setAttribute("aria-hidden", "true");
2828
+ terminalPanel.style.flexBasis = "";
2829
+ toggleButton.textContent = "Terminal";
2830
+ }
2831
+
2832
+ function setTerminalHeight(nextHeightPx) {
2833
+ if (!editorWorkspace || !editorMain) return;
2834
+ const workspaceHeight = editorWorkspace.clientHeight;
2835
+ const clamped = clamp(nextHeightPx, 180, Math.max(workspaceHeight - 160, 180));
2836
+ terminalPanel.style.flexBasis = `${clamped}px`;
2837
+ requestAnimationFrame(fitBottomTerminal);
2838
+ }
2839
+
2840
+ toggleButton?.addEventListener("click", () => {
2841
+ setBottomTerminalOpen(!terminalOpen);
2842
+ });
2843
+
2844
+ terminalCloseButton?.addEventListener("click", () => {
2845
+ setBottomTerminalOpen(false);
2846
+ });
2847
+
2848
+ terminalClearButton?.addEventListener("click", () => {
2849
+ if (!bottomTerminal) return;
2850
+ bottomTerminal.clear();
2851
+ bottomTerminal.focus();
2852
+ });
2853
+
2854
+ terminalMount?.addEventListener("pointerdown", () => {
2855
+ bottomTerminal?.focus();
2856
+ });
2857
+
2858
+ terminalResizer?.addEventListener("pointerdown", (event) => {
2859
+ if (!terminalOpen) return;
2860
+ dragState = {
2861
+ startY: event.clientY,
2862
+ startHeight: terminalPanel.getBoundingClientRect().height,
2863
+ };
2864
+ terminalPanel.classList.add("dragging");
2865
+ terminalResizer.setPointerCapture(event.pointerId);
2866
+ event.preventDefault();
2867
+ });
2868
+
2869
+ terminalResizer?.addEventListener("pointermove", (event) => {
2870
+ if (!dragState) return;
2871
+ const delta = dragState.startY - event.clientY;
2872
+ setTerminalHeight(dragState.startHeight + delta);
2873
+ });
2874
+
2875
+ function stopTerminalDrag(event) {
2876
+ if (!dragState) return;
2877
+ dragState = null;
2878
+ terminalPanel.classList.remove("dragging");
2879
+ if (event?.pointerId !== undefined) {
2880
+ terminalResizer?.releasePointerCapture(event.pointerId);
2881
+ }
2882
+ }
2883
+
2884
+ terminalResizer?.addEventListener("pointerup", stopTerminalDrag);
2885
+ terminalResizer?.addEventListener("pointercancel", stopTerminalDrag);
2886
+
2887
+ terminalResizer?.addEventListener("keydown", (event) => {
2888
+ if (!terminalOpen) return;
2889
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
2890
+ event.preventDefault();
2891
+ const direction = event.key === "ArrowUp" ? 24 : -24;
2892
+ const currentHeight = terminalPanel.getBoundingClientRect().height || 0;
2893
+ setTerminalHeight(currentHeight + direction);
2894
+ });
2895
+
2896
+ window.addEventListener("resize", fitBottomTerminal);
2897
+ window.addEventListener("studio-projectchange", (event) => {
2898
+ const nextProjectDir = event.detail?.projectDir;
2899
+ if (!nextProjectDir || nextProjectDir === terminalProjectDir) return;
2900
+ restartBottomTerminal(nextProjectDir).catch((error) => {
2901
+ updateTerminalStatus(`Failed to switch shell: ${error.message}`);
2902
+ });
2903
+ });
2904
+
2905
+ function startPanelDrag(event, nextState) {
2906
+ if (compactLayout.matches) return;
2907
+ panelDragState = nextState;
2908
+ nextState.handle.classList.add("dragging");
2909
+ nextState.handle.setPointerCapture(event.pointerId);
2910
+ event.preventDefault();
2911
+ }
2912
+
2913
+ function stopPanelDrag(event) {
2914
+ if (!panelDragState) return;
2915
+ panelDragState.handle.classList.remove("dragging");
2916
+ if (event?.pointerId !== undefined) {
2917
+ panelDragState.handle.releasePointerCapture(event.pointerId);
2918
+ }
2919
+ panelDragState = null;
2920
+ }
2921
+
2922
+ sidebarResizer?.addEventListener("pointerdown", (event) => {
2923
+ startPanelDrag(event, {
2924
+ type: "sidebar",
2925
+ startX: event.clientX,
2926
+ startWidth:
2927
+ document.getElementById("sidebar")?.getBoundingClientRect().width || 250,
2928
+ handle: sidebarResizer,
2929
+ });
2930
+ });
2931
+
2932
+ chatResizer?.addEventListener("pointerdown", (event) => {
2933
+ startPanelDrag(event, {
2934
+ type: "chat",
2935
+ startX: event.clientX,
2936
+ startWidth:
2937
+ document.getElementById("chat-panel")?.getBoundingClientRect().width || 400,
2938
+ handle: chatResizer,
2939
+ });
2940
+ });
2941
+
2942
+ outputResizer?.addEventListener("pointerdown", (event) => {
2943
+ startPanelDrag(event, {
2944
+ type: "output",
2945
+ startY: event.clientY,
2946
+ startHeight:
2947
+ document.getElementById("terminal")?.getBoundingClientRect().height || 220,
2948
+ handle: outputResizer,
2949
+ });
2950
+ });
2951
+
2952
+ function handlePanelDrag(event) {
2953
+ if (!panelDragState) return;
2954
+ if (panelDragState.type === "sidebar") {
2955
+ const delta = event.clientX - panelDragState.startX;
2956
+ const chatWidth =
2957
+ document.getElementById("chat-panel")?.getBoundingClientRect().width || 400;
2958
+ setHorizontalPanelWidths(panelDragState.startWidth + delta, chatWidth);
2959
+ return;
2960
+ }
2961
+
2962
+ if (panelDragState.type === "chat") {
2963
+ const delta = panelDragState.startX - event.clientX;
2964
+ const sidebarWidth =
2965
+ document.getElementById("sidebar")?.getBoundingClientRect().width || 250;
2966
+ setHorizontalPanelWidths(sidebarWidth, panelDragState.startWidth + delta);
2967
+ return;
2968
+ }
2969
+
2970
+ const delta = panelDragState.startY - event.clientY;
2971
+ setOutputPanelHeight(panelDragState.startHeight + delta);
2972
+ }
2973
+
2974
+ [sidebarResizer, chatResizer, outputResizer].forEach((handle) => {
2975
+ handle?.addEventListener("pointermove", handlePanelDrag);
2976
+ handle?.addEventListener("pointerup", stopPanelDrag);
2977
+ handle?.addEventListener("pointercancel", stopPanelDrag);
2978
+ });
2979
+
2980
+ sidebarResizer?.addEventListener("keydown", (event) => {
2981
+ if (compactLayout.matches) return;
2982
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
2983
+ event.preventDefault();
2984
+ const direction = event.key === "ArrowRight" ? 24 : -24;
2985
+ const sidebarWidth =
2986
+ document.getElementById("sidebar")?.getBoundingClientRect().width || 250;
2987
+ const chatWidth =
2988
+ document.getElementById("chat-panel")?.getBoundingClientRect().width || 400;
2989
+ setHorizontalPanelWidths(sidebarWidth + direction, chatWidth);
2990
+ });
2991
+
2992
+ chatResizer?.addEventListener("keydown", (event) => {
2993
+ if (compactLayout.matches) return;
2994
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
2995
+ event.preventDefault();
2996
+ const direction = event.key === "ArrowLeft" ? 24 : -24;
2997
+ const sidebarWidth =
2998
+ document.getElementById("sidebar")?.getBoundingClientRect().width || 250;
2999
+ const chatWidth =
3000
+ document.getElementById("chat-panel")?.getBoundingClientRect().width || 400;
3001
+ setHorizontalPanelWidths(sidebarWidth, chatWidth + direction);
3002
+ });
3003
+
3004
+ outputResizer?.addEventListener("keydown", (event) => {
3005
+ if (compactLayout.matches) return;
3006
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
3007
+ event.preventDefault();
3008
+ const direction = event.key === "ArrowUp" ? 24 : -24;
3009
+ const height =
3010
+ document.getElementById("terminal")?.getBoundingClientRect().height || 220;
3011
+ setOutputPanelHeight(height + direction);
3012
+ });
3013
+
3014
+ if (typeof compactLayout.addEventListener === "function") {
3015
+ compactLayout.addEventListener("change", syncPanelLayout);
3016
+ } else if (typeof compactLayout.addListener === "function") {
3017
+ compactLayout.addListener(syncPanelLayout);
3018
+ }
3019
+ syncPanelLayout();
3020
+ applyTheme(getResolvedTheme(), localStorage.getItem("theme") ? "saved" : "system");
3021
+
3022
+ themeToggle?.addEventListener("click", () => {
3023
+ const nextTheme = root.classList.contains("dark") ? "light" : "dark";
3024
+ applyTheme(nextTheme, "saved");
3025
+ });
3026
+
3027
+ const handleSystemThemeChange = () => {
3028
+ if (localStorage.getItem("theme")) return;
3029
+ applyTheme(getResolvedTheme(), "system");
3030
+ };
3031
+
3032
+ if (typeof systemColorScheme.addEventListener === "function") {
3033
+ systemColorScheme.addEventListener("change", handleSystemThemeChange);
3034
+ } else if (typeof systemColorScheme.addListener === "function") {
3035
+ systemColorScheme.addListener(handleSystemThemeChange);
3036
+ }
3037
+
3038
+ const settingsToggle = document.getElementById("settings-toggle");
3039
+ const settingsPanel = document.getElementById("settings-panel");
3040
+ const settingsClose = document.getElementById("settings-close");
3041
+ const shortcutsToggle = document.getElementById("shortcuts-toggle");
3042
+ const shortcutsPanel = document.getElementById("shortcuts-panel");
3043
+ const shortcutsClose = document.getElementById("shortcuts-close");
3044
+ function setSettingsPanelOpen(nextOpen) {
3045
+ if (!settingsToggle || !settingsPanel) return;
3046
+ const open = Boolean(nextOpen);
3047
+ settingsPanel.classList.toggle("visible", open);
3048
+ settingsPanel.setAttribute("aria-hidden", open ? "false" : "true");
3049
+ settingsToggle.setAttribute("aria-expanded", open ? "true" : "false");
3050
+ }
3051
+
3052
+ settingsToggle?.addEventListener("click", () => {
3053
+ const isOpen = settingsPanel?.classList.contains("visible");
3054
+ setSettingsPanelOpen(!isOpen);
3055
+ });
3056
+
3057
+ settingsClose?.addEventListener("click", () => {
3058
+ setSettingsPanelOpen(false);
3059
+ settingsToggle?.focus();
3060
+ });
3061
+
3062
+ function setShortcutsPanelOpen(nextOpen) {
3063
+ if (!shortcutsToggle || !shortcutsPanel) return;
3064
+ const open = Boolean(nextOpen);
3065
+ shortcutsPanel.classList.toggle("hidden", !open);
3066
+ shortcutsPanel.setAttribute("aria-hidden", open ? "false" : "true");
3067
+ shortcutsToggle.setAttribute("aria-expanded", open ? "true" : "false");
3068
+ }
3069
+
3070
+ shortcutsToggle?.addEventListener("click", (event) => {
3071
+ event.stopPropagation();
3072
+ const isHidden = shortcutsPanel?.classList.contains("hidden");
3073
+ setShortcutsPanelOpen(isHidden);
3074
+ });
3075
+
3076
+ shortcutsClose?.addEventListener("click", (event) => {
3077
+ event.stopPropagation();
3078
+ setShortcutsPanelOpen(false);
3079
+ shortcutsToggle?.focus();
3080
+ });
3081
+
3082
+ document.addEventListener("mousedown", (event) => {
3083
+ if (shortcutsPanel?.classList.contains("hidden")) return;
3084
+ if (shortcutsPanel?.contains(event.target) || shortcutsToggle?.contains(event.target)) {
3085
+ return;
3086
+ }
3087
+ setShortcutsPanelOpen(false);
3088
+ });
3089
+
3090
+ document.addEventListener("mousedown", (event) => {
3091
+ if (!settingsPanel?.classList.contains("visible")) return;
3092
+ if (settingsPanel.contains(event.target) || settingsToggle?.contains(event.target)) {
3093
+ return;
3094
+ }
3095
+ setSettingsPanelOpen(false);
3096
+ });
3097
+
3098
+ document.addEventListener("mousedown", (event) => {
3099
+ if (shortcutsPanel?.classList.contains("hidden")) return;
3100
+ if (shortcutsPanel.contains(event.target) || shortcutsToggle?.contains(event.target)) {
3101
+ return;
3102
+ }
3103
+ setShortcutsPanelOpen(false);
3104
+ });
3105
+
3106
+ const overlay = document.getElementById("diff-preview-overlay");
3107
+ const box = document.getElementById("diff-preview-box");
3108
+ const renderRoot = document.getElementById("diff-preview-render");
3109
+ const filePathEl = document.getElementById("diff-file-path");
3110
+ const badgeEl = document.getElementById("diff-preview-badge");
3111
+ const previewButton = document.getElementById("preview-diff-trigger");
3112
+ const closeButton = box?.querySelector(".diff-preview-close");
3113
+ const viewButtons = Array.from(
3114
+ box?.querySelectorAll("[data-diff-view]") || [],
3115
+ );
3116
+ const statEls = {
3117
+ additions: document.getElementById("diff-summary-additions"),
3118
+ deletions: document.getElementById("diff-summary-deletions"),
3119
+ substitutions: document.getElementById("diff-summary-substitutions"),
3120
+ };
3121
+
3122
+ if (!overlay || !box || !renderRoot || !previewButton) return;
3123
+
3124
+ const sampleChange = {
3125
+ path: "src/components/AgentPanel.jsx",
3126
+ label: "Sample agent patch",
3127
+ oldContent: [
3128
+ "export function AgentPanel({ agents }) {",
3129
+ " return (",
3130
+ " <section className=\"agent-panel\">",
3131
+ " <h2>Active Agents</h2>",
3132
+ " <ul>{agents.map((agent) => <li key={agent.id}>{agent.name}</li>)}</ul>",
3133
+ " </section>",
3134
+ " );",
3135
+ "}",
3136
+ ].join("\\n"),
3137
+ newContent: [
3138
+ "export function AgentPanel({ agents, status }) {",
3139
+ " return (",
3140
+ " <section className=\"agent-panel agent-panel--elevated\">",
3141
+ " <header className=\"agent-panel__header\">",
3142
+ " <h2>Active Agents</h2>",
3143
+ " <span className=\"agent-panel__status\">{status}</span>",
3144
+ " </header>",
3145
+ " <ul>{agents.map((agent) => <li key={agent.id}>{agent.name}</li>)}</ul>",
3146
+ " </section>",
3147
+ " );",
3148
+ "}",
3149
+ ].join("\\n"),
3150
+ };
3151
+
3152
+ const state = {
3153
+ mode: "sample",
3154
+ activeView: "side-by-side",
3155
+ currentChange: sampleChange,
3156
+ originalAcceptDiff: window.acceptDiff,
3157
+ originalRejectDiff: window.rejectDiff,
3158
+ };
3159
+
3160
+ function escapeHtml(value) {
3161
+ return value
3162
+ .replace(/&/g, "&amp;")
3163
+ .replace(/</g, "&lt;")
3164
+ .replace(/>/g, "&gt;");
3165
+ }
3166
+
3167
+ function diffLines(oldText, newText) {
3168
+ const oldLines = oldText.split("\\n");
3169
+ const newLines = newText.split("\\n");
3170
+ const rows = [];
3171
+ const max = Math.max(oldLines.length, newLines.length);
3172
+ let additions = 0;
3173
+ let deletions = 0;
3174
+ let substitutions = 0;
3175
+
3176
+ for (let index = 0; index < max; index += 1) {
3177
+ const oldLine = oldLines[index];
3178
+ const newLine = newLines[index];
3179
+
3180
+ if (oldLine === newLine && oldLine !== undefined) {
3181
+ rows.push({
3182
+ type: "equal",
3183
+ oldNumber: index + 1,
3184
+ newNumber: index + 1,
3185
+ oldLine,
3186
+ newLine,
3187
+ });
3188
+ continue;
3189
+ }
3190
+
3191
+ if (oldLine !== undefined && newLine !== undefined) {
3192
+ substitutions += 1;
3193
+ rows.push({
3194
+ type: "replacement",
3195
+ oldNumber: index + 1,
3196
+ newNumber: index + 1,
3197
+ oldLine,
3198
+ newLine,
3199
+ });
3200
+ continue;
3201
+ }
3202
+
3203
+ if (oldLine === undefined) {
3204
+ additions += 1;
3205
+ rows.push({
3206
+ type: "addition",
3207
+ oldNumber: "",
3208
+ newNumber: index + 1,
3209
+ oldLine: "",
3210
+ newLine,
3211
+ });
3212
+ continue;
3213
+ }
3214
+
3215
+ deletions += 1;
3216
+ rows.push({
3217
+ type: "deletion",
3218
+ oldNumber: index + 1,
3219
+ newNumber: "",
3220
+ oldLine,
3221
+ newLine: "",
3222
+ });
3223
+ }
3224
+
3225
+ return { rows, summary: { additions, deletions, substitutions } };
3226
+ }
3227
+
3228
+ function updateViewButtons() {
3229
+ viewButtons.forEach((button) => {
3230
+ button.classList.toggle(
3231
+ "active",
3232
+ button.dataset.diffView === state.activeView,
3233
+ );
3234
+ });
3235
+ }
3236
+
3237
+ function updateSummary(summary) {
3238
+ statEls.additions.textContent = summary.additions;
3239
+ statEls.deletions.textContent = summary.deletions;
3240
+ statEls.substitutions.textContent = summary.substitutions;
3241
+ }
3242
+
3243
+ function sideBySideMarkup(rows) {
3244
+ const left = [];
3245
+ const right = [];
3246
+
3247
+ rows.forEach((row) => {
3248
+ const leftType =
3249
+ row.type === "addition" ? "placeholder" : row.type;
3250
+ const rightType =
3251
+ row.type === "deletion" ? "placeholder" : row.type;
3252
+
3253
+ left.push(
3254
+ `<div class="diff-line ${leftType}"><div class="diff-line-number">${row.oldNumber}</div><div class="diff-line-code">${escapeHtml(row.oldLine || " ")}</div></div>`,
3255
+ );
3256
+ right.push(
3257
+ `<div class="diff-line ${rightType}"><div class="diff-line-number">${row.newNumber}</div><div class="diff-line-code">${escapeHtml(row.newLine || " ")}</div></div>`,
3258
+ );
3259
+ });
3260
+
3261
+ return `
3262
+ <div class="diff-grid">
3263
+ <section class="diff-panel">
3264
+ <div class="diff-panel-title">Before</div>
3265
+ <div class="diff-lines">${left.join("")}</div>
3266
+ </section>
3267
+ <section class="diff-panel">
3268
+ <div class="diff-panel-title">After</div>
3269
+ <div class="diff-lines">${right.join("")}</div>
3270
+ </section>
3271
+ </div>
3272
+ `;
3273
+ }
3274
+
3275
+ function inlineMarkup(rows) {
3276
+ const lines = rows.map((row) => {
3277
+ const content =
3278
+ row.type === "addition"
3279
+ ? row.newLine
3280
+ : row.type === "deletion"
3281
+ ? row.oldLine
3282
+ : row.newLine;
3283
+
3284
+ return `<div class="diff-line ${row.type}"><div class="diff-line-number">${row.newNumber || row.oldNumber}</div><div class="diff-line-code">${escapeHtml(content || " ")}</div></div>`;
3285
+ });
3286
+
3287
+ return `<section class="diff-inline">${lines.join("")}</section>`;
3288
+ }
3289
+
3290
+ function renderDiff(change) {
3291
+ const { rows, summary } = diffLines(change.oldContent, change.newContent);
3292
+ updateSummary(summary);
3293
+ renderRoot.innerHTML =
3294
+ state.activeView === "inline"
3295
+ ? inlineMarkup(rows)
3296
+ : sideBySideMarkup(rows);
3297
+ }
3298
+
3299
+ function openSampleDiff(change = sampleChange) {
3300
+ state.mode = "sample";
3301
+ state.currentChange = change;
3302
+ filePathEl.textContent = change.path;
3303
+ badgeEl.textContent = change.label || "Sample agent patch";
3304
+ renderDiff(change);
3305
+ overlay.classList.add("visible");
3306
+ overlay.setAttribute("aria-hidden", "false");
3307
+ }
3308
+
3309
+ function closeDiffPreviewModal() {
3310
+ overlay.classList.remove("visible");
3311
+ overlay.setAttribute("aria-hidden", "true");
3312
+ }
3313
+
3314
+ previewButton.addEventListener("click", () => {
3315
+ openSampleDiff();
3316
+ });
3317
+
3318
+ closeButton?.addEventListener("click", closeDiffPreviewModal);
3319
+
3320
+ overlay.addEventListener("mousedown", (event) => {
3321
+ if (event.target === overlay) {
3322
+ closeDiffPreviewModal();
3323
+ }
3324
+ });
3325
+
3326
+ viewButtons.forEach((button) => {
3327
+ button.addEventListener("click", () => {
3328
+ state.activeView = button.dataset.diffView;
3329
+ updateViewButtons();
3330
+ renderDiff(state.currentChange);
3331
+ });
3332
+ });
3333
+
3334
+ document.addEventListener("keydown", (event) => {
3335
+ if (event.key === "Escape" && settingsPanel?.classList.contains("visible")) {
3336
+ setSettingsPanelOpen(false);
3337
+ settingsToggle?.focus();
3338
+ }
3339
+
3340
+ if (event.key === "Escape" && !shortcutsPanel?.classList.contains("hidden")) {
3341
+ setShortcutsPanelOpen(false);
3342
+ shortcutsToggle?.focus();
3343
+ }
3344
+
3345
+ if (event.key === "Escape" && overlay.classList.contains("visible")) {
3346
+ event.preventDefault();
3347
+ closeDiffPreviewModal();
3348
+ }
3349
+ });
3350
+
3351
+ window.acceptDiff = async function () {
3352
+ if (overlay.classList.contains("visible")) {
3353
+ closeDiffPreviewModal();
3354
+ }
3355
+
3356
+ if (
3357
+ state.mode !== "sample" &&
3358
+ typeof state.originalAcceptDiff === "function"
3359
+ ) {
3360
+ return state.originalAcceptDiff();
3361
+ }
3362
+ };
3363
+
3364
+ window.rejectDiff = function () {
3365
+ closeDiffPreviewModal();
3366
+
3367
+ if (
3368
+ state.mode !== "sample" &&
3369
+ typeof state.originalRejectDiff === "function"
3370
+ ) {
3371
+ return state.originalRejectDiff();
3372
+ }
3373
+ };
3374
+
3375
+ window.__studioDiffPreview = {
3376
+ open(change) {
3377
+ const normalized = {
3378
+ path: change?.path || sampleChange.path,
3379
+ label: change?.label || "Agent change preview",
3380
+ oldContent: change?.oldContent || sampleChange.oldContent,
3381
+ newContent: change?.newContent || sampleChange.newContent,
3382
+ };
3383
+ openSampleDiff(normalized);
3384
+ },
3385
+ close: closeDiffPreviewModal,
3386
+ };
3387
+
3388
+ updateViewButtons();
3389
+ renderDiff(sampleChange);
3390
+ });
3391
+ </script>
3392
+ </body>
3393
+ </html>