synabun 2.0.9 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (679) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +68 -4
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/launcher.mjs +9 -3
  10. package/lib/claude-hooks.js +172 -18
  11. package/lib/client-config-repair.js +17 -8
  12. package/lib/desktop-app.js +7 -1
  13. package/lib/external-tools.js +1 -1
  14. package/lib/first-launch-protection.js +57 -12
  15. package/lib/mcp-dist.js +176 -0
  16. package/lib/release-npm-ci.mjs +138 -0
  17. package/lib/release-publish.mjs +91 -0
  18. package/lib/release-scan.mjs +50 -0
  19. package/lib/server-crash-record.js +123 -0
  20. package/lib/server-exit.js +318 -0
  21. package/lib/server-supervisor.js +508 -0
  22. package/lib/start-launcher.js +43 -5
  23. package/lib/tools-cli.js +45 -0
  24. package/lib/update-snapshot.js +228 -0
  25. package/lib/virustotal.mjs +192 -0
  26. package/lib/windows-crash-event.js +269 -0
  27. package/mcp-server/dist/http.d.ts +20 -1
  28. package/mcp-server/dist/http.d.ts.map +1 -1
  29. package/mcp-server/dist/http.js +59 -12
  30. package/mcp-server/dist/http.js.map +1 -1
  31. package/mcp-server/dist/index.d.ts +33 -27
  32. package/mcp-server/dist/index.d.ts.map +1 -1
  33. package/mcp-server/dist/index.js +128 -143
  34. package/mcp-server/dist/index.js.map +1 -1
  35. package/mcp-server/dist/preload.js +1 -1
  36. package/mcp-server/dist/preload.js.map +1 -1
  37. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  38. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  39. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  40. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  42. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  44. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  45. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  46. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  47. package/mcp-server/dist/services/neural-interface.js +67 -11
  48. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  49. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  50. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  51. package/mcp-server/dist/services/server-instructions.js +20 -62
  52. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  53. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  54. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  55. package/mcp-server/dist/services/tool-catalog.js +339 -0
  56. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  60. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  61. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  62. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  63. package/mcp-server/dist/services/tool-list.js +334 -0
  64. package/mcp-server/dist/services/tool-list.js.map +1 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  66. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  67. package/mcp-server/dist/services/tool-runtime.js +400 -0
  68. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  70. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  71. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  72. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  73. package/mcp-server/dist/tools/agents.d.ts +10 -10
  74. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  75. package/mcp-server/dist/tools/agents.js +22 -13
  76. package/mcp-server/dist/tools/agents.js.map +1 -1
  77. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  78. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  79. package/mcp-server/dist/tools/image-tools.js +1 -1
  80. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  81. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  82. package/mcp-server/dist/tools/skill-tools.js +639 -0
  83. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  84. package/mcp-server/dist/tools/skill.d.ts +7 -0
  85. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  86. package/mcp-server/dist/tools/skill.js +11 -0
  87. package/mcp-server/dist/tools/skill.js.map +1 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  89. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  90. package/mcp-server/dist/tools/tool-groups.js +147 -0
  91. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  92. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  93. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  94. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  96. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  97. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  98. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  99. package/mcp-server/package-lock.json +2 -2
  100. package/mcp-server/package.json +1 -1
  101. package/mcp-server/src/http.ts +75 -12
  102. package/mcp-server/src/index.ts +147 -154
  103. package/mcp-server/src/preload.ts +12 -12
  104. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  105. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  106. package/mcp-server/src/services/neural-interface.ts +79 -20
  107. package/mcp-server/src/services/server-instructions.ts +27 -63
  108. package/mcp-server/src/services/tool-catalog.ts +403 -0
  109. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  110. package/mcp-server/src/services/tool-list.ts +410 -0
  111. package/mcp-server/src/services/tool-runtime.ts +457 -0
  112. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  113. package/mcp-server/src/tools/agents.ts +22 -14
  114. package/mcp-server/src/tools/image-tools.ts +1 -1
  115. package/mcp-server/src/tools/skill-tools.ts +589 -0
  116. package/mcp-server/src/tools/skill.ts +13 -0
  117. package/mcp-server/src/tools/tool-groups.ts +164 -0
  118. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  119. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  120. package/neural-interface/i18n/en.json +3680 -206
  121. package/neural-interface/i18n/pt-BR.json +3628 -157
  122. package/neural-interface/lib/account-usage.js +5 -3
  123. package/neural-interface/lib/app-controls-api.js +161 -0
  124. package/neural-interface/lib/assistant-api.js +148 -35
  125. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  126. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  127. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  128. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  129. package/neural-interface/lib/assistant-brains/opencode.js +122 -44
  130. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +59 -0
  131. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +97 -0
  132. package/neural-interface/lib/assistant-catalog.js +23 -9
  133. package/neural-interface/lib/assistant-dispatch.js +874 -156
  134. package/neural-interface/lib/assistant-envelope.js +22 -2
  135. package/neural-interface/lib/assistant-persona.js +80 -11
  136. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  137. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  138. package/neural-interface/lib/assistant-route-gate.js +6 -4
  139. package/neural-interface/lib/assistant-router.js +13 -8
  140. package/neural-interface/lib/assistant-runtime.js +183 -26
  141. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  142. package/neural-interface/lib/assistant-tools.js +51 -0
  143. package/neural-interface/lib/backup-service.js +620 -3
  144. package/neural-interface/lib/backup-zip-reader.js +10 -5
  145. package/neural-interface/lib/backup-zip-writer.js +257 -0
  146. package/neural-interface/lib/browser-execution.js +10 -2
  147. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  148. package/neural-interface/lib/browser-network-guard.js +284 -0
  149. package/neural-interface/lib/browser-policy-roots.js +64 -0
  150. package/neural-interface/lib/browser-tool-policy.js +186 -31
  151. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  152. package/neural-interface/lib/claude-hooks-status.js +103 -0
  153. package/neural-interface/lib/claude-model-catalog.js +64 -14
  154. package/neural-interface/lib/claude-temporary.js +5 -11
  155. package/neural-interface/lib/cli-install.js +18 -138
  156. package/neural-interface/lib/cli-resolver.js +4 -1
  157. package/neural-interface/lib/cli-update-planner.js +32 -31
  158. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  159. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  160. package/neural-interface/lib/codex-settings-schema.js +109 -47
  161. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  162. package/neural-interface/lib/confined-fs.js +30 -3
  163. package/neural-interface/lib/crash-notice-api.js +98 -0
  164. package/neural-interface/lib/editor-completion-opencode.js +164 -67
  165. package/neural-interface/lib/exec-loop-command.js +4 -1
  166. package/neural-interface/lib/http-guards.js +8 -2
  167. package/neural-interface/lib/install-details.js +4 -1
  168. package/neural-interface/lib/launch-cache.js +293 -0
  169. package/neural-interface/lib/login-start.js +524 -0
  170. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  171. package/neural-interface/lib/mcp-client-config.js +0 -29
  172. package/neural-interface/lib/mcp-external-servers.js +323 -0
  173. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  174. package/neural-interface/lib/morelogin.js +30 -0
  175. package/neural-interface/lib/native-loop-providers.js +565 -50
  176. package/neural-interface/lib/native-loop-runtime.js +287 -41
  177. package/neural-interface/lib/opencode-config.js +557 -0
  178. package/neural-interface/lib/opencode-db-schema.js +58 -0
  179. package/neural-interface/lib/opencode-history.js +49 -204
  180. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  181. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  182. package/neural-interface/lib/opencode-protocol.js +310 -0
  183. package/neural-interface/lib/opencode-provider-view.js +81 -0
  184. package/neural-interface/lib/opencode-runtime-config.js +32 -15
  185. package/neural-interface/lib/opencode-serve-records.js +624 -0
  186. package/neural-interface/lib/opencode-v2-client.js +53 -131
  187. package/neural-interface/lib/opencode-v2-ws-requests.js +35 -195
  188. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  189. package/neural-interface/lib/opencode-worker-data.js +27 -0
  190. package/neural-interface/lib/opencode2-client.js +796 -0
  191. package/neural-interface/lib/opencode2-proxy.js +76 -0
  192. package/neural-interface/lib/opencode2-translate.js +1057 -0
  193. package/neural-interface/lib/owner-authority.js +249 -0
  194. package/neural-interface/lib/page-session.js +275 -0
  195. package/neural-interface/lib/peer-identity.js +763 -0
  196. package/neural-interface/lib/process-identity.js +361 -0
  197. package/neural-interface/lib/request-gate.js +17 -2
  198. package/neural-interface/lib/rulesets/api.js +1 -1
  199. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  200. package/neural-interface/lib/rulesets/installer.js +92 -27
  201. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  202. package/neural-interface/lib/session-cache-builder.js +18 -10
  203. package/neural-interface/lib/session-title-generator.js +58 -18
  204. package/neural-interface/lib/skills/api-v2.js +495 -0
  205. package/neural-interface/lib/skills/bundle.js +418 -0
  206. package/neural-interface/lib/skills/catalog.js +294 -0
  207. package/neural-interface/lib/skills/creation-api.js +191 -0
  208. package/neural-interface/lib/skills/creation-brief.js +247 -0
  209. package/neural-interface/lib/skills/creation-sources.js +267 -0
  210. package/neural-interface/lib/skills/creation.js +843 -0
  211. package/neural-interface/lib/skills/deploy.js +564 -0
  212. package/neural-interface/lib/skills/digest.js +119 -0
  213. package/neural-interface/lib/skills/discover.js +111 -0
  214. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  215. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  216. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  217. package/neural-interface/lib/skills/eval-trace.js +119 -0
  218. package/neural-interface/lib/skills/evals-api.js +212 -0
  219. package/neural-interface/lib/skills/evals.js +1573 -0
  220. package/neural-interface/lib/skills/export.js +200 -0
  221. package/neural-interface/lib/skills/frontmatter.js +64 -0
  222. package/neural-interface/lib/skills/health.js +75 -0
  223. package/neural-interface/lib/skills/json-edit.js +165 -0
  224. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  225. package/neural-interface/lib/skills/known-bridges.js +96 -0
  226. package/neural-interface/lib/skills/library.js +823 -0
  227. package/neural-interface/lib/skills/local-store.js +21 -0
  228. package/neural-interface/lib/skills/migration.js +433 -0
  229. package/neural-interface/lib/skills/operations.js +45 -0
  230. package/neural-interface/lib/skills/ownership.js +427 -0
  231. package/neural-interface/lib/skills/packages.js +88 -0
  232. package/neural-interface/lib/skills/reviews.js +212 -0
  233. package/neural-interface/lib/skills/revision.js +80 -0
  234. package/neural-interface/lib/skills/roots.js +262 -0
  235. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  236. package/neural-interface/lib/skills/scan-assets.js +252 -0
  237. package/neural-interface/lib/skills/scan-context.js +26 -0
  238. package/neural-interface/lib/skills/scan-shell.js +324 -0
  239. package/neural-interface/lib/skills/scan.js +364 -0
  240. package/neural-interface/lib/skills/scanners.js +54 -0
  241. package/neural-interface/lib/skills/service.js +522 -0
  242. package/neural-interface/lib/skills/telemetry.js +113 -0
  243. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  244. package/neural-interface/lib/style-guide/api.js +4 -3
  245. package/neural-interface/lib/tool-lists.js +124 -0
  246. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  247. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  248. package/neural-interface/lib/worker-confinement.js +1192 -0
  249. package/neural-interface/lib/worker-guard-setting.js +56 -0
  250. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  251. package/neural-interface/lib/worker-relay-ports.js +227 -0
  252. package/neural-interface/lib/worker-resume.js +229 -0
  253. package/neural-interface/lib/worker-sandbox.js +299 -0
  254. package/neural-interface/package-lock.json +1354 -50
  255. package/neural-interface/package.json +6 -2
  256. package/neural-interface/public/index.html +5 -3
  257. package/neural-interface/public/index2d.html +19 -11
  258. package/neural-interface/public/onboarding.html +2 -2
  259. package/neural-interface/public/shared/account-menu.js +8 -5
  260. package/neural-interface/public/shared/agent-runtime-options.js +76 -16
  261. package/neural-interface/public/shared/api.js +1527 -1336
  262. package/neural-interface/public/shared/app-frame.js +192 -0
  263. package/neural-interface/public/shared/assistant/asst-agents-dock.js +205 -19
  264. package/neural-interface/public/shared/assistant/asst-api.js +11 -3
  265. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  266. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  267. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  268. package/neural-interface/public/shared/assistant/asst-brain-picker.js +241 -112
  269. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  270. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  271. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  272. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  273. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  274. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  275. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  276. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  277. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  278. package/neural-interface/public/shared/assistant/asst-menu.js +84 -12
  279. package/neural-interface/public/shared/assistant/asst-panel.js +303 -44
  280. package/neural-interface/public/shared/assistant/asst-rack-styles.js +93 -58
  281. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  282. package/neural-interface/public/shared/assistant/asst-render.js +770 -100
  283. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  284. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  285. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  286. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  287. package/neural-interface/public/shared/assistant/asst-scenes.js +530 -0
  288. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  289. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  290. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  291. package/neural-interface/public/shared/assistant/asst-state.js +25 -15
  292. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  293. package/neural-interface/public/shared/assistant/asst-styles.js +102 -21
  294. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +165 -13
  295. package/neural-interface/public/shared/automation-context.js +5 -2
  296. package/neural-interface/public/shared/cc-dropdown.js +550 -0
  297. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  298. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  299. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  300. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  301. package/neural-interface/public/shared/cdx/cdx-panel.js +58 -150
  302. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  303. package/neural-interface/public/shared/cdx/cdx-render.js +62 -32
  304. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  305. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  306. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  307. package/neural-interface/public/shared/cdx/cdx-tabs.js +201 -74
  308. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  309. package/neural-interface/public/shared/cli-install-view.js +4 -8
  310. package/neural-interface/public/shared/cli-status.js +2 -2
  311. package/neural-interface/public/shared/constants.js +2 -2
  312. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  313. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  314. package/neural-interface/public/shared/context-menu-model.js +366 -0
  315. package/neural-interface/public/shared/context-menu.js +372 -0
  316. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  317. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  318. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  319. package/neural-interface/public/shared/data-cache.js +223 -0
  320. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  321. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  322. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  323. package/neural-interface/public/shared/desktop-update-view.js +35 -5
  324. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  325. package/neural-interface/public/shared/file-links.js +73 -8
  326. package/neural-interface/public/shared/html-shell.js +78 -86
  327. package/neural-interface/public/shared/i18n.js +2 -0
  328. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  329. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  330. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  331. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  332. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  333. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +9 -2
  334. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  335. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  336. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  337. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  338. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  339. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  340. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  341. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  342. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +140 -254
  343. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  344. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +97 -203
  345. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  346. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  347. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -22
  348. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  349. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +20 -13
  350. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  351. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +53 -82
  352. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  353. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  354. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  355. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  356. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -40
  357. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  358. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +205 -62
  359. package/neural-interface/public/shared/opencode-providers.js +39 -0
  360. package/neural-interface/public/shared/page-session.js +96 -0
  361. package/neural-interface/public/shared/registry.js +5 -3
  362. package/neural-interface/public/shared/retired-2d.js +3 -0
  363. package/neural-interface/public/shared/schedules-view.js +452 -0
  364. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  365. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  366. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  367. package/neural-interface/public/shared/settings/settings-data.js +90 -0
  368. package/neural-interface/public/shared/settings/settings-ia.js +23 -6
  369. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  370. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  371. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  372. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  373. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  374. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  375. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  376. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  377. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  378. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  379. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  380. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  381. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  382. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  383. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  384. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  385. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  386. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  387. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  388. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  389. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  390. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  391. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  392. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  393. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  394. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  395. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  396. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  397. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  398. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  399. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  400. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  401. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  402. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  403. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  404. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  405. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  406. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  407. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  408. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  409. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  410. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  411. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  412. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  413. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  414. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  415. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  416. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  417. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  418. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  419. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  420. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  421. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  422. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  423. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  424. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  425. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  426. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  427. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  428. package/neural-interface/public/shared/styles.css +2065 -2150
  429. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  430. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  431. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  432. package/neural-interface/public/shared/tool-list-view.js +112 -0
  433. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  434. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  435. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  436. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  437. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  438. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  439. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  440. package/neural-interface/public/shared/tour/tour.css +144 -0
  441. package/neural-interface/public/shared/ui-assistant.js +27 -5
  442. package/neural-interface/public/shared/ui-automation-studio.js +155 -152
  443. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  444. package/neural-interface/public/shared/ui-claude-panel.js +153 -13
  445. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  446. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  447. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  448. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  449. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  450. package/neural-interface/public/shared/ui-detail.js +21 -9
  451. package/neural-interface/public/shared/ui-file-links.js +1 -0
  452. package/neural-interface/public/shared/ui-image-gallery.js +56 -52
  453. package/neural-interface/public/shared/ui-invite.js +18 -2
  454. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  455. package/neural-interface/public/shared/ui-link.js +9 -9
  456. package/neural-interface/public/shared/ui-menubar.js +229 -40
  457. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  458. package/neural-interface/public/shared/ui-native-loop-router.js +9 -1
  459. package/neural-interface/public/shared/ui-navbar.js +4 -24
  460. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  461. package/neural-interface/public/shared/ui-panels.js +96 -65
  462. package/neural-interface/public/shared/ui-schedules-studio.js +2267 -953
  463. package/neural-interface/public/shared/ui-sessions.js +20 -7
  464. package/neural-interface/public/shared/ui-settings.js +679 -496
  465. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  466. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  467. package/neural-interface/public/shared/ui-sidepanel-sessions.js +56 -5
  468. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  469. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  470. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  471. package/neural-interface/public/shared/ui-skills.js +42 -2781
  472. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  473. package/neural-interface/public/shared/ui-sync.js +22 -5
  474. package/neural-interface/public/shared/ui-terminal.js +159 -199
  475. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  476. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  477. package/neural-interface/public/shared/ui-trash.js +14 -5
  478. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  479. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  480. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -61
  481. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  482. package/neural-interface/public/shared/ui-tutorial.js +285 -499
  483. package/neural-interface/public/shared/ui-update.js +121 -16
  484. package/neural-interface/public/shared/ui-whiteboard.js +313 -59
  485. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  486. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  487. package/neural-interface/public/shared/view-variant.js +66 -0
  488. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  489. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  490. package/neural-interface/public/shared/worker-end.js +10 -3
  491. package/neural-interface/public/shared/worker-restart.js +12 -0
  492. package/neural-interface/public/sidepanel-session.html +1 -1
  493. package/neural-interface/public/variant/2d/background.js +2 -1
  494. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  495. package/neural-interface/public/variant/2d/graph.js +42 -20
  496. package/neural-interface/public/variant/2d/hulls.js +14 -9
  497. package/neural-interface/public/variant/2d/lasso.js +11 -7
  498. package/neural-interface/public/variant/2d/main.js +36 -2
  499. package/neural-interface/public/variant/2d/minimap.js +11 -13
  500. package/neural-interface/public/variant/3d/main.js +72 -6
  501. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  502. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  503. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  504. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  505. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  506. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  507. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  508. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  509. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  510. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  511. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  512. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  513. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  514. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  515. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  516. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  517. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  518. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  519. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  520. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  521. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  522. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  523. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  524. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  525. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  526. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  527. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  528. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  529. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  530. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  531. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  532. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  533. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  534. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  535. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  536. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  537. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  538. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  539. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  540. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  541. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  542. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  543. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  544. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  545. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  546. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  547. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  548. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  549. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  550. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  551. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  552. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  553. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  554. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  555. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  556. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  557. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  558. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  559. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  560. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  561. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  562. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  563. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  564. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  565. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  566. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  567. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  568. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  569. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  570. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  571. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  572. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  573. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  574. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  575. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  576. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  577. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  578. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  579. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  580. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  581. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  582. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  583. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  584. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  585. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  586. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  587. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  588. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  589. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  590. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  591. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  592. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  593. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  594. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  595. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  596. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  597. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  598. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  599. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  600. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  601. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  602. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  603. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  604. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  605. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  606. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  607. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  608. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  609. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  610. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  611. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  612. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  613. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  614. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  615. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  616. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  617. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  618. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  619. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  620. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  621. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  622. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  623. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  624. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  625. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  626. package/neural-interface/scripts/build-vendor.mjs +116 -3
  627. package/neural-interface/server.js +4834 -2541
  628. package/neural-interface/templates/rulesets/core.md +1 -1
  629. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  630. package/neural-interface/templates/rulesets/manifest.json +8 -8
  631. package/npm-shrinkwrap.json +18 -12
  632. package/package.json +16 -3
  633. package/postinstall.js +9 -4
  634. package/preuninstall.js +1 -1
  635. package/setup.js +97 -322
  636. package/skills/skill-forge/SKILL.md +177 -0
  637. package/skills/skill-forge/evals/evals.json +54 -0
  638. package/skills/skill-forge/references/package.md +105 -0
  639. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  640. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  641. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  642. package/skills/synabun/SKILL.md +10 -7
  643. package/skills/synabun/modules/audit.md +1 -5
  644. package/skills/synabun/modules/changelog.md +1 -5
  645. package/skills/synabun/modules/idea.md +1 -1
  646. package/skills/synabun/modules/memorize.md +6 -9
  647. package/skills/synabun/modules/schedule.md +5 -5
  648. package/mcp-server/dist/services/profiles.d.ts +0 -120
  649. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  650. package/mcp-server/dist/services/profiles.js +0 -387
  651. package/mcp-server/dist/services/profiles.js.map +0 -1
  652. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  653. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  654. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  655. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  656. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  657. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  658. package/mcp-server/dist/tools/leonardo.js +0 -21
  659. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  660. package/mcp-server/dist/tools/profile.d.ts +0 -17
  661. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  662. package/mcp-server/dist/tools/profile.js +0 -114
  663. package/mcp-server/dist/tools/profile.js.map +0 -1
  664. package/mcp-server/src/services/profiles.ts +0 -428
  665. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  666. package/mcp-server/src/tools/leonardo.ts +0 -30
  667. package/mcp-server/src/tools/profile.ts +0 -122
  668. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  669. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  670. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  671. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  672. package/skills/leonardo/SKILL.md +0 -168
  673. package/skills/leonardo/icon.svg +0 -1
  674. package/skills/leonardo/modules/browser-guide.md +0 -378
  675. package/skills/leonardo/modules/image-prompter.md +0 -283
  676. package/skills/leonardo/modules/model-advisor.md +0 -100
  677. package/skills/leonardo/modules/prompt-library.md +0 -110
  678. package/skills/leonardo/modules/style-guide.md +0 -186
  679. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -1,53 +1,33 @@
