@thevinci/web 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/README.md +197 -0
  2. package/bin/cli-entry.js +55 -0
  3. package/bin/cli-output.js +145 -0
  4. package/bin/cli.js +4887 -0
  5. package/bin/cli.test.js +64 -0
  6. package/dist/apple-touch-icon-120x120.png +0 -0
  7. package/dist/apple-touch-icon-152x152.png +0 -0
  8. package/dist/apple-touch-icon-167x167.png +0 -0
  9. package/dist/apple-touch-icon-180x180.png +0 -0
  10. package/dist/apple-touch-icon.png +0 -0
  11. package/dist/apple-touch-icon.svg +528 -0
  12. package/dist/assets/JsonTreeView-CSm9OzXG.js +1 -0
  13. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  14. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  15. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  16. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  17. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  18. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  19. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  20. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  21. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  22. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  23. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  24. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  25. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  26. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  27. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  28. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  29. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  30. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  31. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  32. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  33. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  34. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  35. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  36. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  37. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  38. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  39. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  40. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  41. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  42. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  43. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  44. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  45. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  46. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  47. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  48. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  49. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  50. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  51. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  52. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  53. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  54. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  55. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  56. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  57. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  58. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  59. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  60. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  61. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  62. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  63. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  64. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  65. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  66. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  67. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  68. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  69. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  70. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  71. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  72. package/dist/assets/MarkdownRendererImpl-DensKOLc.js +6 -0
  73. package/dist/assets/MultiRunWindow-Bo7THayo.js +1 -0
  74. package/dist/assets/OnboardingScreen-BDqmzTVR.js +2 -0
  75. package/dist/assets/SettingsWindow-coz__Ykw.js +1 -0
  76. package/dist/assets/TerminalView-DrZ-i3Dr.js +1 -0
  77. package/dist/assets/ToolOutputDialog-Eglzslt3.js +16 -0
  78. package/dist/assets/es-4o9ciP61.js +15 -0
  79. package/dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  80. package/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  81. package/dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  82. package/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  83. package/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  84. package/dist/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
  85. package/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  86. package/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  87. package/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  88. package/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  89. package/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  90. package/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  91. package/dist/assets/index-DLTDToSP.css +1 -0
  92. package/dist/assets/index-DgiFEKGN.js +1 -0
  93. package/dist/assets/ko-B20imCHE.js +15 -0
  94. package/dist/assets/main-BV3KOtdA.css +1 -0
  95. package/dist/assets/main-CDKJj0sH.js +226 -0
  96. package/dist/assets/main-LC-PSNVM.js +2 -0
  97. package/dist/assets/miniChat-CQUiG_cr.js +2 -0
  98. package/dist/assets/modelPrefsAutoSave-Dm799vzR.js +6986 -0
  99. package/dist/assets/pl-DQJ7LSzj.js +15 -0
  100. package/dist/assets/pt-BR-OmjHUz9y.js +15 -0
  101. package/dist/assets/renderElectronMiniChatApp-CARbeW0G.js +2 -0
  102. package/dist/assets/uk-BNFxOlO4.js +15 -0
  103. package/dist/assets/vendor--DBfsbEis.css +1 -0
  104. package/dist/assets/vendor-.bun-B9l0ZNi2.js +4094 -0
  105. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  106. package/dist/assets/wasmSttWorker-Dtlxac_K.js +1 -0
  107. package/dist/assets/wasmSttWorker-oo7Dm_jy.js +1806 -0
  108. package/dist/assets/worker-CbT6TVo7.js +155 -0
  109. package/dist/assets/zh-CN-C6T-Ac7F.js +15 -0
  110. package/dist/favicon-16.png +0 -0
  111. package/dist/favicon-32.png +0 -0
  112. package/dist/favicon.png +0 -0
  113. package/dist/favicon.svg +528 -0
  114. package/dist/index.html +607 -0
  115. package/dist/logo-dark-192x192.png +0 -0
  116. package/dist/logo-dark-512x512.png +0 -0
  117. package/dist/logo-dark-512x512.svg +528 -0
  118. package/dist/logo-light-192x192.png +0 -0
  119. package/dist/logo-light-512x512.png +0 -0
  120. package/dist/logo-light-512x512.svg +528 -0
  121. package/dist/mini-chat.html +16 -0
  122. package/dist/pwa-192.png +0 -0
  123. package/dist/pwa-512.png +0 -0
  124. package/dist/pwa-maskable-192.png +0 -0
  125. package/dist/pwa-maskable-512.png +0 -0
  126. package/dist/site.webmanifest +21 -0
  127. package/dist/sw.js +1 -0
  128. package/package.json +118 -0
  129. package/public/apple-touch-icon-120x120.png +0 -0
  130. package/public/apple-touch-icon-152x152.png +0 -0
  131. package/public/apple-touch-icon-167x167.png +0 -0
  132. package/public/apple-touch-icon-180x180.png +0 -0
  133. package/public/apple-touch-icon.png +0 -0
  134. package/public/apple-touch-icon.svg +528 -0
  135. package/public/favicon-16.png +0 -0
  136. package/public/favicon-32.png +0 -0
  137. package/public/favicon.png +0 -0
  138. package/public/favicon.svg +528 -0
  139. package/public/logo-dark-192x192.png +0 -0
  140. package/public/logo-dark-512x512.png +0 -0
  141. package/public/logo-dark-512x512.svg +528 -0
  142. package/public/logo-light-192x192.png +0 -0
  143. package/public/logo-light-512x512.png +0 -0
  144. package/public/logo-light-512x512.svg +528 -0
  145. package/public/pwa-192.png +0 -0
  146. package/public/pwa-512.png +0 -0
  147. package/public/pwa-maskable-192.png +0 -0
  148. package/public/pwa-maskable-512.png +0 -0
  149. package/public/site.webmanifest +21 -0
  150. package/server/TERMINAL_WS_PROTOCOL.md +48 -0
  151. package/server/index.d.ts +39 -0
  152. package/server/index.js +1311 -0
  153. package/server/lib/cloudflare-tunnel.js +650 -0
  154. package/server/lib/event-stream/DOCUMENTATION.md +61 -0
  155. package/server/lib/event-stream/directory-ws-bridge.js +185 -0
  156. package/server/lib/event-stream/global-hub.js +158 -0
  157. package/server/lib/event-stream/global-hub.test.js +140 -0
  158. package/server/lib/event-stream/global-ws-bridge.js +206 -0
  159. package/server/lib/event-stream/index.js +25 -0
  160. package/server/lib/event-stream/protocol.js +131 -0
  161. package/server/lib/event-stream/protocol.test.js +182 -0
  162. package/server/lib/event-stream/runtime.js +180 -0
  163. package/server/lib/event-stream/runtime.test.js +512 -0
  164. package/server/lib/event-stream/upstream-reader.js +226 -0
  165. package/server/lib/event-stream/upstream-reader.test.js +276 -0
  166. package/server/lib/fs/DOCUMENTATION.md +36 -0
  167. package/server/lib/fs/routes.js +1040 -0
  168. package/server/lib/fs/search.js +238 -0
  169. package/server/lib/git/DOCUMENTATION.md +152 -0
  170. package/server/lib/git/credentials.js +74 -0
  171. package/server/lib/git/identity-storage.js +112 -0
  172. package/server/lib/git/index.js +6 -0
  173. package/server/lib/git/routes.js +972 -0
  174. package/server/lib/git/service.js +3432 -0
  175. package/server/lib/git/service.test.js +39 -0
  176. package/server/lib/github/DOCUMENTATION.md +171 -0
  177. package/server/lib/github/auth.js +307 -0
  178. package/server/lib/github/device-flow.js +50 -0
  179. package/server/lib/github/index.js +24 -0
  180. package/server/lib/github/octokit.js +10 -0
  181. package/server/lib/github/pr-status.js +519 -0
  182. package/server/lib/github/repo/fork-detection.js +102 -0
  183. package/server/lib/github/repo/index.js +55 -0
  184. package/server/lib/github/routes.js +1560 -0
  185. package/server/lib/magic-prompts/routes.js +63 -0
  186. package/server/lib/magic-prompts/runtime.js +119 -0
  187. package/server/lib/notifications/DOCUMENTATION.md +122 -0
  188. package/server/lib/notifications/emitter-runtime.js +102 -0
  189. package/server/lib/notifications/index.js +4 -0
  190. package/server/lib/notifications/message.js +52 -0
  191. package/server/lib/notifications/message.test.js +34 -0
  192. package/server/lib/notifications/push-runtime.js +304 -0
  193. package/server/lib/notifications/routes.js +315 -0
  194. package/server/lib/notifications/runtime.js +566 -0
  195. package/server/lib/notifications/template-runtime.js +349 -0
  196. package/server/lib/notifications/template-runtime.test.js +26 -0
  197. package/server/lib/opencode/DOCUMENTATION.md +362 -0
  198. package/server/lib/opencode/agents.js +634 -0
  199. package/server/lib/opencode/auth-state-runtime.js +88 -0
  200. package/server/lib/opencode/auth.js +83 -0
  201. package/server/lib/opencode/bootstrap-runtime.js +131 -0
  202. package/server/lib/opencode/cli-entry-runtime.js +43 -0
  203. package/server/lib/opencode/cli-options.js +128 -0
  204. package/server/lib/opencode/commands.js +339 -0
  205. package/server/lib/opencode/config-entity-routes.js +370 -0
  206. package/server/lib/opencode/core-routes.js +500 -0
  207. package/server/lib/opencode/core-routes.test.js +26 -0
  208. package/server/lib/opencode/env-config.js +74 -0
  209. package/server/lib/opencode/env-keys.js +68 -0
  210. package/server/lib/opencode/env-runtime.js +1162 -0
  211. package/server/lib/opencode/env-runtime.test.js +116 -0
  212. package/server/lib/opencode/feature-routes-runtime.js +244 -0
  213. package/server/lib/opencode/hmr-state-runtime.js +85 -0
  214. package/server/lib/opencode/index.js +66 -0
  215. package/server/lib/opencode/lifecycle.js +1019 -0
  216. package/server/lib/opencode/lifecycle.test.js +240 -0
  217. package/server/lib/opencode/mcp.js +278 -0
  218. package/server/lib/opencode/network-runtime.js +104 -0
  219. package/server/lib/opencode/network-runtime.test.js +37 -0
  220. package/server/lib/opencode/opencode-resolution-runtime.js +71 -0
  221. package/server/lib/opencode/path-utils.js +100 -0
  222. package/server/lib/opencode/path-utils.test.js +71 -0
  223. package/server/lib/opencode/project-directory-runtime.js +124 -0
  224. package/server/lib/opencode/project-icon-routes.js +399 -0
  225. package/server/lib/opencode/project-icon-routes.test.js +107 -0
  226. package/server/lib/opencode/providers.js +96 -0
  227. package/server/lib/opencode/proxy.js +445 -0
  228. package/server/lib/opencode/pwa-manifest-routes.js +257 -0
  229. package/server/lib/opencode/pwa-manifest-routes.test.js +133 -0
  230. package/server/lib/opencode/routes.js +541 -0
  231. package/server/lib/opencode/server-startup-runtime.js +156 -0
  232. package/server/lib/opencode/server-utils-runtime.js +168 -0
  233. package/server/lib/opencode/server-utils-runtime.test.js +135 -0
  234. package/server/lib/opencode/session-runtime.js +356 -0
  235. package/server/lib/opencode/session-runtime.test.js +151 -0
  236. package/server/lib/opencode/settings-helpers.js +770 -0
  237. package/server/lib/opencode/settings-helpers.test.js +109 -0
  238. package/server/lib/opencode/settings-normalization-runtime.js +428 -0
  239. package/server/lib/opencode/settings-runtime.js +826 -0
  240. package/server/lib/opencode/settings-runtime.test.js +85 -0
  241. package/server/lib/opencode/shared.js +615 -0
  242. package/server/lib/opencode/shutdown-runtime.js +139 -0
  243. package/server/lib/opencode/shutdown-runtime.test.js +58 -0
  244. package/server/lib/opencode/skill-routes.js +701 -0
  245. package/server/lib/opencode/skills.js +548 -0
  246. package/server/lib/opencode/startup-pipeline-runtime.js +130 -0
  247. package/server/lib/opencode/static-routes-runtime.js +65 -0
  248. package/server/lib/opencode/theme-runtime.js +167 -0
  249. package/server/lib/opencode/tunnel-auth.js +591 -0
  250. package/server/lib/opencode/tunnel-wiring-runtime.js +94 -0
  251. package/server/lib/opencode/vinci-routes.js +76 -0
  252. package/server/lib/opencode/watcher.js +115 -0
  253. package/server/lib/opencode/watcher.test.js +239 -0
  254. package/server/lib/preview/proxy-runtime.js +1333 -0
  255. package/server/lib/preview/proxy-runtime.test.js +144 -0
  256. package/server/lib/projects/project-config.js +567 -0
  257. package/server/lib/projects/project-config.test.js +175 -0
  258. package/server/lib/projects/project-id.js +13 -0
  259. package/server/lib/quota/DOCUMENTATION.md +58 -0
  260. package/server/lib/quota/index.js +25 -0
  261. package/server/lib/quota/providers/claude.js +107 -0
  262. package/server/lib/quota/providers/codex.js +113 -0
  263. package/server/lib/quota/providers/copilot.js +165 -0
  264. package/server/lib/quota/providers/google/api.js +92 -0
  265. package/server/lib/quota/providers/google/auth.js +108 -0
  266. package/server/lib/quota/providers/google/index.js +124 -0
  267. package/server/lib/quota/providers/google/transforms.js +109 -0
  268. package/server/lib/quota/providers/index.js +168 -0
  269. package/server/lib/quota/providers/interface.js +55 -0
  270. package/server/lib/quota/providers/kimi.js +108 -0
  271. package/server/lib/quota/providers/minimax-cn-coding-plan.js +140 -0
  272. package/server/lib/quota/providers/minimax-coding-plan.js +139 -0
  273. package/server/lib/quota/providers/nanogpt.js +124 -0
  274. package/server/lib/quota/providers/ollama-cloud.js +112 -0
  275. package/server/lib/quota/providers/openai.js +91 -0
  276. package/server/lib/quota/providers/openrouter.js +92 -0
  277. package/server/lib/quota/providers/zai.js +91 -0
  278. package/server/lib/quota/providers/zhipuai-coding-plan.js +133 -0
  279. package/server/lib/quota/providers/zhipuai.js +114 -0
  280. package/server/lib/quota/routes.js +27 -0
  281. package/server/lib/quota/utils/auth.js +50 -0
  282. package/server/lib/quota/utils/formatters.js +85 -0
  283. package/server/lib/quota/utils/formatters.test.js +54 -0
  284. package/server/lib/quota/utils/index.js +10 -0
  285. package/server/lib/quota/utils/transformers.js +55 -0
  286. package/server/lib/scheduled-tasks/DOCUMENTATION.md +44 -0
  287. package/server/lib/scheduled-tasks/routes.js +235 -0
  288. package/server/lib/scheduled-tasks/runtime.js +773 -0
  289. package/server/lib/scheduled-tasks/runtime.test.js +100 -0
  290. package/server/lib/security/request-security.js +115 -0
  291. package/server/lib/session-folders/routes.js +63 -0
  292. package/server/lib/session-folders/routes.test.js +102 -0
  293. package/server/lib/skills-catalog/DOCUMENTATION.md +178 -0
  294. package/server/lib/skills-catalog/cache.js +29 -0
  295. package/server/lib/skills-catalog/clawdhub/api.js +158 -0
  296. package/server/lib/skills-catalog/clawdhub/index.js +30 -0
  297. package/server/lib/skills-catalog/clawdhub/install.js +238 -0
  298. package/server/lib/skills-catalog/clawdhub/scan.js +113 -0
  299. package/server/lib/skills-catalog/curated-sources.js +21 -0
  300. package/server/lib/skills-catalog/git.js +77 -0
  301. package/server/lib/skills-catalog/index.js +42 -0
  302. package/server/lib/skills-catalog/install.js +294 -0
  303. package/server/lib/skills-catalog/scan.js +221 -0
  304. package/server/lib/skills-catalog/source.js +87 -0
  305. package/server/lib/terminal/DOCUMENTATION.md +76 -0
  306. package/server/lib/terminal/index.js +31 -0
  307. package/server/lib/terminal/output-replay-buffer.js +78 -0
  308. package/server/lib/terminal/output-replay-buffer.test.js +75 -0
  309. package/server/lib/terminal/runtime.js +850 -0
  310. package/server/lib/terminal/runtime.test.js +96 -0
  311. package/server/lib/terminal/terminal-ws-protocol.js +68 -0
  312. package/server/lib/terminal/terminal-ws-protocol.test.js +145 -0
  313. package/server/lib/text/DOCUMENTATION.md +35 -0
  314. package/server/lib/text/summarization.js +138 -0
  315. package/server/lib/text/summarization.test.js +34 -0
  316. package/server/lib/tts/DOCUMENTATION.md +146 -0
  317. package/server/lib/tts/base-url.js +62 -0
  318. package/server/lib/tts/capability-runtime.js +31 -0
  319. package/server/lib/tts/index.js +19 -0
  320. package/server/lib/tts/routes.js +261 -0
  321. package/server/lib/tts/routes.test.js +53 -0
  322. package/server/lib/tts/service.js +178 -0
  323. package/server/lib/tts/stt.js +75 -0
  324. package/server/lib/tunnels/DOCUMENTATION.md +18 -0
  325. package/server/lib/tunnels/index.js +166 -0
  326. package/server/lib/tunnels/managed-config.js +201 -0
  327. package/server/lib/tunnels/providers/cloudflare.js +260 -0
  328. package/server/lib/tunnels/registry.js +51 -0
  329. package/server/lib/tunnels/routes.js +605 -0
  330. package/server/lib/tunnels/types.js +219 -0
  331. package/server/lib/ui-auth/DOCUMENTATION.md +38 -0
  332. package/server/lib/ui-auth/ui-auth.js +673 -0
  333. package/server/lib/ui-auth/ui-passkeys.js +545 -0
  334. package/server/opencode-proxy.test.js +151 -0
  335. package/server/proxy-headers.js +61 -0
  336. package/server/proxy-headers.test.js +58 -0
  337. package/server/sse-routes.test.js +152 -0
