synabun 2.0.0-beta → 2.0.5

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 (374) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/THIRD-PARTY-LICENSES.md +51 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +2 -2
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +20 -0
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/launcher.mjs +40 -0
  14. package/lib/claude-hooks.js +61 -8
  15. package/lib/client-config-repair.js +4 -3
  16. package/lib/data-home-migration.js +4 -0
  17. package/lib/desktop-pwa.js +206 -0
  18. package/lib/external-tools.js +221 -0
  19. package/lib/first-launch-protection.js +5 -0
  20. package/lib/packaged-runtime.js +110 -0
  21. package/lib/start-launcher.js +1396 -0
  22. package/mcp-server/package-lock.json +2 -2
  23. package/mcp-server/package.json +1 -3
  24. package/neural-interface/i18n/en.json +1540 -154
  25. package/neural-interface/i18n/pt-BR.json +1538 -153
  26. package/neural-interface/lib/assistant-api.js +62 -6
  27. package/neural-interface/lib/assistant-brains/claude.js +4 -0
  28. package/neural-interface/lib/assistant-catalog.js +179 -27
  29. package/neural-interface/lib/assistant-config.js +31 -2
  30. package/neural-interface/lib/assistant-dispatch.js +62 -13
  31. package/neural-interface/lib/assistant-envelope.js +9 -6
  32. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  33. package/neural-interface/lib/assistant-memory.js +24 -10
  34. package/neural-interface/lib/assistant-persona.js +6 -2
  35. package/neural-interface/lib/assistant-pricing.js +21 -6
  36. package/neural-interface/lib/assistant-runtime.js +181 -12
  37. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  38. package/neural-interface/lib/assistant-test-density.js +142 -0
  39. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  40. package/neural-interface/lib/assistant-user-context.js +908 -0
  41. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  42. package/neural-interface/lib/assistant-user-learn.js +404 -0
  43. package/neural-interface/lib/assistant-user-threads.js +226 -0
  44. package/neural-interface/lib/assistant-user-writer.js +181 -0
  45. package/neural-interface/lib/augmented-path.js +23 -3
  46. package/neural-interface/lib/browser-background-tab.js +134 -0
  47. package/neural-interface/lib/browser-language.js +83 -0
  48. package/neural-interface/lib/browser-screenshot.js +678 -61
  49. package/neural-interface/lib/claude-agent-bridge.js +87 -153
  50. package/neural-interface/lib/claude-executable.js +241 -0
  51. package/neural-interface/lib/claude-model-catalog.js +82 -93
  52. package/neural-interface/lib/claude-temporary.js +46 -7
  53. package/neural-interface/lib/cloudflared/install.js +533 -0
  54. package/neural-interface/lib/cloudflared/manager.js +545 -0
  55. package/neural-interface/lib/cloudflared/pins.js +115 -0
  56. package/neural-interface/lib/cloudflared/pins.json +63 -0
  57. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  58. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  59. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  60. package/neural-interface/lib/editor-completion.js +714 -0
  61. package/neural-interface/lib/editor-config.js +149 -0
  62. package/neural-interface/lib/editor-files-api.js +268 -0
  63. package/neural-interface/lib/editor-git-api.js +134 -0
  64. package/neural-interface/lib/file-explorer-api.js +1039 -0
  65. package/neural-interface/lib/file-explorer-system.js +355 -0
  66. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  67. package/neural-interface/lib/file-explorer-watch.js +239 -0
  68. package/neural-interface/lib/git-diff-api.js +103 -0
  69. package/neural-interface/lib/git-read.js +202 -0
  70. package/neural-interface/lib/greeting/api.js +89 -0
  71. package/neural-interface/lib/greeting/budgets.js +90 -0
  72. package/neural-interface/lib/greeting/engine.js +461 -0
  73. package/neural-interface/lib/greeting/facts.js +320 -0
  74. package/neural-interface/lib/greeting/sources.js +252 -0
  75. package/neural-interface/lib/greeting/writer.js +139 -0
  76. package/neural-interface/lib/http-guards.js +189 -13
  77. package/neural-interface/lib/machine-names.js +88 -0
  78. package/neural-interface/lib/memory-api.js +31 -7
  79. package/neural-interface/lib/native-binary-runtime.js +69 -337
  80. package/neural-interface/lib/native-loop-providers.js +40 -49
  81. package/neural-interface/lib/offline-page.js +54 -0
  82. package/neural-interface/lib/plugin-installer.js +4 -2
  83. package/neural-interface/lib/project-storage.js +5 -7
  84. package/neural-interface/lib/request-gate.js +589 -0
  85. package/neural-interface/lib/session-title-generator.js +5 -0
  86. package/neural-interface/lib/synabun-update-plan.js +19 -0
  87. package/neural-interface/lib/ui-blob-api.js +108 -0
  88. package/neural-interface/lib/ui-blob-store.js +68 -0
  89. package/neural-interface/package-lock.json +701 -253
  90. package/neural-interface/package.json +13 -2
  91. package/neural-interface/public/install-app.html +34 -0
  92. package/neural-interface/public/manifest.json +2 -0
  93. package/neural-interface/public/offline.html +402 -66
  94. package/neural-interface/public/onboarding.html +264 -147
  95. package/neural-interface/public/shared/agent-runtime-options.js +22 -6
  96. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  97. package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
  98. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  99. package/neural-interface/public/shared/assistant/asst-mascot.js +26 -64
  100. package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
  101. package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
  102. package/neural-interface/public/shared/assistant/asst-rack-styles.js +0 -3
  103. package/neural-interface/public/shared/assistant/asst-render.js +219 -67
  104. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  105. package/neural-interface/public/shared/assistant/asst-route.js +23 -329
  106. package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
  107. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  108. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  109. package/neural-interface/public/shared/assistant/asst-state.js +185 -1
  110. package/neural-interface/public/shared/assistant/asst-styles.js +162 -6
  111. package/neural-interface/public/shared/blob-store.js +111 -22
  112. package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
  113. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  114. package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
  115. package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
  116. package/neural-interface/public/shared/command-runner-model.js +30 -0
  117. package/neural-interface/public/shared/constants.js +6 -1
  118. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  119. package/neural-interface/public/shared/cp/cp-styles.js +15 -0
  120. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  121. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  122. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  123. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  124. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  125. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  126. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  127. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  128. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  129. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  130. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  131. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  132. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  133. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  134. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  135. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  136. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  137. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  138. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  139. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  140. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  141. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  142. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  143. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  144. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  145. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  146. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  147. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  148. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  149. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  150. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  151. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  152. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  153. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  154. package/neural-interface/public/shared/file-links.js +339 -0
  155. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  156. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  157. package/neural-interface/public/shared/html-shell.js +169 -170
  158. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  159. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  160. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
  161. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  162. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
  163. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
  164. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  165. package/neural-interface/public/shared/pwa-install-page.js +62 -0
  166. package/neural-interface/public/shared/pwa-install.css +12 -0
  167. package/neural-interface/public/shared/pwa-install.js +127 -0
  168. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  169. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  170. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  171. package/neural-interface/public/shared/settings/settings-ia.js +18 -4
  172. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  173. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  174. package/neural-interface/public/shared/start-bridge.js +802 -0
  175. package/neural-interface/public/shared/styleguide/sg-colors.js +5 -2
  176. package/neural-interface/public/shared/styleguide/sg-import-export.js +4 -3
  177. package/neural-interface/public/shared/styleguide/sg-kit.js +4 -1
  178. package/neural-interface/public/shared/styleguide/sg-panel.js +9 -1
  179. package/neural-interface/public/shared/styleguide/sg-typography.js +1 -0
  180. package/neural-interface/public/shared/styles.css +2219 -1117
  181. package/neural-interface/public/shared/synabun-mascot.js +20 -56
  182. package/neural-interface/public/shared/term-ready.js +307 -0
  183. package/neural-interface/public/shared/tunnel-view.js +167 -0
  184. package/neural-interface/public/shared/ui-claude-panel.js +160 -19
  185. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  186. package/neural-interface/public/shared/ui-detail.js +15 -0
  187. package/neural-interface/public/shared/ui-explorer.js +22 -0
  188. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  189. package/neural-interface/public/shared/ui-file-links.js +103 -0
  190. package/neural-interface/public/shared/ui-help.js +26 -0
  191. package/neural-interface/public/shared/ui-invite.js +126 -116
  192. package/neural-interface/public/shared/ui-judgments.js +1 -1
  193. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  194. package/neural-interface/public/shared/ui-loading.js +367 -12
  195. package/neural-interface/public/shared/ui-menubar.js +21 -55
  196. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  197. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  198. package/neural-interface/public/shared/ui-navbar.js +45 -21
  199. package/neural-interface/public/shared/ui-settings.js +491 -547
  200. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  201. package/neural-interface/public/shared/ui-sync.js +16 -0
  202. package/neural-interface/public/shared/ui-terminal.js +153 -149
  203. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  204. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  205. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  206. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  207. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  208. package/neural-interface/public/shared/worker-end.js +251 -0
  209. package/neural-interface/public/sw.js +3 -1
  210. package/neural-interface/public/variant/2d/main.js +2 -1
  211. package/neural-interface/public/variant/3d/main.js +2 -1
  212. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  213. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  214. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  215. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  216. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  217. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  218. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  219. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  220. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  221. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  222. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  223. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  224. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  225. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  226. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  227. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  228. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  229. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  230. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  231. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  232. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  233. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  234. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  235. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  236. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  237. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  238. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  239. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  240. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  241. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  242. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  243. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  244. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  245. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  246. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  247. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  248. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  249. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  250. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  251. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  252. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  253. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  254. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  255. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  256. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  257. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  258. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  259. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  260. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  261. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  262. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  263. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  264. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  265. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  266. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  267. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  268. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  269. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  270. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  271. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  272. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  273. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  274. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  275. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  276. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  277. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  278. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  279. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  280. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  281. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  282. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  283. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  284. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  285. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  286. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  287. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  288. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  289. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  290. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  291. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  292. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  293. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  294. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  295. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  296. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  297. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  298. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  299. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  300. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  301. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  302. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  303. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  304. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  305. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  306. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  307. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  308. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  309. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  310. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  311. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  312. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  313. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  314. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  315. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  316. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  317. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  318. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  319. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  320. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  321. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  322. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  323. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  324. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  325. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  326. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  327. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  328. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  329. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  330. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  331. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  332. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  333. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  334. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  335. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  336. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  337. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  338. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  339. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  340. package/neural-interface/scripts/build-vendor.mjs +78 -2
  341. package/neural-interface/scripts/rebuild-pty.js +15 -12
  342. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  343. package/neural-interface/server.js +1092 -1212
  344. package/neural-interface/templates/rulesets/README.md +1 -1
  345. package/package.json +13 -7
  346. package/postinstall.js +11 -81
  347. package/setup.js +111 -6
  348. package/benchmarks/browser-live-results.json +0 -79
  349. package/benchmarks/browser-v2-results.json +0 -258
  350. package/docs/browser-v2.md +0 -109
  351. package/docs/hook-integrity.md +0 -89
  352. package/docs/judgments.md +0 -182
  353. package/docs/style-guide.md +0 -246
  354. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  355. package/neural-interface/public/category-logos/claude.svg +0 -1
  356. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  357. package/neural-interface/public/category-logos/openai.svg +0 -1
  358. package/neural-interface/public/category-logos/synabun.png +0 -0
  359. package/neural-interface/public/claude.svg +0 -1
  360. package/neural-interface/public/gallery.svg +0 -6
  361. package/neural-interface/public/leonardoai.svg +0 -1
  362. package/neural-interface/public/mcp.svg +0 -4
  363. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  364. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  365. package/neural-interface/public/sessionicon.svg +0 -8
  366. package/neural-interface/public/toolbar-album.svg +0 -8
  367. package/neural-interface/public/toolbar-question.svg +0 -6
  368. package/neural-interface/public/toolbar-sessions.svg +0 -6
  369. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  370. package/scripts/audit-claude-hook-usage.mjs +0 -12
  371. package/scripts/audit-memory-duplicates.mjs +0 -44
  372. package/scripts/measure-claude-hook-overhead.mjs +0 -77
  373. package/scripts/recover-user-config.mjs +0 -254
  374. package/skills/synabun/synabunicon.png +0 -0