1
- // A reversible, viewport-based tour shared by both variants. It annotates the
2
- // user's current view: no config writes, forced focus mode or session creation.
3
- // What it changes to show a step (an open menu, the Assistant panel) it puts back.
1
+ // The Workspace tour's entry, and the project-exploration setup.
2
+ //
3
+ // The tour itself is public/shared/tour/: SynaBun's mascot narrates from its home in
4
+ // the Assistant panel and uses the panel as a whiteboard. This module opens it: from
5
+ // the ? button (a returning user gets the chapter list), or from the empty state's
6
+ // first-visit starter row ("Show me the workspace · 2 min", asst-panel.js). It never
7
+ // opens by itself. It presents the panel without starting a session
8
+ // (beginAssistantTour), keeps the saved cursor and the completed / skipped flags,
9
+ // and puts everything back when the tour ends.
10
+ //
11
+ // The setup (ONBOARDING_STEPS: explore → tokens → CLI → model → project → launch) is
12
+ // a separate small dialog, reached from the tour's chapter list. Only its explicit
13
+ // "Begin exploration" button starts a CLI; nothing else here starts work.
4
14
  import { storage, flushStorage } from './storage.js';
5
- import { state, emit, on } from './state.js';
15
+ import { emit, on } from './state.js';
6
16
  import { KEYS } from './constants.js';