@@ -0,0 +1,607 @@
1
+ <!doctype html>
2
+ <html lang="en" class="h-full">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
6
+
7
+ <!-- Favicon -->
8
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
9
+ <link rel="icon" type="image/png" href="/favicon-32.png" sizes="32x32" />
10
+ <link rel="icon" type="image/png" href="/favicon-16.png" sizes="16x16" />
11
+ <link rel="mask-icon" href="/favicon.svg" color="#edb449" />
12
+
13
+ <!-- Apple touch icon - PNG format required for iOS PWA support -->
14
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png" />
15
+ <link rel="apple-touch-icon" sizes="167x167" href="/apple-touch-icon-167x167.png" />
16
+ <link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png" />
17
+
18
+ <!-- Preload Nerd Fonts for terminal icon display -->
19
+ <link rel="preload" href="https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff2"
20
+ as="font" type="font/woff2" crossorigin="anonymous">
21
+ <link rel="preload" href="https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff2"
22
+ as="font" type="font/woff2" crossorigin="anonymous">
23
+
24
+ <!-- Web app manifest (endpoint-first with data URL fallback) -->
25
+ <script>
26
+ const baseUrl = location.origin;
27
+ const defaultAppName = 'Vinci - AI Coding Assistant';
28
+ const defaultShortName = 'Vinci';
29
+ const pwaNameStorageKey = 'vinci.pwaName';
30
+ const pwaOrientationStorageKey = 'vinci.pwaOrientation';
31
+ const mobileKeyboardModeStorageKey = 'vinci.mobileKeyboardMode';
32
+ const pwaRecentSessionsStorageKey = 'vinci.pwaRecentSessions';
33
+ const viewportBaseContent = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover';
34
+
35
+ const normalizePwaName = (value, fallback) => {
36
+ if (typeof value !== 'string') {
37
+ return fallback;
38
+ }
39
+ const normalized = value.trim().replace(/\s+/g, ' ');
40
+ if (!normalized) {
41
+ return fallback;
42
+ }
43
+ return normalized.slice(0, 64);
44
+ };
45
+
46
+ const truncate = (value, maxLength) => {
47
+ if (typeof value !== 'string') {
48
+ return '';
49
+ }
50
+ return value.length > maxLength ? value.slice(0, maxLength) : value;
51
+ };
52
+
53
+ const normalizePwaOrientation = (value, fallback = 'system') => {
54
+ if (value === 'portrait' || value === 'landscape' || value === 'system') {
55
+ return value;
56
+ }
57
+ return fallback;
58
+ };
59
+
60
+ const normalizeMobileKeyboardMode = (value, fallback = 'native') => {
61
+ if (value === 'native' || value === 'resize-content') {
62
+ return value;
63
+ }
64
+ return fallback;
65
+ };
66
+
67
+ const supportsMobileKeyboardResizeContent = () => {
68
+ const userAgent = navigator.userAgent || '';
69
+ const platform = navigator.platform || '';
70
+ const maxTouchPoints = navigator.maxTouchPoints || 0;
71
+ const isIOS = /iPhone|iPad|iPod/i.test(userAgent)
72
+ || ((/Macintosh|MacIntel/i.test(userAgent) || /MacIntel/i.test(platform)) && maxTouchPoints > 1);
73
+
74
+ return !isIOS;
75
+ };
76
+
77
+ const getSupportedMobileKeyboardMode = (mode) => {
78
+ if (mode === 'resize-content' && !supportsMobileKeyboardResizeContent()) {
79
+ return 'native';
80
+ }
81
+ return mode;
82
+ };
83
+
84
+ const getViewportContentForMobileKeyboardMode = (value) => {
85
+ const mode = getSupportedMobileKeyboardMode(normalizeMobileKeyboardMode(value, 'native'));
86
+ return mode === 'resize-content'
87
+ ? `${viewportBaseContent}, interactive-widget=resizes-content`
88
+ : viewportBaseContent;
89
+ };
90
+
91
+ const applyStoredMobileKeyboardMode = () => {
92
+ let mode = 'native';
93
+ try {
94
+ mode = getSupportedMobileKeyboardMode(normalizeMobileKeyboardMode(localStorage.getItem(mobileKeyboardModeStorageKey), 'native'));
95
+ if (mode === 'native') {
96
+ localStorage.removeItem(mobileKeyboardModeStorageKey);
97
+ }
98
+ } catch {
99
+ mode = 'native';
100
+ }
101
+
102
+ document.documentElement.setAttribute('data-oc-mobile-keyboard-mode', mode);
103
+ const viewportMeta = document.querySelector('meta[name="viewport"]');
104
+ if (viewportMeta) {
105
+ viewportMeta.setAttribute('content', getViewportContentForMobileKeyboardMode(mode));
106
+ }
107
+ };
108
+
109
+ applyStoredMobileKeyboardMode();
110
+
111
+ const getStoredInstallName = () => {
112
+ try {
113
+ const storedName = localStorage.getItem(pwaNameStorageKey);
114
+ return normalizePwaName(storedName, defaultAppName);
115
+ } catch {
116
+ return defaultAppName;
117
+ }
118
+ };
119
+
120
+ const setStoredInstallName = (value) => {
121
+ const normalizedName = normalizePwaName(value, '');
122
+ try {
123
+ if (normalizedName) {
124
+ localStorage.setItem(pwaNameStorageKey, normalizedName);
125
+ } else {
126
+ localStorage.removeItem(pwaNameStorageKey);
127
+ }
128
+ } catch {
129
+ return defaultAppName;
130
+ }
131
+ return normalizedName || defaultAppName;
132
+ };
133
+
134
+ const getStoredOrientation = () => {
135
+ try {
136
+ const storedOrientation = localStorage.getItem(pwaOrientationStorageKey);
137
+ return normalizePwaOrientation(storedOrientation, 'system');
138
+ } catch {
139
+ return 'system';
140
+ }
141
+ };
142
+
143
+ const setStoredOrientation = (value) => {
144
+ const normalizedOrientation = normalizePwaOrientation(value, 'system');
145
+ try {
146
+ localStorage.setItem(pwaOrientationStorageKey, normalizedOrientation);
147
+ } catch {
148
+ return 'system';
149
+ }
150
+ return normalizedOrientation;
151
+ };
152
+
153
+ const getQueryInstallNameOverride = () => {
154
+ try {
155
+ const params = new URLSearchParams(location.search);
156
+ const queryName = params.get('pwa_name') ?? params.get('app_name') ?? params.get('appName');
157
+ if (queryName === null) {
158
+ return null;
159
+ }
160
+
161
+ const normalizedQueryName = normalizePwaName(queryName, '');
162
+ if (normalizedQueryName) {
163
+ localStorage.setItem(pwaNameStorageKey, normalizedQueryName);
164
+ return normalizedQueryName;
165
+ }
166
+
167
+ localStorage.removeItem(pwaNameStorageKey);
168
+ return defaultAppName;
169
+ } catch {
170
+ return null;
171
+ }
172
+ };
173
+
174
+ const parseRecentSessionShortcuts = () => {
175
+ try {
176
+ const raw = localStorage.getItem(pwaRecentSessionsStorageKey);
177
+ if (!raw) {
178
+ return [];
179
+ }
180
+
181
+ const parsed = JSON.parse(raw);
182
+ if (!Array.isArray(parsed)) {
183
+ return [];
184
+ }
185
+
186
+ const seen = new Set();
187
+ const recentSessions = [];
188
+
189
+ for (const item of parsed) {
190
+ if (!item || typeof item !== 'object') {
191
+ continue;
192
+ }
193
+
194
+ const sessionId = typeof item.sessionId === 'string' ? item.sessionId.trim().slice(0, 160) : '';
195
+ if (!sessionId || seen.has(sessionId)) {
196
+ continue;
197
+ }
198
+
199
+ const fallbackTitle = `Session ${recentSessions.length + 1}`;
200
+ const title = truncate(normalizePwaName(item.title, fallbackTitle), 48);
201
+
202
+ seen.add(sessionId);
203
+ recentSessions.push({ sessionId, title });
204
+
205
+ if (recentSessions.length >= 3) {
206
+ break;
207
+ }
208
+ }
209
+
210
+ return recentSessions;
211
+ } catch {
212
+ return [];
213
+ }
214
+ };
215
+
216
+ const buildShortcuts = (recentSessions) => {
217
+ const shortcuts = [
218
+ {
219
+ name: 'Appearance Settings',
220
+ short_name: 'Settings',
221
+ description: 'Open appearance settings',
222
+ url: `${baseUrl}/?settings=appearance`,
223
+ icons: [{ src: `${baseUrl}/pwa-192.png`, sizes: '192x192', type: 'image/png' }],
224
+ },
225
+ ];
226
+
227
+ for (const session of recentSessions) {
228
+ const sessionTitle = truncate(session.title, 32);
229
+ shortcuts.push({
230
+ name: sessionTitle,
231
+ short_name: sessionTitle,
232
+ description: 'Open recent session',
233
+ url: `${baseUrl}/?session=${encodeURIComponent(session.sessionId)}`,
234
+ icons: [{ src: `${baseUrl}/pwa-192.png`, sizes: '192x192', type: 'image/png' }],
235
+ });
236
+ }
237
+
238
+ return shortcuts;
239
+ };
240
+
241
+ const buildManifest = (appName, recentSessions, orientationPreference) => {
242
+ const shortName = appName === defaultAppName ? defaultShortName : truncate(appName, 30);
243
+ const manifestOrientation = orientationPreference === 'portrait'
244
+ ? 'portrait-primary'
245
+ : orientationPreference === 'landscape'
246
+ ? 'landscape-primary'
247
+ : null;
248
+ return {
249
+ name: appName,
250
+ short_name: shortName,
251
+ description: 'Web interface companion for OpenCode AI coding agent',
252
+ id: `${baseUrl}/`,
253
+ start_url: `${baseUrl}/`,
254
+ scope: `${baseUrl}/`,
255
+ display: 'standalone',
256
+ display_override: ['window-controls-overlay'],
257
+ ...(manifestOrientation ? { orientation: manifestOrientation } : {}),
258
+ background_color: '#151313',
259
+ theme_color: '#edb449',
260
+ icons: [
261
+ { src: `${baseUrl}/pwa-192.png`, sizes: '192x192', type: 'image/png', purpose: 'any' },
262
+ { src: `${baseUrl}/pwa-512.png`, sizes: '512x512', type: 'image/png', purpose: 'any' },
263
+ { src: `${baseUrl}/pwa-maskable-192.png`, sizes: '192x192', type: 'image/png', purpose: 'any maskable' },
264
+ { src: `${baseUrl}/pwa-maskable-512.png`, sizes: '512x512', type: 'image/png', purpose: 'any maskable' },
265
+ { src: `${baseUrl}/apple-touch-icon-180x180.png`, sizes: '180x180', type: 'image/png', purpose: 'any' },
266
+ { src: `${baseUrl}/apple-touch-icon-152x152.png`, sizes: '152x152', type: 'image/png', purpose: 'any' },
267
+ { src: `${baseUrl}/favicon-32.png`, sizes: '32x32', type: 'image/png' },
268
+ { src: `${baseUrl}/favicon-16.png`, sizes: '16x16', type: 'image/png' },
269
+ ],
270
+ shortcuts: buildShortcuts(recentSessions),
271
+ categories: ['developer', 'tools', 'productivity'],
272
+ lang: 'en',
273
+ };
274
+ };
275
+
276
+ const buildManifestEndpointUrl = (installNameOverride = null, orientationOverride = null) => {
277
+ const params = new URLSearchParams();
278
+ if (typeof installNameOverride === 'string') {
279
+ params.set('appName', installNameOverride);
280
+ }
281
+ if (typeof orientationOverride === 'string') {
282
+ params.set('orientation', normalizePwaOrientation(orientationOverride, 'system'));
283
+ }
284
+ const search = params.toString();
285
+ return `${baseUrl}/manifest.webmanifest${search ? `?${search}` : ''}`;
286
+ };
287
+
288
+ const manifestLink = document.createElement('link');
289
+ manifestLink.rel = 'manifest';
290
+ // Required so the browser sends auth cookies when fetching the manifest,
291
+ // enabling PWA installation behind authentication proxies (e.g. Cloudflare Access).
292
+ manifestLink.crossOrigin = 'use-credentials';
293
+ document.head.appendChild(manifestLink);
294
+
295
+ let activeManifestBlobUrl = null;
296
+ let manifestRequestVersion = 0;
297
+
298
+ const setManifestFromBlob = (manifest) => {
299
+ if (activeManifestBlobUrl) {
300
+ URL.revokeObjectURL(activeManifestBlobUrl);
301
+ }
302
+
303
+ const manifestBlob = new Blob([JSON.stringify(manifest)], { type: 'application/manifest+json' });
304
+ activeManifestBlobUrl = URL.createObjectURL(manifestBlob);
305
+ manifestLink.href = activeManifestBlobUrl;
306
+ };
307
+
308
+ const setManifestFromEndpoint = (manifestUrl) => {
309
+ if (activeManifestBlobUrl) {
310
+ URL.revokeObjectURL(activeManifestBlobUrl);
311
+ activeManifestBlobUrl = null;
312
+ }
313
+ manifestLink.href = manifestUrl;
314
+ };
315
+
316
+ const canUseManifestEndpoint = async (manifestUrl, requestVersion) => {
317
+ if (typeof fetch !== 'function') {
318
+ return false;
319
+ }
320
+
321
+ const controller = typeof AbortController === 'function' ? new AbortController() : null;
322
+ const timeoutId = setTimeout(() => {
323
+ controller?.abort();
324
+ }, 1500);
325
+
326
+ try {
327
+ const response = await fetch(manifestUrl, {
328
+ credentials: 'include',
329
+ cache: 'no-store',
330
+ headers: {
331
+ Accept: 'application/manifest+json, application/json;q=0.9, */*;q=0.1',
332
+ },
333
+ ...(controller ? { signal: controller.signal } : {}),
334
+ });
335
+
336
+ if (requestVersion !== manifestRequestVersion || !response.ok) {
337
+ return false;
338
+ }
339
+
340
+ const contentType = response.headers.get('content-type') || '';
341
+ return /manifest|json/i.test(contentType);
342
+ } catch {
343
+ return false;
344
+ } finally {
345
+ clearTimeout(timeoutId);
346
+ }
347
+ };
348
+
349
+ const updateManifest = async (installNameOverride = null, orientationOverride = null) => {
350
+ const resolvedFallbackName = typeof installNameOverride === 'string' ? installNameOverride : getStoredInstallName();
351
+ const resolvedOrientation = typeof orientationOverride === 'string'
352
+ ? normalizePwaOrientation(orientationOverride, 'system')
353
+ : getStoredOrientation();
354
+ const recentSessions = parseRecentSessionShortcuts();
355
+ const manifest = buildManifest(resolvedFallbackName, recentSessions, resolvedOrientation);
356
+ const manifestUrl = buildManifestEndpointUrl(installNameOverride, resolvedOrientation);
357
+ const requestVersion = ++manifestRequestVersion;
358
+
359
+ const useEndpoint = await canUseManifestEndpoint(manifestUrl, requestVersion);
360
+ if (requestVersion !== manifestRequestVersion) {
361
+ return;
362
+ }
363
+
364
+ if (useEndpoint) {
365
+ setManifestFromEndpoint(manifestUrl);
366
+ return;
367
+ }
368
+
369
+ setManifestFromBlob(manifest);
370
+ };
371
+
372
+ const refreshManifestFromStorage = () => {
373
+ void updateManifest();
374
+ };
375
+
376
+ const initialInstallNameOverride = getQueryInstallNameOverride();
377
+ void updateManifest(initialInstallNameOverride);
378
+
379
+ window.__APP_GET_PWA_INSTALL_NAME__ = () => getStoredInstallName();
380
+ window.__APP_SET_PWA_INSTALL_NAME__ = (value) => {
381
+ const resolvedName = setStoredInstallName(value);
382
+ void updateManifest(resolvedName);
383
+ return resolvedName;
384
+ };
385
+ window.__APP_SET_PWA_ORIENTATION__ = (value) => {
386
+ const resolvedOrientation = setStoredOrientation(value);
387
+ void updateManifest(null, resolvedOrientation);
388
+ return resolvedOrientation;
389
+ };
390
+ window.__APP_UPDATE_PWA_MANIFEST__ = () => {
391
+ refreshManifestFromStorage();
392
+ };
393
+ </script>
394
+
395
+ <script>
396
+ // Blocking script to detect and apply theme before first paint
397
+ (function() {
398
+ try {
399
+ var themeMode = localStorage.getItem('themeMode');
400
+ var variant = localStorage.getItem('selectedThemeVariant');
401
+ var useSystem = localStorage.getItem('useSystemTheme');
402
+ var isDark;
403
+
404
+ // Check themeMode first (new storage key)
405
+ if (themeMode === 'dark') {
406
+ isDark = true;
407
+ } else if (themeMode === 'light') {
408
+ isDark = false;
409
+ } else if (themeMode === 'system' || useSystem === null || useSystem === 'true') {
410
+ // System preference
411
+ isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
412
+ } else if (variant === 'light' || variant === 'dark') {
413
+ // Legacy storage key fallback
414
+ isDark = variant === 'dark';
415
+ } else {
416
+ // Default to system
417
+ isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
418
+ }
419
+
420
+ // Apply theme class and data attribute
421
+ document.documentElement.classList.add(isDark ? 'dark' : 'light');
422
+ document.documentElement.setAttribute('data-splash-variant', isDark ? 'dark' : 'light');
423
+ document.documentElement.style.setProperty('color-scheme', isDark ? 'dark' : 'light');
424
+
425
+ // Splash colors persisted by the app theme system
426
+ var splashBgLight = localStorage.getItem('splashBgLight');
427
+ var splashFgLight = localStorage.getItem('splashFgLight');
428
+ var splashBgDark = localStorage.getItem('splashBgDark');
429
+ var splashFgDark = localStorage.getItem('splashFgDark');
430
+
431
+ if (splashBgLight) document.documentElement.style.setProperty('--splash-background-light', splashBgLight);
432
+ if (splashFgLight) document.documentElement.style.setProperty('--splash-stroke-light', splashFgLight);
433
+ if (splashBgDark) document.documentElement.style.setProperty('--splash-background-dark', splashBgDark);
434
+ if (splashFgDark) document.documentElement.style.setProperty('--splash-stroke-dark', splashFgDark);
435
+ } catch (error) {
436
+ console.warn('Failed to apply theme:', error);
437
+ }
438
+ })();
439
+ </script>
440
+
441
+ <!-- Theme color - Safari iOS 26+ prioritizes CSS background-color over this, but keep as fallback -->
442
+ <meta name="theme-color" content="#151313" />
443
+ <meta name="theme-color" content="#151313" media="(prefers-color-scheme: dark)" />
444
+
445
+ <!-- iOS Safari PWA styling -->
446
+ <meta name="apple-mobile-web-app-capable" content="yes" />
447
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
448
+ <meta name="apple-mobile-web-app-title" content="Vinci" />
449
+
450
+ <title>Vinci - AI Coding Assistant</title>
451
+ <meta name="description" content="Web interface companion for OpenCode AI coding agent" />
452
+ <meta name="application-name" content="Vinci" />
453
+ <meta name="apple-mobile-web-app-title" content="Vinci" />
454
+
455
+ <!-- Inline CSS for loading screen and Nerd Fonts (before Tailwind loads) -->
456
+ <style>
457
+ /* Nerd Font @font-face declarations for terminal icon support */
458
+ @font-face {
459
+ font-family: 'JetBrainsMono Nerd Font';
460
+ src:
461
+ local('JetBrainsMono Nerd Font'),
462
+ url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff2') format('woff2'),
463
+ url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff') format('woff');
464
+ font-weight: normal;
465
+ font-style: normal;
466
+ font-display: swap;
467
+ unicode-range: U+E000-F8FF, U+F0000-FFFFF;
468
+ }
469
+
470
+ @font-face {
471
+ font-family: 'FiraCode Nerd Font';
472
+ src:
473
+ local('FiraCode Nerd Font'),
474
+ url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff2') format('woff2'),
475
+ url('https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff') format('woff');
476
+ font-weight: normal;
477
+ font-style: normal;
478
+ font-display: swap;
479
+ unicode-range: U+E000-F8FF, U+F0000-FFFFF;
480
+ }
481
+
482
+ :root {
483
+ --splash-background-dark: #151313;
484
+ --splash-stroke-dark: #CECDC3;
485
+ --splash-background-light: #FFFCF0;
486
+ --splash-stroke-light: #100F0F;
487
+
488
+ --splash-background: var(--splash-background-dark);
489
+ --splash-stroke: var(--splash-stroke-dark);
490
+
491
+ /* Fallback fills (overridden below when supported) */
492
+ --splash-face-fill: rgba(255, 255, 255, 0.15);
493
+ --splash-cell-fill: rgba(255, 255, 255, 0.35);
494
+ --splash-logo-fill: var(--splash-stroke);
495
+ }
496
+
497
+ html[data-splash-variant='light'] {
498
+ --splash-background: var(--splash-background-light);
499
+ --splash-stroke: var(--splash-stroke-light);
500
+ --splash-face-fill: rgba(0, 0, 0, 0.15);
501
+ --splash-cell-fill: rgba(0, 0, 0, 0.4);
502
+ --splash-logo-fill: var(--splash-stroke);
503
+ }
504
+
505
+ html[data-splash-variant='dark'] {
506
+ --splash-background: var(--splash-background-dark);
507
+ --splash-stroke: var(--splash-stroke-dark);
508
+ --splash-logo-fill: var(--splash-stroke);
509
+ }
510
+
511
+ @supports (color: color-mix(in srgb, white 50%, transparent)) {
512
+ :root {
513
+ --splash-face-fill: color-mix(in srgb, var(--splash-stroke) 15%, transparent);
514
+ --splash-cell-fill: color-mix(in srgb, var(--splash-stroke) 35%, transparent);
515
+ }
516
+ }
517
+ #initial-loading {
518
+ background-color: var(--splash-background);
519
+ color: var(--splash-foreground);
520
+ display: flex;
521
+ align-items: center;
522
+ justify-content: center;
523
+ height: 100vh;
524
+ font-family: system-ui, -apple-system, sans-serif;
525
+ transition: opacity 0.3s ease-out;
526
+ position: absolute;
527
+ width: 100%;
528
+ z-index: 9999;
529
+ }
530
+ #initial-loading.fade-out {
531
+ opacity: 0;
532
+ pointer-events: none;
533
+ }
534
+ </style>
535
+ <script type="module" crossorigin src="/assets/main-LC-PSNVM.js"></script>
536
+ <link rel="modulepreload" crossorigin href="/assets/index-DgiFEKGN.js">
537
+ <link rel="modulepreload" crossorigin href="/assets/vendor-.bun-B9l0ZNi2.js">
538
+ <link rel="stylesheet" crossorigin href="/assets/index-DLTDToSP.css">
539
+ <link rel="stylesheet" crossorigin href="/assets/vendor--DBfsbEis.css">
540
+ </head>
541
+ <body class="h-full bg-background text-foreground">
542
+ <div id="root" class="h-full">
543
+ <!-- Loading fallback while React initializes -->
544
+ <div id="initial-loading">
545
+ <div style="display: flex; align-items: center; justify-content: center;">
546
+ <img src="/logo-dark-512x512.svg" alt="Vinci" width="120" height="120" style="max-width: 120px; max-height: 120px;" />
547
+ </div>
548
+ </div>
549
+ </div>
550
+ </div>
551
+
552
+ <script>
553
+ // Fallback: hide loading screen after 10 seconds if React fails to load.
554
+ // Desktop shells manage their own splash via the injected boot outcome;
555
+ // do not force-remove for desktop windows.
556
+ setTimeout(function() {
557
+ if (typeof window.__APP_LOCAL_ORIGIN__ === 'string' && window.__APP_LOCAL_ORIGIN__.length > 0) {
558
+ return;
559
+ }
560
+ var loading = document.getElementById('initial-loading');
561
+ if (loading) {
562
+ console.warn('Loading screen timeout - forcing hide after 10s');
563
+ loading.classList.add('fade-out');
564
+ setTimeout(function() {
565
+ loading.remove();
566
+ }, 300);
567
+ }
568
+ }, 10000);
569
+ </script>
570
+
571
+ <!-- CSS Font Loading API for reliable Nerd Font loading -->
572
+ <script>
573
+ (function() {
574
+ const fonts = [
575
+ {
576
+ name: 'JetBrainsMono Nerd Font',
577
+ url: 'https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/JetBrainsMonoNerdFont-Regular.woff2'
578
+ },
579
+ {
580
+ name: 'FiraCode Nerd Font',
581
+ url: 'https://cdn.jsdelivr.net/gh/mshaugh/nerdfont-webfonts@v3.3.0/build/fonts/FiraCodeNerdFont-Regular.woff2'
582
+ }
583
+ ];
584
+
585
+ const fontPromises = fonts.map(font => {
586
+ const fontFace = new FontFace(font.name, `url(${font.url}) format('woff2')`);
587
+ document.fonts.add(fontFace);
588
+ return fontFace.load().catch(err => {
589
+ console.warn(`Failed to load font: ${font.name}`, err);
590
+ });
591
+ });
592
+
593
+ Promise.allSettled(fontPromises).then(() => {
594
+ document.documentElement.classList.add('fonts-loaded');
595
+ });
596
+ })();
597
+ </script>
598
+
599
+ <!-- Polyfill for process before loading React -->
600
+ <script>
601
+ if (typeof process === 'undefined') {
602
+ window.process = { env: {} };
603
+ }
604
+ </script>
605
+
606
+ </body>
607
+ </html>
Binary file
Binary file