@@ -0,0 +1,490 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — File Explorer: what opens over the page
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The context menu and the preview card (flyouts docked to the right of the
6
+ // panel, an arrow pointing at their row), the questions (confirm dialogs), and
7
+ // the line at the foot of the panel that says what happened.
8
+ // While one is open its keys are taken at the window, before anything in the
9
+ // page sees them: the whiteboard listens at the document in the capture phase
10
+ // and takes Escape, Delete and letters whenever the keyboard is not in a text
11
+ // field. Taken, not cancelled: a key reaches nothing in the page under the
12
+ // popup, and only the keys the popup itself acts on lose what the browser
13
+ // would do with them. Reload (F5, Ctrl+R) and the like stay the browser's.
14
+ // One flyout is open at a time; closed with the keyboard, it gives the
15
+ // keyboard back to where it was. One question is open at a time too: a second
16
+ // one answers the first with "given up" before it takes its place.
17
+ // No imports: the caller hands in the words.
18
+
19
+ const KEY_EVENTS = ['keydown', 'keyup', 'keypress'];
20
+ const stroke = (d) => `<svg viewBox="0 0 24 24" aria-hidden="true">${d}</svg>`;
21
+
22
+ /** The glyph of a menu item, by its id (24 × 24, drawn with the row's colour). */
23
+ export const MENU_ICONS = {
24
+ open: stroke('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>'),
25
+ openKeep: stroke('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><polyline points="9 15 11 17 15 13"/>'),
26
+ openAsText: stroke('<path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/>'),
27
+ preview: stroke('<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/>'),
28
+ newFile: stroke('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="12" x2="12" y2="18"/><line x1="9" y1="15" x2="15" y2="15"/>'),
29
+ newFolder: stroke('<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/><line x1="12" y1="11" x2="12" y2="17"/><line x1="9" y1="14" x2="15" y2="14"/>'),
30
+ send: stroke('<path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z"/><path d="M18 14l.9 2.1L21 17l-2.1.9L18 20l-.9-2.1L15 17l2.1-.9L18 14z"/>'),
31
+ sendTo: stroke('<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>'),
32
+ cut: stroke('<circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><line x1="20" y1="4" x2="8.12" y2="15.88"/><line x1="14.47" y1="14.48" x2="20" y2="20"/><line x1="8.12" y1="8.12" x2="12" y2="12"/>'),
33
+ copy: stroke('<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>'),
34
+ paste: stroke('<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1"/>'),
35
+ duplicate: stroke('<rect x="8" y="8" width="13" height="13" rx="2"/><path d="M4 16V5a2 2 0 0 1 2-2h11"/><line x1="14.5" y1="11.5" x2="14.5" y2="17.5"/><line x1="11.5" y1="14.5" x2="17.5" y2="14.5"/>'),
36
+ copyPath: stroke('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>'),
37
+ copyRelativePath: stroke('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>'),
38
+ copyName: stroke('<path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M12 4v16"/>'),
39
+ rename: stroke('<path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/>'),
40
+ delete: stroke('<polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>'),
41
+ reveal: stroke('<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>'),
42
+ color: stroke('<circle cx="13.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="10.5" r="2.5"/><circle cx="8.5" cy="7.5" r="2.5"/><circle cx="6.5" cy="12.5" r="2.5"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.93 0 1.5-.67 1.5-1.5 0-.39-.14-.74-.39-1.04-.24-.28-.37-.59-.37-.96 0-.83.67-1.5 1.5-1.5H16c3.31 0 6-2.69 6-6 0-5.5-4.5-10-10-10z"/>'),
43
+ expand: stroke('<polyline points="9 6 15 12 9 18"/>'),
44
+ collapse: stroke('<polyline points="6 9 12 15 18 9"/>'),
45
+ collapseChildren: stroke('<path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="M14 10l7-7"/><path d="M3 21l7-7"/>'),
46
+ collapseAll: stroke('<path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="M14 10l7-7"/><path d="M3 21l7-7"/>'),
47
+ expandAll: stroke('<path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/>'),
48
+ refresh: stroke('<polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>'),
49
+ changedOnly: stroke('<circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M6 21V9a9 9 0 0 0 9 9"/>'),
50
+ sortBy: stroke('<path d="M3 6h7M3 12h5M3 18h3M16 4v16M12 16l4 4 4-4"/>'),
51
+ back: stroke('<polyline points="15 6 9 12 15 18"/>'),
52
+ check: stroke('<polyline points="20 6 9 17 4 12"/>'),
53
+ };
54
+
55
+ let _flyout = null; // the open flyout: { close }
56
+ let _dialog = null; // the open question: { giveUp, before }
57
+ const MENU_KEYS = new Set(['Tab', 'Escape', 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' ']);
58
+ const CARD_KEYS = new Set(['Tab', 'Escape', 'ArrowLeft', 'ArrowRight']);
59
+ const plain = (event) => !event.ctrlKey && !event.metaKey && !event.altKey;
60
+
61
+ /** Close the menu or the preview that is open, if one is. */
62
+ export function closeFlyout(o) { _flyout?.close(o); }
63
+ export const isFlyoutOpen = () => !!_flyout;
64
+
65
+ // Put a flyout to the right of the panel with its arrow at `y`; over the panel when the window has no room there.
66
+ function dock(flyout, arrow, { panel, y }) {
67
+ const frame = panel.getBoundingClientRect();
68
+ const size = flyout.getBoundingClientRect();
69
+ const fits = frame.right + 6 + size.width <= window.innerWidth - 8;
70
+ const left = fits ? frame.right + 6 : Math.max(8, window.innerWidth - size.width - 8);
71
+ const top = Math.max(8, Math.min(window.innerHeight - size.height - 8, y - size.height / 2));
72
+ flyout.style.left = `${Math.round(left)}px`;
73
+ flyout.style.top = `${Math.round(top)}px`;
74
+ flyout.classList.toggle('fe-ctx-flyout--over', !fits);
75
+ if (arrow) arrow.style.top = `${Math.round(Math.max(12, Math.min(size.height - 12, y - top)))}px`;
76
+ }
77
+
78
+ // What every flyout shares: one at a time, keys at the window, gone on a press outside, the keyboard handed back.
79
+ // `owns(event)` says whether a key is one the flyout acts on: only those are cancelled (on the way down, where
80
+ // `onKey` acts, and on the way up, where a button would otherwise take a Space as a click).
81
+ function mount(flyout, { row, onKey, owns, onClose }) {
82
+ closeFlyout();
83
+ const before = document.activeElement;
84
+ let closed = false;
85
+ const keys = (event) => {
86
+ event.stopPropagation();
87
+ if (!owns(event)) return;
88
+ if (event.type === 'keydown') onKey(event);
89
+ else event.preventDefault();
90
+ };
91
+ const outside = (event) => { if (!flyout.contains(event.target)) close({ refocus: false }); };
92
+ const gone = () => close({ refocus: false });
93
+ function close({ refocus = true } = {}) {
94
+ if (closed) return;
95
+ closed = true;
96
+ if (_flyout === handle) _flyout = null;
97
+ for (const type of KEY_EVENTS) window.removeEventListener(type, keys, true);
98
+ document.removeEventListener('pointerdown', outside, true);
99
+ window.removeEventListener('resize', gone);
100
+ window.removeEventListener('blur', gone);
101
+ row?.classList.remove('fe-row--ctx-active');
102
+ flyout.classList.remove('fe-ctx-flyout--in');
103
+ flyout.classList.add('fe-ctx-flyout--out');
104
+ setTimeout(() => flyout.remove(), 150);
105
+ // The keyboard goes back to where it was, unless what was chosen took it somewhere (a name being typed, a question).
106
+ const held = flyout.contains(document.activeElement) || document.activeElement === document.body || !document.activeElement;
107
+ if (refocus && held && before?.isConnected) before.focus({ preventScroll: true });
108
+ onClose?.();
109
+ }
110
+ const handle = { close, element: flyout };
111
+ _flyout = handle;
112
+ document.body.appendChild(flyout);
113
+ row?.classList.add('fe-row--ctx-active');
114
+ for (const type of KEY_EVENTS) window.addEventListener(type, keys, true);
115
+ // Not the press that opened it.
116
+ setTimeout(() => { if (!closed) document.addEventListener('pointerdown', outside, true); }, 0);
117
+ window.addEventListener('resize', gone);
118
+ window.addEventListener('blur', gone);
119
+ requestAnimationFrame(() => flyout.classList.add('fe-ctx-flyout--in'));
120
+ return handle;
121
+ }
122
+
123
+ /**
124
+ * @typedef {object} MenuItem
125
+ * @property {string} [id] @property {string} [label] @property {string} [keyLabel] its shortcut, as it is shown
126
+ * @property {boolean} [danger] @property {boolean} [disabled] @property {string} [title] why it is disabled
127
+ * @property {boolean} [checked] @property {boolean} [more] it opens another page of the menu
128
+ * @property {boolean} [back] the row that leads back to the page before
129
+ * @property {string} [swatch] a colour: drawn as a round swatch, in a row with the others
130
+ * @property {boolean} [separator]
131
+ */
132
+
133
+ /**
134
+ * Open the context menu.
135
+ * @param {object} o
136
+ * @param {HTMLElement} o.panel the explorer panel (the menu docks to its right edge)
137
+ * @param {HTMLElement | null} o.row the row it belongs to, or null for the empty space
138
+ * @param {number} [o.y] where the arrow points when there is no row
139
+ * @param {string} o.label its accessible name
140
+ * @param {MenuItem[]} o.items
141
+ * @param {boolean} [o.keyboard] opened with the keyboard: the first item has it from the start
142
+ * @param {(item: MenuItem) => (MenuItem[] | void)} o.onSelect what an item does; a list it returns is shown as the
143
+ * menu's next page
144
+ * @param {() => void} [o.onClose]
145
+ */
146
+ export function openContextMenu({ panel, row, y = null, label, items, keyboard = false, onSelect, onClose }) {
147
+ const flyout = document.createElement('div');
148
+ flyout.className = 'fe-ctx-flyout';
149
+ const arrow = document.createElement('div');
150
+ arrow.className = 'fe-ctx-arrow';
151
+ const menu = document.createElement('div');
152
+ menu.className = 'fe-ctx-menu';
153
+ menu.setAttribute('role', 'menu');
154
+ menu.setAttribute('aria-label', label);
155
+ menu.tabIndex = -1;
156
+ flyout.append(arrow, menu);
157
+ const at = row ? (() => { const box = row.getBoundingClientRect(); return box.top + box.height / 2; })() : y ?? window.innerHeight / 2;
158
+ const pages = []; // the pages left behind, for "back"
159
+ let shown = [];
160
+ let typed = '';
161
+ let typedAt = 0;
162
+
163
+ const stops = () => [...menu.querySelectorAll('[role^="menuitem"]:not([aria-disabled="true"])')];
164
+ const focusAt = (index) => { const list = stops(); if (list.length) list[(index + list.length) % list.length].focus({ preventScroll: true }); };
165
+
166
+ function draw(list, { focusFirst = false } = {}) {
167
+ shown = list;
168
+ menu.textContent = '';
169
+ let swatches = null;
170
+ for (const item of list) {
171
+ if (item.separator) {
172
+ swatches = null;
173
+ const line = document.createElement('div');
174
+ line.className = 'fe-ctx-separator';
175
+ line.setAttribute('role', 'separator');
176
+ menu.append(line);
177
+ continue;
178
+ }
179
+ const button = document.createElement('button');
180
+ button.type = 'button';
181
+ button.tabIndex = -1;
182
+ button.dataset.id = item.id;
183
+ if (item.swatch) {
184
+ if (!swatches) { swatches = document.createElement('div'); swatches.className = 'fe-ctx-swatches'; swatches.setAttribute('role', 'group'); menu.append(swatches); }
185
+ button.className = `fe-color-swatch${item.checked ? ' fe-color-swatch--active' : ''}`;
186
+ button.style.background = item.swatch;
187
+ button.setAttribute('role', 'menuitemradio');
188
+ button.setAttribute('aria-checked', String(!!item.checked));
189
+ button.setAttribute('aria-label', item.label);
190
+ button.dataset.tooltip = item.label;
191
+ swatches.append(button);
192
+ continue;
193
+ }
194
+ swatches = null;
195
+ button.className = `fe-ctx-item${item.danger ? ' fe-ctx-item--danger' : ''}`;
196
+ const checkable = item.checked !== undefined;
197
+ button.setAttribute('role', checkable ? 'menuitemcheckbox' : 'menuitem');
198
+ if (checkable) button.setAttribute('aria-checked', String(!!item.checked));
199
+ if (item.more) button.setAttribute('aria-haspopup', 'menu');
200
+ if (item.disabled) { button.setAttribute('aria-disabled', 'true'); if (item.title) button.title = item.title; }
201
+ const icon = document.createElement('span');
202
+ icon.className = 'fe-ctx-icon';
203
+ icon.innerHTML = (checkable ? (item.checked ? MENU_ICONS.check : '') : MENU_ICONS[item.back ? 'back' : item.icon || item.id]) || '';
204
+ const text = document.createElement('span');
205
+ text.className = 'fe-ctx-label';
206
+ text.textContent = item.label;
207
+ button.append(icon, text);
208
+ if (item.keyLabel || item.more) {
209
+ const key = document.createElement('span');
210
+ key.className = 'fe-ctx-key';
211
+ key.textContent = item.more ? '›' : item.keyLabel;
212
+ key.setAttribute('aria-hidden', 'true');
213
+ if (item.keyLabel && !item.more) button.setAttribute('aria-keyshortcuts', item.keyLabel);
214
+ button.append(key);
215
+ }
216
+ menu.append(button);
217
+ }
218
+ dock(flyout, arrow, { panel, y: at });
219
+ if (focusFirst) focusAt(0); else menu.focus({ preventScroll: true });
220
+ }
221
+
222
+ function choose(item) {
223
+ if (!item || item.disabled) return;
224
+ if (item.back) { const page = pages.pop(); if (page) draw(page, { focusFirst: true }); return; }
225
+ const next = onSelect(item);
226
+ if (Array.isArray(next)) { pages.push(shown); draw(next, { focusFirst: true }); return; }
227
+ handle.close();
228
+ }
229
+ const itemOf = (node) => shown.find((item) => !item.separator && item.id === node?.dataset.id);
230
+
231
+ menu.addEventListener('click', (event) => choose(itemOf(event.target.closest('[data-id]'))));
232
+ // The row under the pointer is the one the keyboard is on: one highlight, not two.
233
+ menu.addEventListener('pointermove', (event) => {
234
+ const node = event.target.closest('[role^="menuitem"]:not([aria-disabled="true"])');
235
+ if (node && node !== document.activeElement) node.focus({ preventScroll: true });
236
+ });
237
+
238
+ const handle = mount(flyout, {
239
+ row,
240
+ onClose,
241
+ // The menu's own keys: moving, choosing, closing, and a letter typed for an item. With Ctrl, Cmd or Alt held a
242
+ // key is the browser's (it still reaches nothing in the page).
243
+ owns: (event) => !event.ctrlKey && !event.metaKey && !event.altKey && (MENU_KEYS.has(event.key) || event.key.length === 1),
244
+ onKey(event) {
245
+ const list = stops();
246
+ const index = list.indexOf(document.activeElement);
247
+ const { key } = event;
248
+ event.preventDefault();
249
+ if (key === 'Tab') { handle.close(); return; }
250
+ if (key === 'Escape') { handle.close(); return; }
251
+ if (key === 'ArrowDown') { focusAt(index + 1); return; }
252
+ if (key === 'ArrowUp') { focusAt(index < 0 ? list.length - 1 : index - 1); return; }
253
+ if (key === 'Home') { focusAt(0); return; }
254
+ if (key === 'End') { focusAt(list.length - 1); return; }
255
+ if (key === 'ArrowLeft') { if (pages.length) choose(shown.find((item) => item.back)); return; }
256
+ if (key === 'ArrowRight') { const item = itemOf(document.activeElement); if (item?.more) choose(item); return; }
257
+ if (key === 'Enter' || key === ' ') { choose(itemOf(document.activeElement)); return; }
258
+ if (key.length === 1) {
259
+ // A letter goes to the next item that starts with what was typed.
260
+ const now = Date.now();
261
+ typed = now - typedAt > 600 ? key.toLowerCase() : typed + key.toLowerCase();
262
+ typedAt = now;
263
+ const from = typed.length > 1 ? Math.max(0, index) : index + 1;
264
+ for (let i = 0; i < list.length; i++) {
265
+ const node = list[(from + i) % list.length];
266
+ if ((itemOf(node)?.label || '').toLowerCase().startsWith(typed)) { node.focus({ preventScroll: true }); break; }
267
+ }
268
+ }
269
+ },
270
+ });
271
+ draw(items, { focusFirst: keyboard });
272
+ return handle;
273
+ }
274
+
275
+ /**
276
+ * Open the card of a file that is not text: its picture when it is one, what it is, and what can be done with it.
277
+ * @param {object} o
278
+ * @param {HTMLElement} o.panel @param {HTMLElement | null} o.row @param {string} o.label its accessible name
279
+ * @param {string} o.name @param {string} o.detail what it is and how large
280
+ * @param {string | null} [o.imageUrl] @param {string} [o.imageAlt] @param {string} [o.imageFailed] said when the picture does not load
281
+ * @param {{ label: string, run: () => void, keep?: boolean }[]} o.actions `keep`: the card stays open after it
282
+ * @param {() => void} [o.onClose]
283
+ */
284
+ export function openPreviewCard({ panel, row, label, name, detail, imageUrl = null, imageAlt = '', imageFailed = '', actions, onClose }) {
285
+ const flyout = document.createElement('div');
286
+ flyout.className = 'fe-ctx-flyout fe-preview';
287
+ const arrow = document.createElement('div');
288
+ arrow.className = 'fe-ctx-arrow';
289
+ const card = document.createElement('div');
290
+ card.className = 'fe-ctx-menu fe-preview-card';
291
+ card.setAttribute('role', 'dialog');
292
+ card.setAttribute('aria-label', label);
293
+ card.tabIndex = -1;
294
+ const at = row ? (() => { const box = row.getBoundingClientRect(); return box.top + box.height / 2; })() : window.innerHeight / 2;
295
+ const title = document.createElement('div');
296
+ title.className = 'fe-preview-name';
297
+ title.textContent = name;
298
+ title.title = name;
299
+ const meta = document.createElement('div');
300
+ meta.className = 'fe-preview-detail';
301
+ meta.textContent = detail;
302
+ card.append(title);
303
+ if (imageUrl) {
304
+ const frame = document.createElement('div');
305
+ frame.className = 'fe-preview-frame';
306
+ const image = document.createElement('img');
307
+ image.alt = imageAlt;
308
+ image.decoding = 'async';
309
+ image.addEventListener('load', () => {
310
+ if (image.naturalWidth) meta.textContent = `${detail} · ${image.naturalWidth} × ${image.naturalHeight}`;
311
+ dock(flyout, arrow, { panel, y: at });
312
+ });
313
+ image.addEventListener('error', () => { frame.textContent = imageFailed; frame.classList.add('fe-preview-frame--failed'); dock(flyout, arrow, { panel, y: at }); });
314
+ image.src = imageUrl;
315
+ frame.append(image);
316
+ card.append(frame);
317
+ }
318
+ card.append(meta);
319
+ const bar = document.createElement('div');
320
+ bar.className = 'fe-preview-actions';
321
+ for (const action of actions) {
322
+ const button = document.createElement('button');
323
+ button.type = 'button';
324
+ button.className = 'fe-modal-btn fe-modal-cancel';
325
+ button.textContent = action.label;
326
+ button.addEventListener('click', () => { if (!action.keep) handle.close(); action.run(); });
327
+ bar.append(button);
328
+ }
329
+ card.append(bar);
330
+ flyout.append(arrow, card);
331
+ const buttons = () => [...bar.querySelectorAll('button')];
332
+ // Whatever makes the card another size (the picture arriving, a long name wrapping), it is put inside the
333
+ // window again, with the margin the menu keeps.
334
+ const sized = typeof ResizeObserver === 'function' ? new ResizeObserver(() => dock(flyout, arrow, { panel, y: at })) : null;
335
+ const handle = mount(flyout, {
336
+ row,
337
+ onClose() { sized?.disconnect(); onClose?.(); },
338
+ // Enter and Space are the focused button's own; with no button focused they would act on nothing.
339
+ owns: (event) => plain(event) && (CARD_KEYS.has(event.key) || ((event.key === 'Enter' || event.key === ' ') && !buttons().includes(document.activeElement))),
340
+ onKey(event) {
341
+ const { key } = event;
342
+ event.preventDefault();
343
+ if (key === 'Escape') { handle.close(); return; }
344
+ if (key === 'Enter' || key === ' ') return;
345
+ const list = buttons();
346
+ const index = list.indexOf(document.activeElement);
347
+ const step = key === 'ArrowLeft' || (key === 'Tab' && event.shiftKey) ? -1 : 1;
348
+ list[(index + step + list.length) % list.length]?.focus();
349
+ },
350
+ });
351
+ dock(flyout, arrow, { panel, y: at });
352
+ sized?.observe(card);
353
+ buttons()[0]?.focus({ preventScroll: true });
354
+ return handle;
355
+ }
356
+
357
+ /**
358
+ * Ask before something that cannot simply be taken back.
359
+ * @param {object} o
360
+ * @param {string} o.title @param {string[]} o.lines what will happen, a sentence per line
361
+ * @param {string} o.confirmLabel @param {string} o.cancelLabel
362
+ * @param {string} [o.altLabel] a third answer between the two
363
+ * @param {boolean} [o.danger] the confirm button removes something: it is not where the keyboard starts
364
+ * @returns {Promise<'confirm' | 'alt' | null>} null: given up (also when another question took its place)
365
+ */
366
+ export function confirmDialog({ title, lines, confirmLabel, cancelLabel, altLabel = '', danger = false }) {
367
+ return new Promise((resolve) => {
368
+ closeFlyout({ refocus: false });
369
+ // A question that is still open is given up through its own way out: its listeners go and its caller hears
370
+ // "no". The keyboard goes back, in the end, to where it was before the first of them.
371
+ const earlier = _dialog;
372
+ earlier?.giveUp();
373
+ // One that was answered a moment ago may still be fading out: it goes at once (two would share an id).
374
+ document.getElementById('fe-dialog')?.remove();
375
+ const before = earlier?.before || document.activeElement;
376
+ const overlay = document.createElement('div');
377
+ overlay.id = 'fe-dialog';
378
+ overlay.className = 'fe-modal-overlay';
379
+ const modal = document.createElement('div');
380
+ modal.className = `fe-modal${danger ? '' : ' fe-modal--plain'}`;
381
+ modal.setAttribute('role', 'alertdialog');
382
+ modal.setAttribute('aria-modal', 'true');
383
+ modal.setAttribute('aria-labelledby', 'fe-dialog-title');
384
+ modal.setAttribute('aria-describedby', 'fe-dialog-body');
385
+ const heading = document.createElement('div');
386
+ heading.className = 'fe-modal-title';
387
+ heading.id = 'fe-dialog-title';
388
+ heading.textContent = title;
389
+ const body = document.createElement('div');
390
+ body.className = 'fe-modal-body';
391
+ body.id = 'fe-dialog-body';
392
+ for (const line of lines.filter(Boolean)) { const p = document.createElement('p'); p.textContent = line; body.append(p); }
393
+ const bar = document.createElement('div');
394
+ bar.className = 'fe-modal-actions';
395
+ const button = (label, className, answer) => {
396
+ const node = document.createElement('button');
397
+ node.type = 'button';
398
+ node.className = `fe-modal-btn ${className}`;
399
+ node.textContent = label;
400
+ node.addEventListener('click', () => done(answer));
401
+ bar.append(node);
402
+ return node;
403
+ };
404
+ const cancel = button(cancelLabel, 'fe-modal-cancel', null);
405
+ if (altLabel) button(altLabel, 'fe-modal-cancel', 'alt');
406
+ const confirm = button(confirmLabel, 'fe-modal-confirm', 'confirm');
407
+ modal.append(heading, body, bar);
408
+ overlay.append(modal);
409
+
410
+ // No key reaches the page under the question; the ones it acts on (and only those) are cancelled.
411
+ const keys = (event) => {
412
+ event.stopPropagation();
413
+ if (event.type !== 'keydown' || !plain(event)) return;
414
+ const list = [...bar.querySelectorAll('button')];
415
+ const index = list.indexOf(document.activeElement);
416
+ if (event.key === 'Escape') { event.preventDefault(); done(null); return; }
417
+ if (event.key === 'Tab' || event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
418
+ event.preventDefault();
419
+ const step = event.key === 'ArrowLeft' || (event.key === 'Tab' && event.shiftKey) ? -1 : 1;
420
+ list[(index + step + list.length) % list.length].focus();
421
+ return;
422
+ }
423
+ // Enter and Space are the focused button's own; with no button focused they would act on nothing.
424
+ if ((event.key === 'Enter' || event.key === ' ') && index < 0) event.preventDefault();
425
+ };
426
+ let answered = false;
427
+ function done(answer, { replaced = false } = {}) {
428
+ if (answered) return;
429
+ answered = true;
430
+ if (_dialog === handle) _dialog = null;
431
+ for (const type of KEY_EVENTS) window.removeEventListener(type, keys, true);
432
+ overlay.classList.remove('fe-modal-overlay--in');
433
+ // Another question is taking its place: it goes at once (two of them would share an id for a moment).
434
+ if (replaced) overlay.remove();
435
+ else {
436
+ setTimeout(() => overlay.remove(), 150);
437
+ if (before?.isConnected) before.focus({ preventScroll: true });
438
+ }
439
+ resolve(answer);
440
+ }
441
+ const handle = { before, giveUp: () => done(null, { replaced: true }) };
442
+ _dialog = handle;
443
+ overlay.addEventListener('pointerdown', (event) => { if (event.target === overlay) done(null); });
444
+ for (const type of KEY_EVENTS) window.addEventListener(type, keys, true);
445
+ document.body.appendChild(overlay);
446
+ requestAnimationFrame(() => overlay.classList.add('fe-modal-overlay--in'));
447
+ (danger ? cancel : confirm).focus({ preventScroll: true });
448
+ });
449
+ }
450
+
451
+ /**
452
+ * The line at the foot of the panel: what happened, and what can be done about it.
453
+ * @param {HTMLElement} root #fe-notice @param {{ dismiss: string }} words
454
+ */
455
+ export function createNotice(root, words) {
456
+ let timer = 0;
457
+ const text = root.querySelector('.fe-notice-text');
458
+ const action = root.querySelector('.fe-notice-action');
459
+ const close = root.querySelector('.fe-notice-close');
460
+ let run = null;
461
+ function clear() {
462
+ clearTimeout(timer);
463
+ root.hidden = true;
464
+ run = null;
465
+ }
466
+ action.addEventListener('click', () => { const todo = run; clear(); todo?.(); });
467
+ close.addEventListener('click', clear);
468
+ close.setAttribute('aria-label', words.dismiss);
469
+ return {
470
+ /**
471
+ * @param {string} message
472
+ * @param {{ tone?: 'info' | 'ok' | 'error', action?: { label: string, run: () => void }, ms?: number }} [o] an error
473
+ * stays until it is dismissed or something else is said
474
+ */
475
+ show(message, { tone = 'info', action: todo = null, ms = tone === 'error' ? 0 : 6000 } = {}) {
476
+ clearTimeout(timer);
477
+ root.dataset.tone = tone;
478
+ root.setAttribute('role', tone === 'error' ? 'alert' : 'status');
479
+ text.textContent = message;
480
+ text.title = message;
481
+ run = todo?.run || null;
482
+ action.hidden = !todo;
483
+ action.textContent = todo?.label || '';
484
+ root.hidden = false;
485
+ if (ms > 0) timer = setTimeout(clear, ms);
486
+ },
487
+ clear,
488
+ text: () => (root.hidden ? '' : text.textContent),
489
+ };
490
+ }