7
17
  import { t } from './i18n.js';
8
- import { registerHelpSection, getVariant } from './registry.js';
9
- import { TUTORIAL_STEPS, FEATURE_STEPS, SKIP_HINT, CHAPTERS, buildExplorePrompt, resumeIndex } from './ui-tutorial-steps.js';
18
+ import { registerHelpSection } from './registry.js';
19
+ import { ONBOARDING_STEPS, buildExplorePrompt } from './ui-tutorial-steps.js';
10
20
  import { CLI_PROFILES, ensureModelsForProfile, modelSelectorValue, formatModelOptionLabel } from './agent-runtime-options.js';
11
- import { isAssistantPanelOpen, toggleAssistantPanel, assistantPanelPeekable } from './assistant/asst-sidepanel.js';
12
- import { createOutline, createAnimatedArrow, createHandDrawnUnderline, createDoodle } from './ui-tutorial-draw.js';
13
- import { visibleRect, unionRect, outlineShape, outlineRect, placeNote, arrowPoints } from './ui-tutorial-layout.js';
21
+ import { beginAssistantTour } from './assistant/asst-sidepanel.js';
22
+ import { dropdownHTML, setDropdownDisabled, setDropdownItems, setDropdownValue, wireDropdowns } from './cc-dropdown.js';
23
+ import { showGuestToast } from './ui-sync.js';
24
+ import { BEATS, beatsFor, resumeBeat } from './tour/tour-beats.js';
25
+ import { markTourSeen, tourReturning } from './tour/tour-state.js';
14
26
 
15
- const NS = 'http://www.w3.org/2000/svg';
16
- const INK = '#6da4ff'; // target outline, arrow, underline (Style Guide dark primary)
17
- const CHALK = 'rgba(240, 243, 247, 0.62)'; // the note's own outline
18
- const DRAW = 250; // one stroke (Style Guide motion "normal")
19
- const ERASE = 120; // the previous step's strokes fading out
20
- const SETTLE_MAX = 600; // longest wait for an opening menu or panel to stop moving
21
- const GUARDED = ['pointerdown', 'pointerup', 'mousedown', 'mouseup', 'click', 'dblclick', 'contextmenu',
22
- 'touchstart', 'dragstart', 'pointerover', 'mouseover', 'mouseenter', 'mousemove'];
23
- // Chrome the note leaves visible when there is free space.
24
- const KEEP_CLEAR = ['#title-bar', '#topright-controls', '#term-minimized-tray', '#wb-toolbar'];
25
- const PEER_PANEL_BUTTONS = ['#topright-claude-panel-btn', '#topright-codex-panel-btn', '#topright-opencode-panel-btn'];
26
- // Names the copy mentions, read off the control on screen so the text matches it
27
- // in every locale; the i18n key covers a control that is not in the DOM.
28
- const NAMES = {
29
- explorer: ['#topright-memory-explorer-btn', 'tutorial.names.explorer'],
30
- focus: ['#titlebar-viz-toggle', 'tooltip.toggleViz'],
31
- apps: ['[data-menu="apps"] > .menubar-label', 'nav.apps'],
32
- browser: ['#menu-terminal-browser', 'menu.apps.browser'],
33
- studio: ['#menu-open-automation-studio', 'menu.automations.studio'],
34
- schedules: ['#menu-automations-schedules', 'tutorial.names.schedules'],
35
- keybinds: ['#topright-keybinds-btn', 'tutorial.names.keybinds'],
36
- settings: ['#menubar-settings-btn', 'nav.settings'],
37
- projects: [null, 'settings.redesign.nav.projects.label'],
38
- computer: [null, 'assistant.computer.toggle'],
39
- more: [null, 'assistant.toolbar.more'],
40
- budget: [null, 'assistant.budget.title'],
41
- };
42
- const WELCOME = TUTORIAL_STEPS.findIndex(s => s.id === 'welcome');
43
-
44
- let active = false, initialized = false, index = WELCOME, firstRun = false, skipHint = false, resumed = false;
45
- let svg, dom, note, ui, observer, targetObserver, previousFocus, nextButton;
46
- let inks = {}; // the strokes on screen, by part: { g, key }
47
- let menuState = [], panelPeek = null, passthrough = false, placed = false, lastBox = null;
48
- let onScreen = null; // the step whose text is in the note: the next one is drawn only once its target holds still
49
- let timers = new Set(), generation = 0, resizeFrame = 0, stepAbort = null;
50
- let offLoaded = null, autoTimer = null, lastGeometry = '', beforeAdvance = null, onPrimary = null, onLater = null, ready = null;
27
+ let initialized = false;
28
+ let ready = null; // the tour's stylesheet and its handwriting, loaded once
29
+ let tour = null; // { engine, presentation }
30
+ let opening = false;
51
31
 
52
32
  const reduced = () => matchMedia('(prefers-reduced-motion: reduce)').matches;
53
33
  const tr = (key, params) => t(`tutorial.${key}`, params);
@@ -57,542 +37,348 @@ const node = (tag, cls, text) => {
57
37
  if (text != null) el.textContent = text;
58
38
  return el;
59
39
  };
60
- const currentStep = () => (skipHint ? SKIP_HINT : TUTORIAL_STEPS[index]);
61
-
62
- function later(fn, ms) {
63
- const id = setTimeout(() => { timers.delete(id); fn(); }, ms);
64
- timers.add(id);
65
- return id;
66
- }
67
- function clearStep() {
68
- generation++;
69
- for (const id of timers) clearTimeout(id);
70
- timers.clear();
71
- stepAbort?.abort(); stepAbort = null;
72
- }
73
-
74
- // ── What the copy and the annotations read from the live UI ──
75
40
 
76
- function names() {
77
- const out = {};
78
- for (const [name, [selector, key]] of Object.entries(NAMES)) {
79
- const el = selector && document.querySelector(selector);
80
- const label = el && (el.querySelector('.menu-text')?.textContent || el.dataset.tooltip || el.textContent);
81
- // A tooltip may end in its shortcut: "Focus Mode (V)".
82
- out[name] = label?.replace(/\s*\([^)]*\)\s*$/, '').trim() || t(key);
83
- }
84
- out.menu = document.querySelector('[data-menu="graph"] > .menubar-label')?.textContent.trim()
85
- || t(getVariant() === '2d' ? 'nav.graph' : 'nav.map');
86
- out.view = out.menu.toLocaleLowerCase();
87
- return out;
88
- }
89
- // A dropdown the tour opened is still fading in when its item is first measured.
90
- const shown = (el) => !el.checkVisibility
91
- || el.checkVisibility({ checkOpacity: !el.closest('.menubar-item.open, #assistant-panel.open'), checkVisibilityCSS: true });
92
- function targetRect(selector, lastSelector) {
93
- for (const el of document.querySelectorAll(selector)) {
94
- if (!shown(el)) continue;
95
- const last = lastSelector && document.querySelector(lastSelector);
96
- const rect = unionRect([el.getBoundingClientRect(), last && shown(last) ? last.getBoundingClientRect() : null]);
97
- if (visibleRect(rect, innerWidth, innerHeight)) return rect;
98
- }
99
- return null;
100
- }
101
- // The first alternative on screen. A hidden toolbar (or a guest's missing control) leaves `missing`.
102
- function resolveStep(step) {
103
- const targets = step.targets || [];
104
- for (let i = 0; i < targets.length; i++) {
105
- const [selector, copy = 'body', lastSelector] = targets[i];
106
- const rect = targetRect(selector, lastSelector);
107
- if (rect) return { rect, alternative: i, copy: i === 0 && step.copy?.[getVariant()] || copy, missing: false };
108
- }
109
- return { rect: null, alternative: -1, copy: step.copy?.[getVariant()] || 'body', missing: targets.length > 0 };
110
- }
111
-
112
- // ── What the tour changes to show a step, and puts back ──
113
-
114
- function showStage(step) {
115
- for (const [el, open] of menuState) {
116
- if (el.isConnected) el.classList.toggle('open', step.menu ? el.dataset.menu === step.menu : open);
117
- }
118
- if (step.panel !== 'assistant') { hidePanelPeek(); return; }
119
- // With no session tab, opening the Assistant starts a session: its button is annotated instead.
120
- if (panelPeek || isAssistantPanelOpen() || !assistantPanelPeekable()) return;
121
- panelPeek = {
122
- peers: PEER_PANEL_BUTTONS.map(s => document.querySelector(s)).filter(b => b?.classList.contains('active')),
123
- lastActivePanel: state.lastActivePanel, lastActiveSidepanel: state.lastActiveSidepanel,
124
- };
125
- toggleAssistantPanel();
126
- }
127
- function hidePanelPeek() {
128
- if (!panelPeek) return;
129
- const peek = panelPeek; panelPeek = null;
130
- if (isAssistantPanelOpen()) toggleAssistantPanel();
131
- // Showing the Assistant hid the docked panel that was open: bring it back.
132
- passthrough = true;
133
- for (const button of peek.peers) if (!button.classList.contains('active')) button.click();
134
- passthrough = false;
135
- state.lastActivePanel = peek.lastActivePanel; state.lastActiveSidepanel = peek.lastActiveSidepanel;
136
- }
137
- function restoreStage() {
138
- for (const [el, open] of menuState) if (el.isConnected) el.classList.toggle('open', open);
139
- hidePanelPeek();
41
+ function loadStyles() {
42
+ if (ready) return ready;
43
+ const sheets = ['/shared/tour/tour.css', '/shared/ui-tutorial.css', '/vendor/caveat/caveat.css'].map((href) => {
44
+ if (document.querySelector(`link[href="${href}"]`)) return Promise.resolve();
45
+ const link = node('link');
46
+ link.rel = 'stylesheet';
47
+ link.href = href;
48
+ const loaded = new Promise((done) => { link.onload = link.onerror = done; });
49
+ document.head.append(link);
50
+ return loaded;
51
+ });
52
+ // The handwriting is measured as it is written: wait for it (briefly; the fallback is cursive).
53
+ ready = Promise.race([
54
+ Promise.all([...sheets, document.fonts?.load('700 21px Caveat').catch(() => {}), document.fonts?.load('400 17px Caveat').catch(() => {})]),
55
+ new Promise(done => setTimeout(done, 1200)),
56
+ ]);
57
+ return ready;
140
58
  }
141
59
 
142
60
  export function initTutorial() {
143
61
  if (initialized) return;
144
62
  initialized = true;
145
- const css = node('link'); css.rel = 'stylesheet'; css.href = '/shared/ui-tutorial.css'; css.id = 'tutorial-styles';
146
- // The first note is measured with its stylesheet and handwriting font in place.
147
- ready = Promise.race([
148
- Promise.all([new Promise(done => { css.onload = css.onerror = done; }),
149
- document.fonts?.load("700 34px 'Caveat'").catch(() => {}), document.fonts?.load("26px 'Caveat'").catch(() => {})]),
150
- new Promise(done => setTimeout(done, 1200)),
151
- ]);
152
- document.head.append(css);
153
63
  const opener = document.querySelector('#titlebar-tutorial-btn');
154
64
  if (opener) {
155
- opener.dataset.tooltip = tr('title');
156
- opener.setAttribute('aria-label', tr('title'));
65
+ opener.dataset.tooltip = t('tour.title');
66
+ opener.setAttribute('aria-label', t('tour.title'));
157
67
  opener.setAttribute('aria-pressed', 'false');
158
68
  }
159
69
  // No HTML interpolation of translations: help registration accepts static HTML.
160
70
  const help = node('div', 'help-section');
161
- help.append(node('div', 'help-section-title', tr('title')), node('div', 'help-row', tr('helpDescription')));
71
+ help.append(node('div', 'help-section-title', t('tour.title')), node('div', 'help-row', tr('helpDescription')));
162
72
  registerHelpSection({ order: 95, html: help.outerHTML });
163
- if (![KEYS.TUTORIAL_COMPLETED, KEYS.TUTORIAL_SKIPPED, KEYS.TUTORIAL_STARTED].some(k => storage.getItem(k))) {
164
- offLoaded = on('data-loaded', () => {
165
- offLoaded?.(); offLoaded = null;
166
- autoTimer = setTimeout(() => { autoTimer = null; if (!active) startTutorial(false); }, 500);
167
- });
168
- }
73
+ // The empty state's starter row asks for the tour on the panel it sits in.
74
+ on('tour:open', (request = {}) => { startTour({ host: request.host || null, from: request.from || 'starter' }); });
75
+ window.addEventListener('pagehide', () => destroyTutorial());
169
76
  }
170
77
 
171
- export { resumeIndex } from './ui-tutorial-steps.js';
172
-
173
- // Navbar calls startTutorial(true): it is a toggle, preserving every choice.
174
- export function startTutorial(manual = false) {
175
- if (autoTimer) { clearTimeout(autoTimer); autoTimer = null; }
176
- offLoaded?.(); offLoaded = null;
177
- if (active) { destroyTutorial(); return; }
178
- firstRun = !manual && !storage.getItem(KEYS.TUTORIAL_COMPLETED) && !storage.getItem(KEYS.TUTORIAL_SKIPPED);
179
- index = resumeIndex(storage.getItem(KEYS.TUTORIAL_STEP), firstRun);
180
- resumed = index > WELCOME + 1;
181
- active = true; skipHint = false; placed = false; onScreen = null; lastBox = null; lastGeometry = ''; previousFocus = document.activeElement;
182
- menuState = [...document.querySelectorAll('.menubar-item')].map(el => [el, el.classList.contains('open')]);
183
- storage.setItem(KEYS.TUTORIAL_STARTED, 'true');
184
- svg = document.createElementNS(NS, 'svg'); svg.id = 'wb-tutorial'; svg.setAttribute('aria-hidden', 'true');
185
- dom = node('div'); dom.id = 'wb-tutorial-dom';
186
- buildNote();
187
- // Keep showcase menus and the underlying board from handling tour controls.
188
- for (const event of ['click', 'pointerdown', 'wheel']) dom.addEventListener(event, e => e.stopPropagation());
189
- document.body.append(dom, svg);
190
- document.querySelector('#titlebar-tutorial-btn')?.setAttribute('aria-pressed', 'true');
191
- window.addEventListener('keydown', keyHandler, true);
192
- for (const event of GUARDED) window.addEventListener(event, guardPointer, true);
193
- window.addEventListener('wheel', guardWheel, { capture: true, passive: false });
194
- window.addEventListener('focusin', guardFocus, true);
195
- window.addEventListener('resize', scheduleLayout);
196
- window.visualViewport?.addEventListener('resize', scheduleLayout);
197
- document.addEventListener('transitionend', onSettled, true);
198
- window.addEventListener('pagehide', destroyTutorial);
199
- observer = new ResizeObserver(scheduleLayout);
200
- observer.observe(note);
201
- targetObserver = new MutationObserver(records => {
202
- if (records.some(r => !r.target.closest?.('#wb-tutorial, #wb-tutorial-dom'))) scheduleLayout();
203
- });
204
- targetObserver.observe(document.body, { attributes: true, attributeFilter: ['class', 'hidden'], childList: true, subtree: true });
205
- emit('tutorial:start');
206
- const token = ++generation;
207
- (ready || Promise.resolve()).then(() => { if (active && token === generation) renderStep(); });
78
+ /** The ? button: a toggle. A tour on screen ends (as Esc does); else it opens. */
79
+ export function startTutorial() {
80
+ if (tour) { tour.engine.close('skip'); return; }
81
+ startTour({ from: 'help' });
208
82
  }
209
83
 
84
+ /** Ends a running tour at once (page hide, tests). */
210
85
  export function destroyTutorial() {
211
- if (!active) return;
212
- clearStep(); active = false;
213
- cancelAnimationFrame(resizeFrame); resizeFrame = 0;
214
- observer?.disconnect(); observer = null;
215
- targetObserver?.disconnect(); targetObserver = null;
216
- svg?.remove(); dom?.remove(); svg = dom = note = ui = nextButton = null; inks = {};
217
- window.removeEventListener('keydown', keyHandler, true);
218
- for (const event of GUARDED) window.removeEventListener(event, guardPointer, true);
219
- window.removeEventListener('wheel', guardWheel, true);
220
- window.removeEventListener('focusin', guardFocus, true);
221
- window.removeEventListener('resize', scheduleLayout);
222
- window.visualViewport?.removeEventListener('resize', scheduleLayout);
223
- document.removeEventListener('transitionend', onSettled, true);
224
- window.removeEventListener('pagehide', destroyTutorial);
225
- restoreStage(); menuState = [];
226
- document.querySelector('#titlebar-tutorial-btn')?.setAttribute('aria-pressed', 'false');
227
- if (previousFocus?.isConnected) previousFocus.focus({ preventScroll: true });
228
- previousFocus = null;
229
- flushStorage(); emit('tutorial:exit');
86
+ if (!tour) return;
87
+ tour.engine.close('gone');
230
88
  }
231
89
 
232
- // ── Input: the tour is modal. Nothing reaches the board, the graph or the panels behind it ──
233
-
234
- const allowed = (el) => !!el?.closest?.('#wb-tutorial-dom, #titlebar-tutorial-btn');
235
- function guardPointer(e) {
236
- if (passthrough || allowed(e.target)) return;
237
- if (e.cancelable) e.preventDefault();
238
- e.stopImmediatePropagation();
239
- }
240
- function guardWheel(e) { if (!e.target.closest?.('.tour-note')) { e.preventDefault(); e.stopImmediatePropagation(); } }
241
- // The Assistant panel focuses its composer when it opens: the keyboard stays on the tour.
242
- function guardFocus(e) { if (!passthrough && !allowed(e.target)) focusDefault(); }
243
- function focusDefault() {
244
- const launch = !skipHint && TUTORIAL_STEPS[index].kind === 'launch';
245
- (nextButton && !nextButton.disabled && !launch ? nextButton : note)?.focus({ preventScroll: true });
246
- }
247
- function keyHandler(e) {
248
- if (!active) return;
249
- if (e.key === 'Escape') { e.preventDefault(); e.stopImmediatePropagation(); destroyTutorial(); return; }
250
- if (e.key === 'Tab') {
251
- const controls = [...dom.querySelectorAll('button:not(:disabled):not([hidden]), select:not(:disabled)')];
252
- const pos = controls.indexOf(document.activeElement);
253
- if (pos < 0 || (!e.shiftKey && pos === controls.length - 1) || (e.shiftKey && pos === 0)) {
254
- e.preventDefault(); (e.shiftKey ? controls.at(-1) : controls[0])?.focus();
90
+ async function startTour({ host = null, from = 'help' } = {}) {
91
+ if (tour || opening) return;
92
+ opening = true;
93
+ try {
94
+ await loadStyles();
95
+ let sent = null; // what the user sent from a session-free preview's composer
96
+ const presentation = host ? null : beginAssistantTour({ onPreviewSend: (text) => { sent = text; tour?.engine.close('sent'); } });
97
+ const stage = host || presentation?.host;
98
+ if (!stage?.root || !stage.renderer) {
99
+ presentation?.restore({ reason: 'skip' });
100
+ if (!host) showGuestToast(t('assistant.panel.blocked'));
101
+ return;
255
102
  }
256
- e.stopImmediatePropagation(); return;
257
- }
258
- if (e.target.matches?.('select')) { e.stopImmediatePropagation(); return; }
259
- if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
260
- e.preventDefault(); e.stopImmediatePropagation();
261
- if (e.key === 'ArrowLeft') back(); else if (!nextButton.disabled) advance();
262
- return;
263
- }
264
- if (e.key === 'Enter' && !e.target.closest?.('button')) {
265
- e.preventDefault(); e.stopImmediatePropagation();
266
- if (!nextButton.disabled) advance();
267
- return;
103
+ const returning = from === 'help' && tourReturning(storage);
104
+ const saved = storage.getItem(KEYS.TUTORIAL_STEP);
105
+ const resumeAt = saved != null && !storage.getItem(KEYS.TUTORIAL_COMPLETED) ? resumeBeat(saved) : -1;
106
+ const { createTour } = await import('./tour/tour-engine.js');
107
+ storage.setItem(KEYS.TUTORIAL_STARTED, 'true');
108
+ const env = { computer: stage.computer !== false };
109
+ const engine = createTour({
110
+ host: stage,
111
+ tr: (key, params) => t(key, params),
112
+ reduced,
113
+ env,
114
+ beats: beatsFor(env),
115
+ resumeAt,
116
+ onBeat: (beat) => { storage.setItem(KEYS.TUTORIAL_STEP, beat.id); emit('tutorial:step', { id: beat.id }); },
117
+ onComplete: () => {
118
+ markTourSeen(storage, 'completed');
119
+ storage.removeItem(KEYS.TUTORIAL_STEP);
120
+ flushStorage();
121
+ try { stage.refreshStarters?.(); } catch { /* the starters come back as they were */ }
122
+ emit('tutorial:complete');
123
+ },
124
+ onExplore: () => openSetup(),
125
+ onClose: (reason) => {
126
+ if (reason === 'skip' && !storage.getItem(KEYS.TUTORIAL_COMPLETED)) {
127
+ markTourSeen(storage, 'skipped');
128
+ try { stage.refreshStarters?.(); } catch { /* as they were */ }
129
+ emit('tutorial:skip');
130
+ }
131
+ flushStorage();
132
+ presentation?.restore({ reason });
133
+ if (reason === 'sent' && sent) presentation?.deliver(sent);
134
+ if (reason === 'done' && (!presentation || presentation.live)) stage.focusComposer?.();
135
+ tour = null;
136
+ document.querySelector('#titlebar-tutorial-btn')?.setAttribute('aria-pressed', 'false');
137
+ emit('tutorial:exit', { reason });
138
+ },
139
+ });
140
+ tour = { engine, presentation };
141
+ document.querySelector('#titlebar-tutorial-btn')?.setAttribute('aria-pressed', 'true');
142
+ emit('tutorial:start', { from });
143
+ if (returning) engine.openList();
144
+ else engine.play(0);
145
+ } finally {
146
+ opening = false;
268
147
  }
269
- // Native button Enter/Space still activate; product shortcuts never leak out.
270
- e.stopImmediatePropagation();
271
148
  }
272
149
 
273
- // ── Navigation ──
150
+ export { BEATS as TOUR_BEATS };
274
151
 
275
- function go(next) {
276
- if (!active || next < (firstRun ? 0 : WELCOME) || next >= TUTORIAL_STEPS.length) return;
277
- index = next; skipHint = false; renderStep();
278
- }
279
- function back() {
280
- if (skipHint) { skipHint = false; renderStep(); return; }
281
- go(index - 1);
282
- }
283
- function advance() {
284
- if (skipHint) { destroyTutorial(); return; }
285
- const step = TUTORIAL_STEPS[index];
286
- beforeAdvance?.();
287
- if (step.kind === 'explore') {
288
- storage.setItem(KEYS.ONBOARDING_EXPLORE, 'no'); go(WELCOME); return;
289
- }
290
- if (step.kind === 'launch') return; // Only the explicit launch button starts work.
291
- if (index === TUTORIAL_STEPS.length - 1) {
292
- storage.setItem(KEYS.TUTORIAL_COMPLETED, 'true'); storage.removeItem(KEYS.TUTORIAL_STEP);
293
- destroyTutorial(); emit('tutorial:complete'); return;
294
- }
295
- go(index + 1);
296
- }
297
- function skip() {
298
- storage.setItem(KEYS.TUTORIAL_SKIPPED, 'true');
299
- skipHint = true; renderStep(); emit('tutorial:skip');
152
+ /** The tour on screen (its engine: play, next, prev, close, index…), or null. For tests and devtools. */
153
+ export function currentTour() {
154
+ return tour?.engine || null;
300
155
  }
301
156
 
302
- // ── The note: one element for the whole tour, so nothing blinks between steps ──
157
+ // ── The project-exploration setup: a small dialog of its own ──
303
158
 
304
- function button(text, fn, cls = '') {
305
- const el = node('button', cls, text); el.type = 'button'; el.addEventListener('click', fn); return el;
306
- }
307
- function buildNote() {
308
- note = node('section', 'tour-note tour-snap'); note.tabIndex = -1;
309
- note.setAttribute('role', 'dialog'); note.setAttribute('aria-modal', 'true');
310
- note.setAttribute('aria-labelledby', 'tour-heading'); note.setAttribute('aria-describedby', 'tour-copy');
311
- const progress = node('div', 'tour-progress');
312
- const dots = node('span', 'tour-dots'); dots.setAttribute('role', 'img');
313
- for (const chapter of CHAPTERS) { const dot = node('span'); dot.dataset.chapter = chapter; dots.append(dot); }
314
- ui = {
315
- label: node('span'), dots,
316
- restart: button(tr('restart'), () => { resumed = false; go(WELCOME); }, 'tour-link'),
317
- body: node('div', 'tour-body'),
318
- prev: button(tr('back'), back),
319
- skip: button('', () => (onLater || skip)(), 'tour-link tour-skip'),
320
- };
321
- nextButton = button('', () => (onPrimary || advance)(), 'primary');
322
- progress.append(ui.label, ui.restart, dots);
323
- const nav = node('nav', 'tour-nav'); nav.setAttribute('aria-label', tr('navigation'));
324
- nav.append(ui.prev, nextButton, ui.skip);
325
- note.append(progress, ui.body, nav, node('p', 'tour-key-hint', tr('keys')));
326
- dom.append(note);
159
+ let setup = null; // { dialog, index, generation, onPrimary, beforeAdvance, previousFocus, abort }
160
+
161
+ function names() {
162
+ const settings = document.querySelector('#menubar-settings-btn .menu-text, #menubar-settings-btn')?.textContent?.trim() || t('nav.settings');
163
+ return { settings, projects: t('settings.redesign.nav.projects.label') };
327
164
  }
328
- // Handwriting appears word by word; the text itself is ordinary DOM text.
329
- function write(el, text, start) {
330
- if (reduced()) { el.textContent = text; return; }
331
- const words = text.split(' ');
332
- const pace = Math.min(26, 400 / words.length);
333
- el.replaceChildren();
334
- words.forEach((word, i) => {
335
- const span = node('span', 'tour-word', word);
336
- span.style.animationDelay = `${Math.round(start + i * pace)}ms`;
337
- el.append(span);
338
- if (i < words.length - 1) el.append(' ');
339
- });
165
+
166
+ /** Open the setup at its first screen (or `index`). */
167
+ export function openSetup(index = 0) {
168
+ if (setup) { renderSetup(index); return; }
169
+ loadStyles();
170
+ const overlay = node('div', 'tour-setup-overlay');
171
+ const dialog = node('section', 'tour-setup');
172
+ dialog.setAttribute('role', 'dialog');
173
+ dialog.setAttribute('aria-modal', 'true');
174
+ dialog.setAttribute('aria-labelledby', 'tour-setup-title');
175
+ dialog.tabIndex = -1;
176
+ overlay.append(dialog);
177
+ document.body.append(overlay);
178
+ const controller = new AbortController();
179
+ wireDropdowns(dialog, { signal: controller.signal });
180
+ setup = { overlay, dialog, index: 0, generation: 0, onPrimary: null, beforeAdvance: null, previousFocus: document.activeElement, controller, abort: null };
181
+ overlay.addEventListener('click', (e) => { if (e.target === overlay) closeSetup(); }, { signal: controller.signal });
182
+ window.addEventListener('keydown', onSetupKey, { capture: true, signal: controller.signal });
183
+ renderSetup(index);
340
184
  }
341
- function renderStep() {
342
- clearStep();
343
- const step = currentStep();
344
- if (!skipHint) storage.setItem(KEYS.TUTORIAL_STEP, step.id);
185
+
186
+ export function closeSetup() {
187
+ if (!setup) return;
188
+ const { overlay, controller, previousFocus, abort } = setup;
189
+ abort?.abort();
190
+ setup.generation += 1;
191
+ setup = null;
192
+ controller.abort();
193
+ const leave = () => overlay.remove();
194
+ if (reduced() || typeof overlay.animate !== 'function') leave();
195
+ else overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: 160, easing: 'cubic-bezier(.2,0,1,.9)' }).finished.then(leave, leave);
196
+ if (previousFocus?.isConnected) previousFocus.focus({ preventScroll: true });
345
197
  flushStorage();
346
- const show = () => { showStage(step); settle(step, () => { fillNote(step); layout(true); keepFocus(step); }); };
347
- // Holding an arrow key runs through the steps: only the one it stops on moves the Assistant panel.
348
- const panelMoves = panelPeek ? step.panel !== 'assistant'
349
- : step.panel === 'assistant' && !isAssistantPanelOpen() && assistantPanelPeekable();
350
- if (panelMoves) later(show, 150); else show();
351
- emit('tutorial:step', { index, id: step.id });
352
198
  }
353
- // A menu or panel the tour just opened is still moving: draw once the target holds still.
354
- function settle(step, done) {
355
- const staged = !!step.menu || (step.panel === 'assistant' && isAssistantPanelOpen());
356
- // Staged, the target is the control inside the menu or panel, not the fallback outside it.
357
- const inside = step.targets ? step.targets.length - (step.panel ? 1 : 0) : 0;
358
- const started = performance.now();
359
- let last = null;
360
- const tick = () => {
361
- const found = resolveStep(step);
362
- const now = found.rect ? [found.alternative, found.rect.left, found.rect.top, found.rect.width, found.rect.height].map(Math.round).join() : '';
363
- const arrived = !staged || (found.alternative >= 0 && found.alternative < inside);
364
- if ((now === last && arrived) || performance.now() - started > SETTLE_MAX) { done(); return; }
365
- last = now; later(tick, 32);
366
- };
367
- tick();
368
- }
369
- function fillNote(step) {
370
- onScreen = step; beforeAdvance = onPrimary = onLater = null;
371
- const found = resolveStep(step);
372
- const featureIndex = FEATURE_STEPS.indexOf(step);
373
- const setup = !!step.kind && step.kind !== 'welcome';
374
- note.dataset.step = step.id;
375
- ui.label.textContent = setup ? tr('setupProgress', { n: index + 1, total: WELCOME })
376
- : featureIndex >= 0 ? `${tr(`chapters.${step.chapter}`)} · ${tr('progress', { n: featureIndex + 1, total: FEATURE_STEPS.length })}`
377
- : tr('title');
378
- ui.dots.hidden = !step.chapter;
379
- ui.dots.setAttribute('aria-label', step.chapter ? tr(`chapters.${step.chapter}`) : '');
380
- for (const dot of ui.dots.children) dot.classList.toggle('current', dot.dataset.chapter === step.chapter);
381
- ui.restart.hidden = !(resumed && featureIndex > 0);
382
-
383
- const heading = node('h2'); heading.id = 'tour-heading';
384
- const copy = node('p', 'tour-copy'); copy.id = 'tour-copy'; copy.dataset.copy = found.copy;
385
- // After the target's outline and the arrow; at once when there is no target.
386
- const base = found.rect ? 350 : 100;
387
- write(heading, tr(`steps.${step.id}.title`), base);
388
- write(copy, tr(`steps.${step.id}.${found.copy}`, names()), base + 150);
389
- const unavailable = node('p', 'tour-status tour-unavailable', tr('unavailable')); unavailable.hidden = !found.missing;
390
- const extra = node('div', 'tour-extra');
391
- ui.body.replaceChildren(heading, copy, unavailable, extra);
392
199
 
393
- ui.prev.disabled = !skipHint && index <= (firstRun ? 0 : WELCOME);
394
- ui.skip.hidden = skipHint || index === TUTORIAL_STEPS.length - 1;
395
- // Past the first setup screen the way out of the exploration is "later": the tour itself is skipped from its welcome.
396
- const postpone = setup && step.kind !== 'explore';
397
- ui.skip.textContent = postpone ? tr('later') : tr('skip');
398
- if (postpone) onLater = () => go(WELCOME);
399
- nextButton.disabled = false;
400
- nextButton.textContent = skipHint ? tr('close') : step.kind === 'welcome' ? tr('start') : step.kind === 'explore' ? tr('later')
401
- : index === TUTORIAL_STEPS.length - 1 ? tr('finish') : tr('next');
402
- if (setup) renderOnboarding(step, extra);
403
- }
404
- // Focus stays on the button the user is working with; it moves only when that button is gone.
405
- function keepFocus(step) {
406
- const focused = document.activeElement;
407
- if (!dom.contains(focused) || focused === note || focused.disabled || focused.hidden || (step.kind === 'launch' && focused === nextButton)) focusDefault();
408
- }
409
- function scheduleLayout() {
410
- if (!active || resizeFrame) return;
411
- resizeFrame = requestAnimationFrame(() => { resizeFrame = 0; layout(false); });
412
- }
413
- function onSettled(e) { if (!e.target.closest?.('#wb-tutorial, #wb-tutorial-dom')) scheduleLayout(); }
414
- function lastLine(el, origin) {
415
- const range = document.createRange(); range.selectNodeContents(el);
416
- const rects = [...range.getClientRects()].filter(r => r.width > 0);
417
- const bottom = Math.max(...rects.map(r => r.bottom));
418
- const line = unionRect(rects.filter(r => r.bottom > bottom - 8));
419
- return { left: line.left - origin.left, bottom: line.bottom - origin.top, width: line.width };
420
- }
421
- // One part of the drawing (the target's strokes, the note's) is redrawn only when its geometry changed.
422
- function redraw(part, key, motion, draw) {
423
- const old = inks[part];
424
- if (old?.key === key) return;
425
- if (old) {
426
- if (motion) { old.g.classList.add('tour-leaving'); setTimeout(() => old.g.remove(), ERASE); } else old.g.remove();
200
+ function onSetupKey(e) {
201
+ if (!setup) return;
202
+ if (e.key === 'Escape') {
203
+ if (setup.dialog.querySelector('.cc-dropdown.open')) return; // the open dropdown closes first
204
+ e.preventDefault(); e.stopImmediatePropagation(); closeSetup(); return;
205
+ }
206
+ if (e.key === 'Tab') {
207
+ const controls = [...setup.dialog.querySelectorAll('button:not(:disabled)')].filter(el => el.getClientRects().length);
208
+ const pos = controls.indexOf(document.activeElement);
209
+ if (pos < 0 || (!e.shiftKey && pos === controls.length - 1) || (e.shiftKey && pos === 0)) {
210
+ e.preventDefault(); (e.shiftKey ? controls.at(-1) : controls[0])?.focus();
211
+ }
212
+ e.stopImmediatePropagation();
213
+ return;
427
214
  }
428
- const g = document.createElementNS(NS, 'g'); svg.append(g);
429
- inks[part] = { g, key };
430
- draw(g);
215
+ // Product shortcuts never leak out of the dialog; its own buttons and dropdowns keep their keys.
216
+ if (setup.dialog.contains(e.target)) e.stopImmediatePropagation();
431
217
  }
432
- function layout(animate) {
433
- // A relayout follows the step on screen; the one being prepared is not drawn early.
434
- const step = onScreen;
435
- if (!active || !note || !step) return;
436
- const found = resolveStep(step);
437
- // The view changed under an open step (the whiteboard toolbar appeared): its copy follows.
438
- const copy = ui.body.querySelector('#tour-copy');
439
- if (copy.dataset.copy !== found.copy) { copy.dataset.copy = found.copy; copy.textContent = tr(`steps.${step.id}.${found.copy}`, names()); }
440
- ui.body.querySelector('.tour-unavailable').hidden = !found.missing;
441
-
442
- const rects = (selectors) => selectors.flatMap(s => [...document.querySelectorAll(s)])
443
- .filter(shown).map(el => el.getBoundingClientRect()).filter(r => r.width > 0 && r.height > 0);
444
- const avoid = rects(['.menubar-item.open .menubar-dropdown']), keep = rects(KEEP_CLEAR);
445
- const key = (...values) => values.map(v => Math.round(v)).join(',');
446
- const flat = (list) => list.flatMap(r => [r.left, r.top, r.width, r.height]);
447
- // The app redraws itself all the time: nothing the note depends on moved, nothing to do.
448
- const geometry = key(innerWidth, innerHeight, note.offsetWidth, note.scrollHeight, ...flat(found.rect ? [found.rect] : []), -1, ...flat(avoid), -1, ...flat(keep));
449
- if (geometry === lastGeometry && !animate) return;
450
- const moved = geometry !== lastGeometry;
451
- lastGeometry = geometry;
452
- note.style.maxHeight = '';
453
- const box = placeNote(found.rect, innerWidth, innerHeight, note.offsetWidth, note.offsetHeight, { avoid, keep, near: lastBox });
454
- note.style.maxHeight = `${box.height}px`;
455
-
456
- const motion = animate && !reduced();
457
- const glide = motion && placed && moved ? DRAW : 0;
458
- const stroke = (ms) => (motion ? ms : 0);
459
- note.classList.toggle('tour-snap', !glide);
460
- note.style.transform = `translate(${Math.round(box.left)}px, ${Math.round(box.top)}px)`;
461
- note.classList.add('tour-placed');
462
- placed = true; lastBox = box;
463
- svg.setAttribute('viewBox', `0 0 ${innerWidth} ${innerHeight}`);
464
218
 
465
- // Order: the target's outline, the arrow, then the note with its text.
466
- let at = 0, arrowFrom = null;
467
- if (found.rect) {
468
- const shape = outlineShape(found.rect);
469
- const ring = outlineRect(found.rect, innerWidth, innerHeight, shape);
470
- const arrow = arrowPoints(box, ring, 6);
471
- arrowFrom = arrow.from;
472
- redraw('target', key(ring.left, ring.top, ring.width, ring.height, arrow.from.x, arrow.from.y, arrow.to.x, arrow.to.y), motion, (g) => {
473
- createOutline(g, ring, { shape, color: INK, strokeWidth: 2.5, wobbleSeed: step.id, duration: stroke(DRAW) });
474
- createAnimatedArrow(g, { ...arrow, color: INK, strokeWidth: 2.5, wobbleSeed: step.id,
475
- duration: stroke(DRAW), delay: stroke(Math.max(200, glide)) });
476
- at = stroke(350);
477
- });
478
- } else redraw('target', '', motion, () => {});
479
- // The underline is SVG, the heading is not: natural wrapping, reliable font
480
- // metrics and semantic copy for screen readers.
481
- const origin = note.getBoundingClientRect();
482
- const line = lastLine(ui.body.querySelector('h2'), origin);
483
- redraw('note', key(box.left, box.top, box.width, box.height, line.left, line.bottom, line.width, arrowFrom?.x || 0), motion, (g) => {
484
- const edge = { left: box.left - 5, top: box.top - 5, right: box.right + 5, bottom: box.bottom + 5, width: box.width + 10, height: box.height + 10 };
485
- createOutline(g, edge, { shape: 'box', radius: 18, wobble: 2.4, color: CHALK, strokeWidth: 1.6,
486
- wobbleSeed: `${step.id}-note`, duration: stroke(DRAW + 100), delay: Math.max(at, glide) });
487
- createHandDrawnUnderline(g, { left: box.left + line.left, bottom: box.top + line.bottom, width: line.width },
488
- { color: INK, wobbleSeed: step.id, duration: stroke(200), delay: Math.max(at, glide) + stroke(300), offset: 3 });
489
- // The flourish sits on the note's top corner, the one the arrow does not leave from.
490
- const corner = arrowFrom && arrowFrom.x > box.left + box.width / 2 && arrowFrom.y < box.top + 60 ? box.left - 8 : box.right - 22;
491
- createDoodle(g, step.kind === 'warning' ? 'alert' : 'sparkle', { x: corner, y: Math.max(4, box.top - 20),
492
- scale: 0.7, color: INK, duration: stroke(200), delay: Math.max(at, glide) + stroke(400) });
493
- });
219
+ function button(text, fn, cls = '') {
220
+ const el = node('button', cls, text);
221
+ el.type = 'button';
222
+ el.addEventListener('click', fn);
223
+ return el;
494
224
  }
495
225
 
496
- // ── First-run setup: the optional project exploration ──
226
+ function goSetup(next) {
227
+ if (!setup) return;
228
+ if (next < 0) return;
229
+ if (next >= ONBOARDING_STEPS.length) { closeSetup(); return; }
230
+ renderSetup(next);
231
+ }
497
232
 
498
- function renderOnboarding(step, content) {
499
- const token = generation;
500
- const fresh = () => active && generation === token && content.isConnected;
233
+ function renderSetup(index) {
234
+ if (!setup) return;
235
+ setup.abort?.abort();
236
+ setup.abort = null;
237
+ const my = ++setup.generation;
238
+ setup.index = index;
239
+ setup.onPrimary = setup.beforeAdvance = null;
240
+ const step = ONBOARDING_STEPS[index];
241
+ const fresh = () => !!setup && setup.generation === my;
242
+ const { dialog } = setup;
243
+ const heading = node('h2', 'tour-setup-title', tr(`steps.${step.id}.title`));
244
+ heading.id = 'tour-setup-title';
245
+ const progress = node('div', 'tour-setup-progress', tr('setupProgress', { n: index + 1, total: ONBOARDING_STEPS.length }));
246
+ const copy = node('p', 'tour-setup-copy', tr(`steps.${step.id}.body`, names()));
247
+ const extra = node('div', 'tour-setup-extra');
248
+ const nav = node('nav', 'tour-setup-nav');
249
+ nav.setAttribute('aria-label', tr('navigation'));
250
+ const back = button(tr('back'), () => goSetup(index - 1), 'tour-setup-btn');
251
+ back.disabled = index === 0;
252
+ const later = button(tr('later'), () => closeSetup(), 'tour-setup-btn tour-setup-link');
253
+ const primary = button(step.kind === 'explore' ? tr('later') : tr('next'), () => {
254
+ if (setup?.onPrimary) { setup.onPrimary(); return; }
255
+ setup?.beforeAdvance?.();
256
+ if (step.kind === 'explore') { storage.setItem(KEYS.ONBOARDING_EXPLORE, 'no'); closeSetup(); return; }
257
+ goSetup(index + 1);
258
+ }, 'tour-setup-btn tour-setup-primary');
259
+ nav.append(back, later, primary);
260
+ if (step.kind === 'explore') later.hidden = true;
261
+ dialog.replaceChildren(progress, heading, copy, extra, nav);
262
+ if (!reduced()) dialog.animate([{ opacity: 0.4, transform: 'translateY(4px)' }, { opacity: 1, transform: 'none' }], { duration: 200, easing: 'cubic-bezier(0,0,.38,.9)' });
501
263
  const saveChoice = (key, value) => { storage.setItem(key, value); flushStorage(); };
502
- const field = (labelText) => {
503
- const label = node('label', 'tour-field', labelText);
504
- const select = node('select'); label.append(select); content.append(label); return select;
264
+ const status = (text = tr('loading')) => { const el = node('p', 'tour-setup-status', text); el.setAttribute('role', 'status'); extra.append(el); return el; };
265
+ const retry = () => extra.append(button(tr('retry'), () => renderSetup(index), 'tour-setup-btn'));
266
+ const field = (label, id) => {
267
+ const wrap = node('div', 'tour-setup-field');
268
+ const caption = node('span', 'tour-setup-label', label);
269
+ caption.id = `${id}-label`;
270
+ wrap.append(caption);
271
+ wrap.insertAdjacentHTML('beforeend', dropdownHTML(id, [{ value: '', label: tr('loading'), selected: true }], { labelledBy: caption.id, disabled: true }));
272
+ extra.append(wrap);
273
+ return wrap.querySelector(`#${id}`);
505
274
  };
506
- const option = (select, label, value) => { const o = node('option', '', label); o.value = value; select.append(o); };
507
- const status = (text = tr('loading')) => { const el = node('p', 'tour-status', text); el.setAttribute('role', 'status'); content.append(el); return el; };
508
- const retry = () => content.append(button(tr('retry'), () => renderStep()));
275
+
509
276
  if (step.kind === 'explore') {
510
- content.append(button(tr('explore'), () => { saveChoice(KEYS.ONBOARDING_EXPLORE, 'yes'); go(index + 1); }));
277
+ extra.append(button(tr('explore'), () => { saveChoice(KEYS.ONBOARDING_EXPLORE, 'yes'); goSetup(index + 1); }, 'tour-setup-btn tour-setup-primary'));
511
278
  } else if (step.kind === 'cli') {
512
- const choices = node('div', 'tour-options'); content.append(choices);
513
- nextButton.disabled = !CLI_PROFILES.some(c => c.id === storage.getItem(KEYS.ONBOARDING_CLI));
279
+ const choices = node('div', 'tour-setup-options');
280
+ extra.append(choices);
281
+ primary.disabled = !CLI_PROFILES.some(c => c.id === storage.getItem(KEYS.ONBOARDING_CLI));
514
282
  for (const cli of CLI_PROFILES) {
515
283
  const choice = button(cli.label, () => {
516
284
  if (storage.getItem(KEYS.ONBOARDING_CLI) !== cli.id) storage.removeItem(KEYS.ONBOARDING_MODEL);
517
- saveChoice(KEYS.ONBOARDING_CLI, cli.id); go(index + 1);
518
- }, 'tour-choice');
519
- choice.setAttribute('aria-pressed', String(storage.getItem(KEYS.ONBOARDING_CLI) === cli.id)); choices.append(choice);
285
+ saveChoice(KEYS.ONBOARDING_CLI, cli.id);
286
+ goSetup(index + 1);
287
+ }, 'tour-setup-choice');
288
+ choice.setAttribute('aria-pressed', String(storage.getItem(KEYS.ONBOARDING_CLI) === cli.id));
289
+ choices.append(choice);
520
290
  }
521
291
  } else if (step.kind === 'model') {
522
- const select = field(tr('model')); select.disabled = true; nextButton.disabled = true;
523
- const loading = status(); let settled = false;
292
+ const input = field(tr('model'), 'tour-setup-model');
293
+ primary.disabled = true;
294
+ const loading = status();
295
+ let settled = false;
524
296
  const finish = (models) => {
525
297
  if (!fresh() || settled) return;
526
- settled = true; clearTimeout(timeout); timers.delete(timeout);
527
- option(select, tr('cliDefault'), '');
528
- for (const m of models) option(select, formatModelOptionLabel(m, { includeService: true }), modelSelectorValue(m));
529
- select.value = storage.getItem(KEYS.ONBOARDING_MODEL) || '';
530
- if (select.selectedIndex < 0) select.value = '';
531
- select.disabled = false; nextButton.disabled = false;
298
+ settled = true;
299
+ clearTimeout(timeout);
300
+ const saved = storage.getItem(KEYS.ONBOARDING_MODEL) || '';
301
+ const entries = [{ value: '', label: tr('cliDefault') }, ...models.map(m => ({ value: modelSelectorValue(m), label: formatModelOptionLabel(m, { includeService: true }) }))];
302
+ setDropdownItems(input, entries.map(e => ({ ...e, selected: e.value === saved })));
303
+ setDropdownValue(input, entries.some(e => e.value === saved) ? saved : '');
304
+ setDropdownDisabled(input, false);
305
+ primary.disabled = false;
532
306
  loading.textContent = models.length ? tr('catalog') : tr('catalogUnavailable');
533
- select.addEventListener('change', () => saveChoice(KEYS.ONBOARDING_MODEL, select.value));
534
- beforeAdvance = () => saveChoice(KEYS.ONBOARDING_MODEL, select.value);
307
+ input.addEventListener('change', () => saveChoice(KEYS.ONBOARDING_MODEL, input.value));
308
+ setup.beforeAdvance = () => saveChoice(KEYS.ONBOARDING_MODEL, input.value);
535
309
  if (!models.length) retry();
536
310
  };
537
- const timeout = later(() => finish([]), 8000);
311
+ const timeout = setTimeout(() => finish([]), 8000);
538
312
  ensureModelsForProfile(storage.getItem(KEYS.ONBOARDING_CLI)).then(m => finish(Array.isArray(m) ? m : [])).catch(() => finish([]));
539
313
  } else if (step.kind === 'project') {
540
- const select = field(tr('project')); select.disabled = true; nextButton.disabled = true;
314
+ const input = field(tr('project'), 'tour-setup-project');
315
+ primary.disabled = true;
541
316
  const loading = status();
542
- stepAbort = new AbortController();
543
- const abort = stepAbort;
544
- const timeout = later(() => abort.abort(), 8000);
545
- fetch('/api/terminal/profiles', { signal: abort.signal }).then(r => { if (!r.ok) throw new Error('profiles'); return r.json(); }).then(data => {
317
+ const abort = new AbortController();
318
+ setup.abort = abort;
319
+ const timeout = setTimeout(() => abort.abort(), 8000);
320
+ fetch('/api/terminal/profiles', { signal: abort.signal }).then(r => { if (!r.ok) throw new Error('profiles'); return r.json(); }).then((data) => {
546
321
  if (!fresh()) return;
547
322
  const projects = (data.projects || []).filter(p => p.path);
548
323
  const name = (p) => p.label || p.path.split(/[\\/]/).filter(Boolean).pop();
549
- option(select, tr('chooseProject'), '');
550
- projects.forEach((p, i) => option(select, name(p), String(i)));
551
- let prior = null; try { prior = JSON.parse(storage.getItem(KEYS.ONBOARDING_PROJECT) || 'null'); } catch { /* choose again */ }
324
+ let prior = null;
325
+ try { prior = JSON.parse(storage.getItem(KEYS.ONBOARDING_PROJECT) || 'null'); } catch { /* choose again */ }
552
326
  const existing = projects.findIndex(p => p.path === prior?.path);
553
- select.value = existing >= 0 ? String(existing) : '';
554
- select.disabled = !projects.length; nextButton.disabled = existing < 0;
327
+ setDropdownItems(input, [{ value: '', label: tr('chooseProject') }, ...projects.map((p, i) => ({ value: String(i), label: name(p) }))]);
328
+ setDropdownValue(input, existing >= 0 ? String(existing) : '');
329
+ setDropdownDisabled(input, !projects.length);
330
+ primary.disabled = existing < 0;
555
331
  loading.textContent = projects.length ? tr('projectHint') : tr('noProjects');
556
- select.addEventListener('change', () => {
557
- const project = projects[Number(select.value)];
558
- nextButton.disabled = select.value === '';
559
- if (select.value !== '' && project) saveChoice(KEYS.ONBOARDING_PROJECT, JSON.stringify({ path: project.path, label: name(project) }));
332
+ input.addEventListener('change', () => {
333
+ const project = projects[Number(input.value)];
334
+ primary.disabled = input.value === '';
335
+ if (input.value !== '' && project) saveChoice(KEYS.ONBOARDING_PROJECT, JSON.stringify({ path: project.path, label: name(project) }));
560
336
  });
561
337
  }).catch(() => {
562
338
  if (!fresh()) return;
563
- loading.textContent = tr('projectsUnavailable'); retry();
564
- }).finally(() => { clearTimeout(timeout); timers.delete(timeout); });
565
- content.append(button(tr('addProject', names()), async () => {
566
- // Explicit user action hands off to the existing Projects settings page.
567
- destroyTutorial(); const { openSettingsModal } = await import('./ui-settings.js');
339
+ loading.textContent = tr('projectsUnavailable');
340
+ retry();
341
+ }).finally(() => clearTimeout(timeout));
342
+ extra.append(button(tr('addProject', names()), async () => {
343
+ // An explicit choice hands off to the Projects page of Settings.
344
+ closeSetup();
345
+ const { openSettingsModal } = await import('./ui-settings.js');
568
346
  await openSettingsModal({ tab: 'projects', expand: ['stg-sec-workspaces'] });
569
- }));
347
+ }, 'tour-setup-btn tour-setup-link'));
570
348
  } else if (step.kind === 'launch') {
571
- nextButton.textContent = tr('begin');
349
+ primary.textContent = tr('begin');
572
350
  // A second click meant for the previous screen must not start the CLI.
573
- nextButton.disabled = true; later(() => { nextButton.disabled = false; }, 600);
351
+ primary.disabled = true;
352
+ const unlock = setTimeout(() => { if (fresh()) primary.disabled = false; }, 600);
574
353
  const loading = status('');
575
354
  let busy = false;
576
- onPrimary = () => {
355
+ setup.onPrimary = () => {
577
356
  if (busy || !fresh()) return;
578
- let project = null; try { project = JSON.parse(storage.getItem(KEYS.ONBOARDING_PROJECT) || 'null'); } catch { /* handled below */ }
357
+ let project = null;
358
+ try { project = JSON.parse(storage.getItem(KEYS.ONBOARDING_PROJECT) || 'null'); } catch { /* handled below */ }
579
359
  const cli = storage.getItem(KEYS.ONBOARDING_CLI);
580
360
  if (!project?.path || !CLI_PROFILES.some(c => c.id === cli)) { loading.textContent = tr('selectionMissing'); return; }
581
- busy = true; nextButton.disabled = true; loading.textContent = tr('launching');
361
+ busy = true;
362
+ primary.disabled = true;
363
+ loading.textContent = tr('launching');
582
364
  // The same category slug Settings → Projects gives an exploration.
583
365
  const slug = (project.label || project.path.split(/[\\/]/).filter(Boolean).pop() || 'project').toLowerCase().replace(/[^a-z0-9]+/g, '');
584
366
  const model = storage.getItem(KEYS.ONBOARDING_MODEL);
585
367
  const finish = (err) => {
368
+ clearTimeout(timeout);
586
369
  if (!fresh()) return;
587
- clearTimeout(timeout); timers.delete(timeout);
588
- if (err) { busy = false; nextButton.disabled = false; loading.textContent = tr('launchFailed'); }
589
- else { firstRun = false; go(WELCOME); } // setup is done: Back from the welcome does not return to it
370
+ if (err) { busy = false; primary.disabled = false; loading.textContent = tr('launchFailed'); return; }
371
+ closeSetup();
590
372
  };
591
- const timeout = later(() => finish(new Error('timeout')), 15000);
373
+ const timeout = setTimeout(() => finish(new Error('timeout')), 15000);
592
374
  try {
593
375
  emit('terminal:launch-floating', { profile: cli, cwd: project.path, createOpts: model ? { model } : {},
594
376
  initialMessage: buildExplorePrompt(slug), autoSubmit: true, onDone: finish });
595
377
  } catch (err) { finish(err); }
596
378
  };
379
+ void unlock;
597
380
  }
381
+ // The keyboard lands on the way on (never on Begin: only a deliberate click launches).
382
+ const focus = step.kind === 'launch' ? dialog : (primary.disabled ? dialog : primary);
383
+ focus.focus({ preventScroll: true });
598
384
  }