@wenathlan/devthink 2.0.15 → 2.0.20

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 (444) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/{dist/bridge.d.ts → bridge.d.ts} +1 -1
  3. package/bridge.d.ts.map +1 -0
  4. package/bridge.js +1877 -0
  5. package/bun.d.ts.map +1 -0
  6. package/bun.js +52329 -0
  7. package/caps/background.json +712 -0
  8. package/caps/cli.json +360 -0
  9. package/caps/library.json +3611 -0
  10. package/caps/mcp.json +126 -0
  11. package/caps/pagebridge.json +377 -0
  12. package/caps/popup.json +51 -0
  13. package/caps/sidepanel.json +522 -0
  14. package/checksums.txt +142 -0
  15. package/cli.d.ts.map +1 -0
  16. package/{dist/cli.js → cli.js} +2 -2
  17. package/companion.d.ts.map +1 -0
  18. package/companion.js +389 -0
  19. package/crossbrowser.d.ts.map +1 -0
  20. package/crossbrowser.js +1191 -0
  21. package/dashdone.d.ts.map +1 -0
  22. package/dashdone.js +368 -0
  23. package/deno.d.ts.map +1 -0
  24. package/deno.js +52325 -0
  25. package/devthink.umd.js +52346 -0
  26. package/fixtures/bun.ts +7 -0
  27. package/fixtures/cjs.js +6 -0
  28. package/fixtures/deno.ts +6 -0
  29. package/fixtures/example-org-pagestate.json +22 -0
  30. package/fixtures/importers/automa-workflow.json +18 -0
  31. package/fixtures/importers/selenium-side.json +20 -0
  32. package/fixtures/importers/tabular-plan.csv +7 -0
  33. package/fixtures/importers/uivision-macro.json +10 -0
  34. package/fixtures/importers/v1-plan.json +13 -0
  35. package/fixtures/mcp-client.mjs +14 -0
  36. package/fixtures/pages/checkout.html +192 -0
  37. package/fixtures/pages/dashboard.html +110 -0
  38. package/fixtures/pages/feed.html +113 -0
  39. package/fixtures/pages/product-grid.html +150 -0
  40. package/fixtures/plans/form-inventory-plan.json +11 -0
  41. package/fixtures/plans/release-notes-plan.json +11 -0
  42. package/fixtures/recipes/agentscompete.json +11 -0
  43. package/fixtures/recipes/agentsforms.json +11 -0
  44. package/fixtures/recipes/agentsmonitor.json +11 -0
  45. package/fixtures/recipes/agentsreview.json +29 -0
  46. package/fixtures/recipes/agentswarm.json +11 -0
  47. package/fixtures/recipes/fillcalendar.json +12 -0
  48. package/fixtures/recipes/fillcheckout.json +13 -0
  49. package/fixtures/recipes/filldependentdropdowns.json +12 -0
  50. package/fixtures/recipes/fillfromcsv.json +13 -0
  51. package/fixtures/recipes/fillloginform.json +12 -0
  52. package/fixtures/recipes/fillmultistepwizard.json +13 -0
  53. package/fixtures/recipes/fillregistration.json +12 -0
  54. package/fixtures/recipes/fillsearchfilters.json +12 -0
  55. package/fixtures/recipes/filltypeahead.json +11 -0
  56. package/fixtures/recipes/monitoralerts.json +12 -0
  57. package/fixtures/recipes/monitoravailability.json +11 -0
  58. package/fixtures/recipes/monitornetworkquiet.json +12 -0
  59. package/fixtures/recipes/monitorpagechanges.json +11 -0
  60. package/fixtures/recipes/monitorpricechange.json +11 -0
  61. package/fixtures/recipes/monitorschedule.json +12 -0
  62. package/fixtures/recipes/scrapedynamicprices.json +13 -0
  63. package/fixtures/recipes/scrapeimagegallery.json +12 -0
  64. package/fixtures/recipes/scrapeinfinitefeed.json +13 -0
  65. package/fixtures/recipes/scrapenestedlists.json +12 -0
  66. package/fixtures/recipes/scrapeproductgrid.json +12 -0
  67. package/fixtures/recipes/scrapereviews.json +12 -0
  68. package/fixtures/recipes/scrapesearchresults.json +12 -0
  69. package/fixtures/recipes/scrapetablepagination.json +12 -0
  70. package/fixtures/recipes/testaccessibility.json +34 -0
  71. package/fixtures/recipes/testconsoleerrors.json +12 -0
  72. package/fixtures/recipes/testdeeplinks.json +12 -0
  73. package/fixtures/recipes/testformvalidation.json +13 -0
  74. package/fixtures/recipes/testlayoutshift.json +12 -0
  75. package/fixtures/recipes/testlinkcheck.json +12 -0
  76. package/fixtures/recipes/testloadtiming.json +12 -0
  77. package/fixtures/recipes/testvisualdiff.json +12 -0
  78. package/fixtures/workflows/changelog-digest-workflow.json +13 -0
  79. package/fixtures/workflows/form-survey-workflow.json +13 -0
  80. package/gallery.json +706 -0
  81. package/hardening.d.ts.map +1 -0
  82. package/hardening.js +188 -0
  83. package/headless.d.ts.map +1 -0
  84. package/headless.js +11522 -0
  85. package/http.d.ts.map +1 -0
  86. package/http.js +2165 -0
  87. package/index.cjs +54682 -0
  88. package/index.d.ts.map +1 -0
  89. package/index.js +52310 -0
  90. package/index.neutral.js +46343 -0
  91. package/{web/manifest.json → manifest.json} +1 -1
  92. package/mcp.d.ts.map +1 -0
  93. package/mcp.js +20515 -0
  94. package/memory.d.ts.map +1 -0
  95. package/{memory.ts → memory.js} +2310 -3834
  96. package/nativehost.template.json +10 -0
  97. package/node.cjs +54686 -0
  98. package/node.d.ts.map +1 -0
  99. package/pack.d.ts.map +1 -0
  100. package/pack.js +805 -0
  101. package/package.json +83 -176
  102. package/policy.d.ts.map +1 -0
  103. package/{policy.ts → policy.js} +4679 -5073
  104. package/progress.d.ts.map +1 -0
  105. package/progress.js +645 -0
  106. package/protocol.d.ts.map +1 -0
  107. package/protocol.js +8334 -0
  108. package/schemas/audit.schema.json +226 -0
  109. package/schemas/capability.schema.json +113 -0
  110. package/schemas/envelope.schema.json +162 -0
  111. package/schemas/memory.schema.json +51 -0
  112. package/schemas/observation.schema.json +113 -0
  113. package/schemas/plan.schema.json +208 -0
  114. package/schemas/progress.schema.json +126 -0
  115. package/schemas/proposal.schema.json +68 -0
  116. package/schemas/session.schema.json +82 -0
  117. package/schemas/tool.schema.json +94 -0
  118. package/server.d.ts.map +1 -0
  119. package/server.js +28877 -0
  120. package/umd-example.html +18 -0
  121. package/umd.d.ts.map +1 -0
  122. package/accounts.ts +0 -2087
  123. package/agent.ts +0 -2331
  124. package/antigravity.ts +0 -2139
  125. package/apifreeze.ts +0 -4146
  126. package/auth.ts +0 -642
  127. package/background.ts +0 -16837
  128. package/bridge.ts +0 -2080
  129. package/build.ts +0 -54
  130. package/bun.ts +0 -26
  131. package/capture.ts +0 -1614
  132. package/checksecrets.ts +0 -72
  133. package/cjs.ts +0 -9
  134. package/cli.ts +0 -3500
  135. package/commands.ts +0 -1101
  136. package/companion.ts +0 -450
  137. package/compatibility.ts +0 -37
  138. package/config.ts +0 -3137
  139. package/constants.ts +0 -943
  140. package/core.ts +0 -1473
  141. package/crossbrowser.ts +0 -1359
  142. package/dashdone.ts +0 -605
  143. package/data.ts +0 -943
  144. package/database.ts +0 -133
  145. package/debug.ts +0 -850
  146. package/deno.ts +0 -27
  147. package/devthink.ts +0 -707
  148. package/dist/accounts.d.ts +0 -409
  149. package/dist/agent.d.ts +0 -837
  150. package/dist/antigravity.d.ts +0 -380
  151. package/dist/apifreeze.d.ts +0 -113
  152. package/dist/auth.d.ts +0 -224
  153. package/dist/capture.d.ts +0 -595
  154. package/dist/cjs.d.ts +0 -6
  155. package/dist/commands.d.ts +0 -310
  156. package/dist/compatibility.d.ts +0 -17
  157. package/dist/config.d.ts +0 -1853
  158. package/dist/constants.d.ts +0 -637
  159. package/dist/core.d.ts +0 -361
  160. package/dist/data.d.ts +0 -330
  161. package/dist/database.d.ts +0 -24
  162. package/dist/debug.d.ts +0 -270
  163. package/dist/engine.d.ts +0 -74
  164. package/dist/environments.d.ts +0 -244
  165. package/dist/evidence.d.ts +0 -277
  166. package/dist/export.d.ts +0 -311
  167. package/dist/fingerprint.d.ts +0 -594
  168. package/dist/flow.d.ts +0 -300
  169. package/dist/gates.d.ts +0 -103
  170. package/dist/identity.d.ts +0 -51
  171. package/dist/ids.d.ts +0 -7
  172. package/dist/llm.d.ts +0 -652
  173. package/dist/models.d.ts +0 -1807
  174. package/dist/net.d.ts +0 -325
  175. package/dist/neutral.d.ts +0 -41
  176. package/dist/oauth.d.ts +0 -703
  177. package/dist/page.d.ts +0 -1166
  178. package/dist/perf.d.ts +0 -481
  179. package/dist/plan.d.ts +0 -134
  180. package/dist/project.d.ts +0 -395
  181. package/dist/providers.d.ts +0 -37
  182. package/dist/quota.d.ts +0 -453
  183. package/dist/recovery.d.ts +0 -621
  184. package/dist/request.d.ts +0 -1492
  185. package/dist/run.d.ts +0 -779
  186. package/dist/runtime.d.ts +0 -212
  187. package/dist/search.d.ts +0 -375
  188. package/dist/security.d.ts +0 -534
  189. package/dist/serve.d.ts +0 -136
  190. package/dist/session.d.ts +0 -296
  191. package/dist/storage.d.ts +0 -53
  192. package/dist/streaming.d.ts +0 -1119
  193. package/dist/swarm.d.ts +0 -328
  194. package/dist/tools.d.ts +0 -170
  195. package/dist/types.d.ts +0 -8357
  196. package/dist/utils.d.ts +0 -82
  197. package/dist/version.d.ts +0 -7
  198. package/dist/versionregistry.d.ts +0 -406
  199. package/dist/views.d.ts +0 -579
  200. package/dist/workbenchsession.d.ts +0 -69
  201. package/dist/workflow.d.ts +0 -649
  202. package/docs/readme.md +0 -92
  203. package/docs/schemas/antigravity.json +0 -192
  204. package/engine.ts +0 -1524
  205. package/environments.ts +0 -1005
  206. package/evidence.ts +0 -643
  207. package/export.ts +0 -889
  208. package/fingerprint.ts +0 -1458
  209. package/flow.ts +0 -1190
  210. package/gates.ts +0 -307
  211. package/gateway.ts +0 -34
  212. package/hardening.ts +0 -191
  213. package/headless.ts +0 -369
  214. package/http.ts +0 -2110
  215. package/identity.ts +0 -246
  216. package/ids.ts +0 -26
  217. package/index.ts +0 -369
  218. package/llm.ts +0 -2682
  219. package/mcp.ts +0 -1835
  220. package/models.ts +0 -3805
  221. package/modes.ts +0 -43
  222. package/net.ts +0 -1214
  223. package/neutral.ts +0 -362
  224. package/node.ts +0 -26
  225. package/oauth.ts +0 -2216
  226. package/offscreen.ts +0 -170
  227. package/pack.ts +0 -956
  228. package/page.ts +0 -7824
  229. package/pagebridge.ts +0 -1116
  230. package/perf.ts +0 -957
  231. package/plan.ts +0 -672
  232. package/plugin.ts +0 -76
  233. package/prisma.config.ts +0 -27
  234. package/progress.ts +0 -1308
  235. package/project.ts +0 -1291
  236. package/protocol.ts +0 -4187
  237. package/providers.ts +0 -246
  238. package/quota.ts +0 -1887
  239. package/recovery.ts +0 -2433
  240. package/request.ts +0 -4482
  241. package/run.ts +0 -2135
  242. package/runtime.ts +0 -526
  243. package/search.ts +0 -1760
  244. package/security.ts +0 -1673
  245. package/serve.ts +0 -419
  246. package/server.ts +0 -5538
  247. package/session.ts +0 -1162
  248. package/storage.ts +0 -182
  249. package/streaming.ts +0 -4101
  250. package/swarm.ts +0 -884
  251. package/sync.ts +0 -56
  252. package/tests/agentcert.test.ts +0 -128
  253. package/tests/agentfleet.test.ts +0 -214
  254. package/tests/agentreview.test.ts +0 -241
  255. package/tests/agentstream.test.ts +0 -335
  256. package/tests/agentwork.test.ts +0 -356
  257. package/tests/apifreeze.test.ts +0 -290
  258. package/tests/apimap.test.ts +0 -112
  259. package/tests/approvalgate.test.ts +0 -175
  260. package/tests/artifactmanifest.test.ts +0 -204
  261. package/tests/attentionfeed.test.ts +0 -163
  262. package/tests/backgroundruns.test.ts +0 -100
  263. package/tests/batchscheduling.test.ts +0 -131
  264. package/tests/blackboard.test.ts +0 -238
  265. package/tests/bridge.test.ts +0 -1088
  266. package/tests/browsercoverage.test.ts +0 -134
  267. package/tests/browserpolyfills.test.ts +0 -124
  268. package/tests/build.mjs +0 -1260
  269. package/tests/clientauth.test.ts +0 -223
  270. package/tests/clitools.test.ts +0 -578
  271. package/tests/confirmgates.test.ts +0 -190
  272. package/tests/containerpack.test.ts +0 -143
  273. package/tests/controlflow.test.ts +0 -2120
  274. package/tests/coordination.test.ts +0 -345
  275. package/tests/copyscreen.test.ts +0 -139
  276. package/tests/core.test.ts +0 -417
  277. package/tests/costcert.test.ts +0 -95
  278. package/tests/crossbrowser.test.ts +0 -115
  279. package/tests/cspaudit.test.ts +0 -79
  280. package/tests/dashdone.test.ts +0 -615
  281. package/tests/datagrid.test.ts +0 -137
  282. package/tests/doccheck.test.ts +0 -107
  283. package/tests/dombatch.test.ts +0 -109
  284. package/tests/emulation.test.ts +0 -282
  285. package/tests/environments.test.ts +0 -259
  286. package/tests/evidenceviews.test.ts +0 -88
  287. package/tests/exporttools.test.ts +0 -185
  288. package/tests/firefoxprep.test.ts +0 -134
  289. package/tests/flowlibrary.test.ts +0 -242
  290. package/tests/flowrun.test.ts +0 -303
  291. package/tests/forensics.test.ts +0 -439
  292. package/tests/gateway.test.ts +0 -939
  293. package/tests/headlesslib.test.ts +0 -171
  294. package/tests/httpstream.test.ts +0 -277
  295. package/tests/immutablelog.test.ts +0 -157
  296. package/tests/inboundguard.test.ts +0 -177
  297. package/tests/lazyload.test.ts +0 -72
  298. package/tests/librarymodes.test.ts +0 -623
  299. package/tests/llm.test.ts +0 -696
  300. package/tests/manifest.test.ts +0 -226
  301. package/tests/maskinputs.test.ts +0 -150
  302. package/tests/matrixverify.test.ts +0 -111
  303. package/tests/mavenpack.test.ts +0 -75
  304. package/tests/mcpserver.test.ts +0 -704
  305. package/tests/memory.test.ts +0 -4155
  306. package/tests/memorycare.test.ts +0 -230
  307. package/tests/migrateplan.test.ts +0 -349
  308. package/tests/minimization.test.ts +0 -414
  309. package/tests/modelroute.test.ts +0 -112
  310. package/tests/multiagent.test.ts +0 -440
  311. package/tests/nativebridge.test.ts +0 -807
  312. package/tests/navigation.test.ts +0 -284
  313. package/tests/nugetpack.test.ts +0 -83
  314. package/tests/orchestration.test.ts +0 -449
  315. package/tests/originpolicy.test.ts +0 -405
  316. package/tests/outputcompare.test.ts +0 -97
  317. package/tests/package.test.ts +0 -91
  318. package/tests/pagebridge.test.ts +0 -5909
  319. package/tests/pentest.test.ts +0 -75
  320. package/tests/perfrecords.test.ts +0 -257
  321. package/tests/permdiff.test.ts +0 -85
  322. package/tests/phishguard.test.ts +0 -128
  323. package/tests/pickerviews.test.ts +0 -155
  324. package/tests/pipelines.test.ts +0 -381
  325. package/tests/planlint.test.ts +0 -240
  326. package/tests/planreview.test.ts +0 -383
  327. package/tests/platformtargets.test.ts +0 -56
  328. package/tests/policy.test.ts +0 -8284
  329. package/tests/portability.test.ts +0 -96
  330. package/tests/progress.test.ts +0 -1036
  331. package/tests/promptlibrary.test.ts +0 -116
  332. package/tests/protocol.test.ts +0 -4793
  333. package/tests/publishingpipeline.test.ts +0 -314
  334. package/tests/quickactions.test.ts +0 -164
  335. package/tests/readiness.test.ts +0 -78
  336. package/tests/recipes.test.ts +0 -163
  337. package/tests/redactshots.test.ts +0 -216
  338. package/tests/registernvidiakeys.ts +0 -51
  339. package/tests/relayserve.test.ts +0 -304
  340. package/tests/releasenotes.test.ts +0 -276
  341. package/tests/resilience.test.ts +0 -467
  342. package/tests/resourceaware.test.ts +0 -187
  343. package/tests/runbudget.test.ts +0 -125
  344. package/tests/runhistory.test.ts +0 -299
  345. package/tests/runreplay.test.ts +0 -113
  346. package/tests/runresume.test.ts +0 -184
  347. package/tests/runstate.test.ts +0 -288
  348. package/tests/runtimeadapters.test.ts +0 -251
  349. package/tests/safariskeleton.test.ts +0 -115
  350. package/tests/sandboxframe.test.ts +0 -196
  351. package/tests/sbom.test.ts +0 -105
  352. package/tests/secretvault.test.ts +0 -188
  353. package/tests/selcache.test.ts +0 -111
  354. package/tests/sessioninterface.test.ts +0 -791
  355. package/tests/sessions.test.ts +0 -591
  356. package/tests/siteprefs.test.ts +0 -228
  357. package/tests/snapshotdelta.test.ts +0 -165
  358. package/tests/statusviews.test.ts +0 -237
  359. package/tests/stepmeter.test.ts +0 -200
  360. package/tests/streamparse.test.ts +0 -163
  361. package/tests/stylepruning.test.ts +0 -90
  362. package/tests/surfaces.test.ts +0 -282
  363. package/tests/sweep.test.ts +0 -78
  364. package/tests/syncbridge.test.ts +0 -164
  365. package/tests/taskqueue.test.ts +0 -206
  366. package/tests/toolcalls.test.ts +0 -336
  367. package/tests/toolcatalog.test.ts +0 -201
  368. package/tests/torture-agent.test.ts +0 -706
  369. package/tests/torture-auth.test.ts +0 -461
  370. package/tests/torture-commands.test.ts +0 -895
  371. package/tests/torture-data.test.ts +0 -923
  372. package/tests/torture-environments.test.ts +0 -285
  373. package/tests/torture-export.test.ts +0 -263
  374. package/tests/torture-http.test.ts +0 -1379
  375. package/tests/torture-llm.test.ts +0 -808
  376. package/tests/torture-memory.test.ts +0 -1084
  377. package/tests/torture-plan.test.ts +0 -829
  378. package/tests/torture-run.test.ts +0 -626
  379. package/tests/torture-session.test.ts +0 -928
  380. package/tests/torture-swarm.test.ts +0 -1245
  381. package/tests/torture-tools.test.ts +0 -964
  382. package/tests/torture-workflow.test.ts +0 -2354
  383. package/tests/tourviews.test.ts +0 -52
  384. package/tests/transparency.test.ts +0 -147
  385. package/tests/trigger.test.ts +0 -941
  386. package/tests/v1sunset.test.ts +0 -428
  387. package/tests/virtlist.test.ts +0 -94
  388. package/tests/vision.test.ts +0 -540
  389. package/tests/vsixpack.test.ts +0 -145
  390. package/tests/webapi.test.ts +0 -523
  391. package/tests/workflow.test.ts +0 -1785
  392. package/tests/workfloweditor.test.ts +0 -759
  393. package/tests/xpipack.test.ts +0 -128
  394. package/tools.ts +0 -734
  395. package/types.ts +0 -9908
  396. package/umd.ts +0 -9
  397. package/utils.ts +0 -249
  398. package/version.ts +0 -8
  399. package/versionregistry.ts +0 -1310
  400. package/views.ts +0 -2153
  401. package/vitest.config.ts +0 -12
  402. package/web/App.tsx +0 -78
  403. package/web/README.md +0 -47
  404. package/web/capacitor.config.ts +0 -46
  405. package/web/const.ts +0 -19
  406. package/web/control.shell.tsx +0 -66
  407. package/web/dashboardpage.ts +0 -554
  408. package/web/db.ts +0 -493
  409. package/web/design.html +0 -541
  410. package/web/gatewayview/Gateway.tsx +0 -345
  411. package/web/gatewayview/config.ts +0 -1042
  412. package/web/gatewayview/definition.ts +0 -174
  413. package/web/icons.ts +0 -15
  414. package/web/optionspage.ts +0 -691
  415. package/web/popup.ts +0 -1651
  416. package/web/schema.prisma +0 -176
  417. package/web/sidepanel.ts +0 -7941
  418. package/web/sitemanifest.json +0 -13
  419. package/web/terminal-ui.tsx +0 -622
  420. package/web/transparencypage.ts +0 -541
  421. package/web/vite.config.ts +0 -39
  422. package/web/workspace.ts +0 -23
  423. package/workbenchmemory.ts +0 -104
  424. package/workbenchsession.ts +0 -304
  425. package/workflow.ts +0 -4495
  426. /package/{dist/bun.d.ts → bun.d.ts} +0 -0
  427. /package/{dist/cli.d.ts → cli.d.ts} +0 -0
  428. /package/{dist/companion.d.ts → companion.d.ts} +0 -0
  429. /package/{dist/crossbrowser.d.ts → crossbrowser.d.ts} +0 -0
  430. /package/{dist/dashdone.d.ts → dashdone.d.ts} +0 -0
  431. /package/{dist/deno.d.ts → deno.d.ts} +0 -0
  432. /package/{dist/hardening.d.ts → hardening.d.ts} +0 -0
  433. /package/{dist/headless.d.ts → headless.d.ts} +0 -0
  434. /package/{dist/http.d.ts → http.d.ts} +0 -0
  435. /package/{dist/index.d.ts → index.d.ts} +0 -0
  436. /package/{dist/mcp.d.ts → mcp.d.ts} +0 -0
  437. /package/{dist/memory.d.ts → memory.d.ts} +0 -0
  438. /package/{dist/node.d.ts → node.d.ts} +0 -0
  439. /package/{dist/pack.d.ts → pack.d.ts} +0 -0
  440. /package/{dist/policy.d.ts → policy.d.ts} +0 -0
  441. /package/{dist/progress.d.ts → progress.d.ts} +0 -0
  442. /package/{dist/protocol.d.ts → protocol.d.ts} +0 -0
  443. /package/{dist/server.d.ts → server.d.ts} +0 -0
  444. /package/{dist/umd.d.ts → umd.d.ts} +0 -0
@@ -1,691 +0,0 @@
1
- export {};
2
-
3
- /**
4
- * Optionspage runtime of the 1.1.64 family.
5
- * The optionspage gathers every setting into one page with sections: the onboarding replay, the commandpalette recent window and shortcut, the logstream live buffer bound, the taskinput history retention, the diffpreview offscreen offload ceiling and the session interface options of the 1.1.63 family, while the transparency, consent and security sections of the earlier releases import through the embedded transparencypage; every write takes effect without reloading the extension because the background reads its settings live on each decision. The 2.0.2 final polish adds the contrast preference of the rc.2 high contrast theme: the toggle persists through the surface layout seam and resolves the high contrast token set over the --theme-* custom properties the darklight family already writes.
6
- */
7
-
8
- import { applycontrasttheme, contrasttokensof } from "../views.js";
9
-
10
- const statusnode = document.querySelector<HTMLElement>("#status");
11
- const onboardingroot = document.querySelector<HTMLElement>("#onboarding");
12
- const paletterecentsinput = document.querySelector<HTMLInputElement>("#paletterecents");
13
- const paletteshortcutinput = document.querySelector<HTMLInputElement>("#paletteshortcut");
14
- const logstreambufferinput = document.querySelector<HTMLInputElement>("#logstreambuffer");
15
- const taskinputretentioninput = document.querySelector<HTMLInputElement>("#taskinputretention");
16
- const diffpreviewbytesinput = document.querySelector<HTMLInputElement>("#diffpreviewbytes");
17
- const recallwindowinput = document.querySelector<HTMLInputElement>("#recallwindow");
18
- const noteretentioninput = document.querySelector<HTMLInputElement>("#noteretention");
19
- const summarywindowinput = document.querySelector<HTMLInputElement>("#summarywindow");
20
-
21
- function status(message: string, error = false): void { if (statusnode) { statusnode.textContent = message; statusnode.dataset.state = error ? "error" : "ready"; } }
22
- async function request(message: unknown): Promise<unknown> { const response = await chrome.runtime.sendMessage(message) as { ok: boolean; value?: unknown; error?: string }; if (!response.ok) throw new Error(response.error); return response.value; }
23
-
24
- type surfacecontext = { sessionpreferences?: { recallwindow?: number; noteretention?: number; summarywindow?: number }; surfacepreferences?: { paletterecents?: number; paletteshortcut?: string; logstreambuffer?: number; taskinputretention?: number; diffpreviewbytes?: number }; perfpreferences?: { prewarmset?: string[]; lazybudget?: number; debouncewindows?: Partial<Record<"scroll" | "input" | "resize" | "mutation", number>>; snapshotcadence?: number; workerdepth?: number; perfretention?: number; virtlistrows?: number; capturepriority?: "speed" | "evidence"; batchwindow?: number; domainlimits?: Record<string, number>; politedelay?: number; politejitter?: number; pollgrowth?: number; stepbudget?: number; memorybudget?: number; budgetthresholds?: { warning?: number; critical?: number }; timeoutbound?: number; suspendwindow?: number; startuptarget?: number; selectorlatency?: number; slowmofactor?: number; runcachepin?: boolean; logpruneretention?: number; logsizewindow?: number; artifactcodec?: "deflate" | "store"; batteryfloor?: number; netbackoff?: Record<string, number>; readlanes?: number; cadencewidening?: number } };
25
-
26
- /** Loads the stored surface options into the inputs; an absent value keeps the placeholder that names the documented default. */
27
- async function load(): Promise<void> {
28
- try {
29
- const context = await request({ kind: "context" }) as surfacecontext;
30
- const preferences = context.surfacepreferences ?? {};
31
- const session = context.sessionpreferences ?? {};
32
- if (preferences.paletterecents !== undefined && paletterecentsinput) paletterecentsinput.value = String(preferences.paletterecents);
33
- if (preferences.paletteshortcut !== undefined && paletteshortcutinput) paletteshortcutinput.value = preferences.paletteshortcut;
34
- if (preferences.logstreambuffer !== undefined && logstreambufferinput) logstreambufferinput.value = String(preferences.logstreambuffer);
35
- if (preferences.taskinputretention !== undefined && taskinputretentioninput) taskinputretentioninput.value = String(preferences.taskinputretention);
36
- if (preferences.diffpreviewbytes !== undefined && diffpreviewbytesinput) diffpreviewbytesinput.value = String(preferences.diffpreviewbytes);
37
- if (session.recallwindow !== undefined && recallwindowinput) recallwindowinput.value = String(session.recallwindow);
38
- if (session.noteretention !== undefined && noteretentioninput) noteretentioninput.value = String(session.noteretention);
39
- if (session.summarywindow !== undefined && summarywindowinput) summarywindowinput.value = String(session.summarywindow);
40
- await loadperf();
41
- status("The surface options loaded; every write takes effect without reloading the extension.");
42
- } catch (error) { status(error instanceof Error ? error.message : String(error), true); }
43
- }
44
-
45
- /** Writes the commandpalette options; the recent window and the shortcut stay user choices with no engine defaults forced. */
46
- async function savepalette(): Promise<void> {
47
- const paletterecents = paletterecentsinput?.value.trim() ?? "";
48
- const paletteshortcut = paletteshortcutinput?.value.trim() ?? "";
49
- await request({ kind: "surface", settings: { ...(paletterecents !== "" ? { paletterecents: Number(paletterecents) } : {}), ...(paletteshortcut !== "" ? { paletteshortcut } : {}) } });
50
- status(`The palette options saved${paletterecents !== "" ? ` with the recent window of ${paletterecents}` : ""}${paletteshortcut !== "" ? ` and the shortcut ${paletteshortcut}` : ""}.`);
51
- }
52
-
53
- /** Writes the logstream live buffer bound; the full history stays in memory whatever the bound. */
54
- async function savelogstream(): Promise<void> {
55
- const bound = logstreambufferinput?.value.trim() ?? "";
56
- await request({ kind: "surface", settings: { ...(bound !== "" ? { logstreambuffer: Number(bound) } : {}) } });
57
- status(bound === "" ? "No bound configured; the live window keeps every event." : `The logstream live buffer bound of ${bound} saved; the full history stays in memory.`);
58
- }
59
-
60
- /** Writes the taskinput history retention. */
61
- async function savetaskinput(): Promise<void> {
62
- const retention = taskinputretentioninput?.value.trim() ?? "";
63
- await request({ kind: "surface", settings: { ...(retention !== "" ? { taskinputretention: Number(retention) } : {}) } });
64
- status(retention === "" ? "No retention configured; the taskinput history keeps every entry." : `The taskinput history retention of ${retention} milliseconds saved.`);
65
- }
66
-
67
- /** Writes the diffpreview offscreen offload byte ceiling. */
68
- async function savediff(): Promise<void> {
69
- const bytes = diffpreviewbytesinput?.value.trim() ?? "";
70
- await request({ kind: "surface", settings: { ...(bytes !== "" ? { diffpreviewbytes: Number(bytes) } : {}) } });
71
- status(bytes === "" ? "No ceiling configured; every diff stays inline." : `The diffpreview offload ceiling of ${bytes} bytes saved.`);
72
- }
73
-
74
- /** Writes the session interface options of the 1.1.63 family through the sessions settings seam. */
75
- async function savesession(): Promise<void> {
76
- const recallwindow = recallwindowinput?.value.trim() ?? "";
77
- const noteretention = noteretentioninput?.value.trim() ?? "";
78
- const summarywindow = summarywindowinput?.value.trim() ?? "";
79
- await request({ kind: "sessions", settings: { ...(recallwindow !== "" ? { recallwindow: Number(recallwindow) } : {}), ...(noteretention !== "" ? { noteretention: Number(noteretention) } : {}), ...(summarywindow !== "" ? { summarywindow: Number(summarywindow) } : {}) } });
80
- status("The session interface options saved; the recall window, the note retention and the summary window take effect at once.");
81
- }
82
-
83
- /** Loads the 1.1.68 performance options: the incrsnapshot cadence, the debouncedom windows per event kind, the worker queue depth, the lazymods prewarm set, the startup module budget, the perf retention, the virtlist row window and the capture priority, every one a user choice with no engine default. */
84
- async function loadperf(): Promise<void> {
85
- const perf = ((await request({ kind: "context" }) as surfacecontext).perfpreferences) ?? {};
86
- if (perf.snapshotcadence !== undefined && document.querySelector<HTMLInputElement>("#snapshotcadence")) document.querySelector<HTMLInputElement>("#snapshotcadence")!.value = String(perf.snapshotcadence);
87
- if (perf.debouncewindows?.scroll !== undefined && document.querySelector<HTMLInputElement>("#debouncescroll")) document.querySelector<HTMLInputElement>("#debouncescroll")!.value = String(perf.debouncewindows.scroll);
88
- if (perf.debouncewindows?.input !== undefined && document.querySelector<HTMLInputElement>("#debounceinput")) document.querySelector<HTMLInputElement>("#debounceinput")!.value = String(perf.debouncewindows.input);
89
- if (perf.debouncewindows?.resize !== undefined && document.querySelector<HTMLInputElement>("#debouncerresize")) document.querySelector<HTMLInputElement>("#debouncerresize")!.value = String(perf.debouncewindows.resize);
90
- if (perf.debouncewindows?.mutation !== undefined && document.querySelector<HTMLInputElement>("#debouncemutation")) document.querySelector<HTMLInputElement>("#debouncemutation")!.value = String(perf.debouncewindows.mutation);
91
- if (perf.workerdepth !== undefined && document.querySelector<HTMLInputElement>("#workerdepth")) document.querySelector<HTMLInputElement>("#workerdepth")!.value = String(perf.workerdepth);
92
- if (perf.prewarmset !== undefined && document.querySelector<HTMLInputElement>("#prewarmset")) document.querySelector<HTMLInputElement>("#prewarmset")!.value = perf.prewarmset.join(",");
93
- if (perf.lazybudget !== undefined && document.querySelector<HTMLInputElement>("#lazybudget")) document.querySelector<HTMLInputElement>("#lazybudget")!.value = String(perf.lazybudget);
94
- if (perf.perfretention !== undefined && document.querySelector<HTMLInputElement>("#perfretention")) document.querySelector<HTMLInputElement>("#perfretention")!.value = String(perf.perfretention);
95
- if (perf.virtlistrows !== undefined && document.querySelector<HTMLInputElement>("#virtlistrows")) document.querySelector<HTMLInputElement>("#virtlistrows")!.value = String(perf.virtlistrows);
96
- if (perf.capturepriority !== undefined && document.querySelector<HTMLSelectElement>("#capturepriority")) document.querySelector<HTMLSelectElement>("#capturepriority")!.value = perf.capturepriority;
97
- const schedulefields: Array<[keyof typeof perf, string]> = [["batchwindow", "#batchwindow"], ["politedelay", "#politedelay"], ["politejitter", "#politejitter"], ["pollgrowth", "#pollgrowth"], ["stepbudget", "#stepbudget"], ["memorybudget", "#memorybudget"], ["timeoutbound", "#timeoutbound"], ["suspendwindow", "#suspendwindow"], ["startuptarget", "#startuptarget"], ["selectorlatency", "#selectorlatency"], ["slowmofactor", "#slowmofactor"], ["logpruneretention", "#logpruneretention"], ["logsizewindow", "#logsizewindow"], ["batteryfloor", "#batteryfloor"], ["readlanes", "#readlanes"], ["cadencewidening", "#cadencewidening"]];
98
- for (const [field, id] of schedulefields) {
99
- const value = perf[field];
100
- if (typeof value === "number" && document.querySelector<HTMLInputElement>(id)) document.querySelector<HTMLInputElement>(id)!.value = String(value);
101
- }
102
- if (perf.domainlimits !== undefined && document.querySelector<HTMLInputElement>("#domainlimits")) document.querySelector<HTMLInputElement>("#domainlimits")!.value = Object.entries(perf.domainlimits).map(([domain, slots]) => `${domain}=${slots}`).join(",");
103
- if (perf.budgetthresholds?.warning !== undefined && document.querySelector<HTMLInputElement>("#budgetwarning")) document.querySelector<HTMLInputElement>("#budgetwarning")!.value = String(perf.budgetthresholds.warning);
104
- if (perf.budgetthresholds?.critical !== undefined && document.querySelector<HTMLInputElement>("#budgetcritical")) document.querySelector<HTMLInputElement>("#budgetcritical")!.value = String(perf.budgetthresholds.critical);
105
- if (perf.artifactcodec !== undefined && document.querySelector<HTMLSelectElement>("#artifactcodec")) document.querySelector<HTMLSelectElement>("#artifactcodec")!.value = perf.artifactcodec;
106
- if (document.querySelector<HTMLInputElement>("#runcachepin")) document.querySelector<HTMLInputElement>("#runcachepin")!.checked = perf.runcachepin === true;
107
- await renderstartupbudget();
108
- }
109
-
110
- /** Renders the startup module budget view: the prewarmed modules, the lazy rest and the budget the user configured, reported without ever refusing a load. */
111
- async function renderstartupbudget(): Promise<void> {
112
- const budgetnode = document.querySelector<HTMLElement>("#startupbudget");
113
- if (!budgetnode) return;
114
- try {
115
- const view = await request({ kind: "perf", lazy: { list: true } }) as { budget: { prewarmed: string[]; lazy: string[]; total: number; budget?: number; over: boolean }; cost: { startupmodules: number; lazy: number; note: string } };
116
- budgetnode.textContent = `Startup budget: ${view.budget.prewarmed.length} prewarmed module${view.budget.prewarmed.length === 1 ? "" : "s"} of ${view.budget.total}${view.budget.prewarmed.length > 0 ? ` (${view.budget.prewarmed.join(", ")})` : ""} · ${view.budget.lazy.length} lazy module${view.budget.lazy.length === 1 ? "" : "s"} out of the startup path${view.budget.budget !== undefined ? ` · user budget ${view.budget.budget}${view.budget.over ? " (over, reported never refused)" : ""}` : " · no budget set"} — ${view.cost.note}`;
117
- } catch { budgetnode.textContent = "The startup module budget loads with the lazy module catalog."; }
118
- }
119
-
120
- /** Saves the 1.1.68 performance options through the perf settings command; every window, depth, cadence and budget stays the user's choice. */
121
- async function saveperf(): Promise<void> {
122
- const numberof = (id: string): number | undefined => { const raw = document.querySelector<HTMLInputElement>(id)?.value.trim() ?? ""; return raw !== "" && Number.isFinite(Number(raw)) ? Number(raw) : undefined; };
123
- const windows: Record<string, number> = {};
124
- for (const [id, kind] of [["#debouncescroll", "scroll"], ["#debounceinput", "input"], ["#debouncerresize", "resize"], ["#debouncemutation", "mutation"]] as const) {
125
- const window = numberof(id);
126
- if (window !== undefined) windows[kind] = window;
127
- }
128
- const prewarmraw = document.querySelector<HTMLInputElement>("#prewarmset")?.value.trim() ?? "";
129
- const prewarmset = prewarmraw === "" ? undefined : prewarmraw.split(",").map(id => id.trim()).filter(id => id !== "");
130
- const priority = document.querySelector<HTMLSelectElement>("#capturepriority")?.value ?? "";
131
- await request({ kind: "perf", settings: { ...(windows !== undefined && Object.keys(windows).length > 0 ? { debouncewindows: windows } : {}), ...(prewarmset !== undefined ? { prewarmset } : {}), ...(numberof("#snapshotcadence") !== undefined ? { snapshotcadence: numberof("#snapshotcadence") } : {}), ...(numberof("#workerdepth") !== undefined ? { workerdepth: numberof("#workerdepth") } : {}), ...(numberof("#lazybudget") !== undefined ? { lazybudget: numberof("#lazybudget") } : {}), ...(numberof("#perfretention") !== undefined ? { perfretention: numberof("#perfretention") } : {}), ...(numberof("#virtlistrows") !== undefined ? { virtlistrows: numberof("#virtlistrows") } : {}), ...(priority === "speed" ? { capturepriority: "speed" } : {}) } });
132
- status("The performance options saved; the cadence, the windows, the depth, the prewarm set and the budget take effect at once.");
133
- await renderstartupbudget();
134
- }
135
-
136
- /** Saves the 1.1.69 scheduling and budget options through the schedule settings command: the batch backpressure window, the domain limits, the politedelay windows, the adaptivepoll growth, the step and memory budgets with their alert thresholds, the timeout bound, the suspend window, the cold start target, the selector latency threshold, the slowmo default factor, the runcache pin, the logprune windows, the artifact codec, the battery floor, the read lane ceiling and the cadence widening factor, every one a user choice that optimizes and never caps. */
137
- async function saveschedule(): Promise<void> {
138
- const numberof = (id: string): number | undefined => { const raw = document.querySelector<HTMLInputElement>(id)?.value.trim() ?? ""; return raw !== "" && Number.isFinite(Number(raw)) ? Number(raw) : undefined; };
139
- const domainlimits: Record<string, number> = {};
140
- const domainraw = document.querySelector<HTMLInputElement>("#domainlimits")?.value.trim() ?? "";
141
- for (const entry of domainraw.split(",").map(part => part.trim()).filter(part => part !== "")) {
142
- const [domain, slots] = entry.split("=").map(part => part.trim());
143
- if (domain !== undefined && domain !== "" && slots !== undefined && Number.isFinite(Number(slots)) && Number(slots) > 0) domainlimits[domain] = Number(slots);
144
- }
145
- const warning = numberof("#budgetwarning");
146
- const critical = numberof("#budgetcritical");
147
- const codec = document.querySelector<HTMLSelectElement>("#artifactcodec")?.value ?? "";
148
- await request({ kind: "schedule", settings: { ...(numberof("#batchwindow") !== undefined ? { batchwindow: numberof("#batchwindow") } : {}), ...(Object.keys(domainlimits).length > 0 ? { domainlimits } : {}), ...(numberof("#politedelay") !== undefined ? { politedelay: numberof("#politedelay") } : {}), ...(numberof("#politejitter") !== undefined ? { politejitter: numberof("#politejitter") } : {}), ...(numberof("#pollgrowth") !== undefined ? { pollgrowth: numberof("#pollgrowth") } : {}), ...(numberof("#stepbudget") !== undefined ? { stepbudget: numberof("#stepbudget") } : {}), ...(numberof("#memorybudget") !== undefined ? { memorybudget: numberof("#memorybudget") } : {}), ...(warning !== undefined || critical !== undefined ? { budgetthresholds: { ...(warning !== undefined ? { warning } : {}), ...(critical !== undefined ? { critical } : {}) } } : {}), ...(numberof("#timeoutbound") !== undefined ? { timeoutbound: numberof("#timeoutbound") } : {}), ...(numberof("#suspendwindow") !== undefined ? { suspendwindow: numberof("#suspendwindow") } : {}), ...(numberof("#startuptarget") !== undefined ? { startuptarget: numberof("#startuptarget") } : {}), ...(numberof("#selectorlatency") !== undefined ? { selectorlatency: numberof("#selectorlatency") } : {}), ...(numberof("#slowmofactor") !== undefined ? { slowmofactor: numberof("#slowmofactor") } : {}), ...(document.querySelector<HTMLInputElement>("#runcachepin")?.checked === true ? { runcachepin: true } : { runcachepin: false }), ...(numberof("#logpruneretention") !== undefined ? { logpruneretention: numberof("#logpruneretention") } : {}), ...(numberof("#logsizewindow") !== undefined ? { logsizewindow: numberof("#logsizewindow") } : {}), ...(codec === "deflate" ? { artifactcodec: "deflate" } : { artifactcodec: "store" }), ...(numberof("#batteryfloor") !== undefined ? { batteryfloor: numberof("#batteryfloor") } : {}), ...(numberof("#readlanes") !== undefined ? { readlanes: numberof("#readlanes") } : {}), ...(numberof("#cadencewidening") !== undefined ? { cadencewidening: numberof("#cadencewidening") } : {}) } });
149
- status("The scheduling and budget options saved; the windows, the limits, the delays, the budgets and the slowmo default factor take effect at once and none ever caps the work.");
150
- }
151
-
152
- /** Renders the onboarding state and replays the walkthrough on demand. */
153
- async function renderonboarding(): Promise<void> {
154
- if (!onboardingroot) return;
155
- try {
156
- const result = await request({ kind: "surface", onboarding: {} }) as { steps: Array<{ id: string; title: string; body: string; surface: string }>; state?: { stepscompleted: string[]; done: boolean } };
157
- onboardingroot.replaceChildren();
158
- const state = result.state;
159
- const line = document.createElement("p");
160
- line.textContent = state === undefined ? "The onboarding walkthrough never ran; it starts on the next popup open." : state.done ? `The walkthrough is done (${state.stepscompleted.length} steps); the replay restarts it on demand and writes no second consent event.` : `The walkthrough stands at ${state.stepscompleted.length} of ${result.steps.length} steps.`;
161
- onboardingroot.append(line);
162
- const list = document.createElement("ol");
163
- list.className = "audit";
164
- for (const step of result.steps) list.append(Object.assign(document.createElement("li"), { textContent: `${step.title} (${step.surface}): ${step.body}${state?.stepscompleted.includes(step.id) ? " — done" : ""}` }));
165
- onboardingroot.append(list);
166
- } catch (error) { onboardingroot.textContent = error instanceof Error ? error.message : String(error); }
167
- }
168
-
169
- document.querySelector<HTMLButtonElement>("#replayonboarding")?.addEventListener("click", () => { void (async () => { await request({ kind: "surface", onboarding: { replay: true } }); status("The onboarding walkthrough replays; it opens on the next popup open."); await renderonboarding(); })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
170
- document.querySelector<HTMLButtonElement>("#palettesettings")?.addEventListener("click", () => { void savepalette().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
171
- document.querySelector<HTMLButtonElement>("#logstreamsettings")?.addEventListener("click", () => { void savelogstream().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
172
- document.querySelector<HTMLButtonElement>("#taskinputsettings")?.addEventListener("click", () => { void savetaskinput().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
173
- document.querySelector<HTMLButtonElement>("#diffsettings")?.addEventListener("click", () => { void savediff().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
174
- document.querySelector<HTMLButtonElement>("#sessionsettings")?.addEventListener("click", () => { void savesession().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
175
-
176
- /** The optionspage sees settings changes through the single broadcast channel so two open pages never drift. */
177
- const surfacechannel: BroadcastChannel | undefined = typeof BroadcastChannel === "function" ? new BroadcastChannel("devthinksurfaces") : undefined;
178
- surfacechannel?.addEventListener("message", (event: MessageEvent) => { const frame = event.data as { channel?: string }; if (frame?.channel === "settings") { void load().catch(() => { /* a failing reload keeps the last loaded options */ }); } });
179
-
180
- void load();
181
- void renderonboarding();
182
-
183
- /**
184
- * Interface finishing options of the 1.1.65 family: the darklight theme preference with its manual override, the interface language of the locale bundles, the recenttray depth, the step toast live count, the notification consent and preference, the user editable shortcutkeys, the importexport bundles with their dropimport zone and the featuretour replay with the guidedtip recall.
185
- */
186
- const themepreferenceselect = document.querySelector<HTMLSelectElement>("#themepreference");
187
- const contrastpreferenceselect = document.querySelector<HTMLSelectElement>("#contrastpreference");
188
- const uilanguageselect = document.querySelector<HTMLSelectElement>("#uilanguage");
189
- const recenttraydepthinput = document.querySelector<HTMLInputElement>("#recenttraydepth");
190
- const toastlivecountinput = document.querySelector<HTMLInputElement>("#toastlivecount");
191
- const notifyconsentinput = document.querySelector<HTMLInputElement>("#notifyconsent");
192
- const notifyenabledinput = document.querySelector<HTMLInputElement>("#notifyenabled");
193
- const shortcutlistnode = document.querySelector<HTMLElement>("#shortcutlist");
194
- const shortcuteditinput = document.querySelector<HTMLInputElement>("#shortcutedit");
195
- const bundlenode = document.querySelector<HTMLElement>("#bundlestatus");
196
- const dropzonenode = document.querySelector<HTMLElement>("#dropzone");
197
- const tourstopsnode = document.querySelector<HTMLElement>("#tourstops");
198
-
199
- type finishingcontext = { surfacepreferences?: { recenttraydepth?: number; notifyconsent?: boolean; notifyenabled?: boolean; themepreference?: string; uilanguage?: string; toastlivecount?: number } };
200
-
201
- /** The surfaces the contrast preference covers: the optionspage owns the toggle while the sidepanel and the dashboardpage render the themed panels, so the preference persists into every one of their layout preference records. */
202
- const contrastsurfaces = ["optionspage", "sidepanel", "dashboardpage"] as const;
203
-
204
- /** Reads the persisted contrast preference through the surface layout seam. */
205
- async function contrastpreferenceof(): Promise<"default" | "high"> {
206
- try {
207
- const layout = await request({ kind: "surface", layout: { get: { surface: "optionspage" } } }) as { layout?: { preferences?: { contrastpreference?: string } } };
208
- return layout.layout?.preferences?.contrastpreference === "high" ? "high" : "default";
209
- } catch { return "default"; /* an unreadable preference keeps the default contrast */ }
210
- }
211
-
212
- /** Persists the contrast preference through the surface layout seam: the toggle writes the preference into the layout record of every themed surface, merging into the preferences each surface already stores. */
213
- async function savecontrastpreference(contrast: "default" | "high"): Promise<void> {
214
- for (const surface of contrastsurfaces) {
215
- const layout = await request({ kind: "surface", layout: { get: { surface } } }) as { layout?: { preferences?: Record<string, string> } };
216
- const preferences = { ...(layout.layout?.preferences ?? {}), contrastpreference: contrast };
217
- await request({ kind: "surface", layout: { set: { surface, preferences } } });
218
- }
219
- status(contrast === "high" ? "The high contrast preference saved for every themed surface; each surface resolves the high contrast token set on its next open." : "The default contrast preference saved; every themed surface keeps its shipped tokens.");
220
- }
221
-
222
- /** Loads the finishing options beside the surface options. */
223
- async function loadfinishing(): Promise<void> {
224
- try {
225
- const context = await request({ kind: "context" }) as finishingcontext;
226
- const preferences = context.surfacepreferences ?? {};
227
- if (preferences.themepreference !== undefined && themepreferenceselect) themepreferenceselect.value = preferences.themepreference;
228
- if (preferences.uilanguage !== undefined && uilanguageselect) uilanguageselect.value = preferences.uilanguage;
229
- if (preferences.recenttraydepth !== undefined && recenttraydepthinput) recenttraydepthinput.value = String(preferences.recenttraydepth);
230
- if (preferences.toastlivecount !== undefined && toastlivecountinput) toastlivecountinput.value = String(preferences.toastlivecount);
231
- if (notifyconsentinput) notifyconsentinput.checked = preferences.notifyconsent === true;
232
- if (notifyenabledinput) notifyenabledinput.checked = preferences.notifyenabled !== false;
233
- if (contrastpreferenceselect) contrastpreferenceselect.value = await contrastpreferenceof();
234
- } catch (error) { status(error instanceof Error ? error.message : String(error), true); }
235
- }
236
-
237
- /** Saves the finishing options: the theme preference, the contrast preference, the language, the tray depth, the toast live count and the notification consent and preference. */
238
- async function savefinishing(): Promise<void> {
239
- const themepreference = themepreferenceselect?.value ?? "";
240
- const contrast = contrastpreferenceselect?.value === "high" ? "high" : "default";
241
- const uilanguage = uilanguageselect?.value ?? "";
242
- const recenttraydepth = recenttraydepthinput?.value.trim() ?? "";
243
- const toastlivecount = toastlivecountinput?.value.trim() ?? "";
244
- await request({ kind: "views", settings: { ...(themepreference !== "" ? { themepreference } : {}), ...(uilanguage !== "" ? { uilanguage } : {}), ...(recenttraydepth !== "" ? { recenttraydepth: Number(recenttraydepth) } : {}), ...(toastlivecount !== "" ? { toastlivecount: Number(toastlivecount) } : {}), notifyconsent: notifyconsentinput?.checked === true, notifyenabled: notifyenabledinput?.checked === true } });
245
- await savecontrastpreference(contrast);
246
- await applythemelive();
247
- status("The interface finishing options saved; the theme, the contrast, the language, the tray depth and the toast live count take effect at once.");
248
- }
249
-
250
- /** Resolves and applies the darklight tokens with their contrast variant on the live document so the theme covers the optionspage too: the contrast preference of the 2.0.2 final polish resolves the high contrast token set of the resolved mode and writes it through the same --theme-* custom properties while the body carries its data-contrast marker. */
251
- async function applythemelive(): Promise<void> {
252
- try {
253
- const result = await request({ kind: "views", theme: { resolve: true, preference: themepreferenceselect?.value ?? "" } }) as { appearance: { mode: "dark" | "light"; tokens: Record<string, string>; source: string } };
254
- const contrast = contrastpreferenceselect?.value === "high" ? "high" : "default";
255
- const tokens = contrast === "high" ? contrasttokensof({ mode: result.appearance.mode, contrast }) : { mode: result.appearance.mode, tokens: result.appearance.tokens };
256
- applycontrasttheme(document.documentElement, document.body, tokens, contrast);
257
- } catch { /* a failing theme read keeps the shipped light theme */ }
258
- }
259
-
260
- /** Renders the user editable shortcutkeys bindings with their display text. */
261
- async function rendershortcuts(): Promise<void> {
262
- if (!shortcutlistnode) return;
263
- try {
264
- const result = await request({ kind: "views", shortcut: { list: true } }) as { bindings?: Array<{ command: string; key: string; modifiers: string[]; display?: string }> };
265
- shortcutlistnode.replaceChildren();
266
- const list = document.createElement("ul");
267
- list.className = "audit";
268
- for (const binding of result.bindings ?? []) {
269
- const combination = [...binding.modifiers, binding.key].join("+");
270
- list.append(Object.assign(document.createElement("li"), { textContent: `${binding.command}: ${binding.display ?? combination}` }));
271
- }
272
- shortcutlistnode.append(list);
273
- } catch (error) { shortcutlistnode.textContent = error instanceof Error ? error.message : String(error); }
274
- }
275
-
276
- /** Saves one edited shortcut binding from its "command: combination" text. */
277
- async function saveshortcut(): Promise<void> {
278
- const text = shortcuteditinput?.value.trim() ?? "";
279
- const split = text.split(":");
280
- const command = split[0]?.trim() ?? "";
281
- const combination = split.slice(1).join(":").trim();
282
- if (command === "" || combination === "") { status("The shortcut edit needs its command and its combination, such as pauserun: alt+q.", true); return; }
283
- await request({ kind: "views", shortcut: { edit: { command, text: combination } } });
284
- status(`The ${command} shortcut now binds ${combination}; the command keeps its palette gates.`);
285
- await rendershortcuts();
286
- }
287
-
288
- /** Renders the featuretour stops so the replay walkthrough reads its route first. */
289
- async function rendertourstops(): Promise<void> {
290
- if (!tourstopsnode) return;
291
- try {
292
- const result = await request({ kind: "views", tour: { stops: true } }) as { stops?: Array<{ id: string; surface: string; title: string; body: string }> };
293
- tourstopsnode.replaceChildren();
294
- const list = document.createElement("ol");
295
- list.className = "audit";
296
- for (const stop of result.stops ?? []) list.append(Object.assign(document.createElement("li"), { textContent: `${stop.title} (${stop.surface}): ${stop.body}` }));
297
- tourstopsnode.append(list);
298
- } catch (error) { tourstopsnode.textContent = error instanceof Error ? error.message : String(error); }
299
- }
300
-
301
- /** Exports the settings bundle and renders its honest exclusion list. */
302
- async function exportbundle(): Promise<void> {
303
- const result = await request({ kind: "views", importexport: { export: true } }) as { payload: { profile: string; exclusions: string[]; contents: { originprofiles: unknown[]; siteprofiles: unknown[]; notes: unknown[] } } };
304
- if (bundlenode) bundlenode.textContent = `The bundle of the profile ${result.payload.profile} carries ${result.payload.contents.siteprofiles.length} site profile${result.payload.contents.siteprofiles.length === 1 ? "" : "s"} and ${result.payload.contents.notes.length} note${result.payload.contents.notes.length === 1 ? "" : "s"}; ${result.payload.exclusions.join(" and ")} never enter any bundle.`;
305
- status("The settings bundle exported with its exclusion list.");
306
- }
307
-
308
- /** Validates one pasted bundle and applies its preferences when the validation passes. */
309
- async function importbundle(): Promise<void> {
310
- const text = window.prompt("Paste the importexport bundle json") ?? "";
311
- if (text.trim() === "") return;
312
- const parsed = JSON.parse(text) as Record<string, unknown>;
313
- const validation = await request({ kind: "views", importexport: { validate: parsed } }) as { ok: boolean; reason: string };
314
- if (!validation.ok) { status(validation.reason, true); return; }
315
- const applied = await request({ kind: "views", importexport: { apply: { preferences: (parsed as { contents?: { preferences?: Record<string, unknown> } }).contents?.preferences ?? {} } } }) as { applied: string[] };
316
- status(`The bundle applied ${applied.applied.length} preference key${applied.applied.length === 1 ? "" : "s"} after its validation passed.`);
317
- await loadfinishing();
318
- }
319
-
320
- /** Accepts the csv, json and workflow files the user drops on the dropimport zone. */
321
- function wir(dropimportzone: HTMLElement): void {
322
- dropimportzone.addEventListener("dragover", event => { event.preventDefault(); });
323
- dropimportzone.addEventListener("drop", event => {
324
- event.preventDefault();
325
- const file = event.dataTransfer?.files[0];
326
- if (file === undefined) return;
327
- void file.text().then(head => request({ kind: "views", dropimport: { file: { filename: file.name, bytes: file.size, head: head.slice(0, 2000) } } })).then(result => {
328
- const session = (result as { session: { filename: string; kind: string } }).session;
329
- status(`The dropimport detected the ${session.kind} kind of ${session.filename}; the import path takes the file from here.`);
330
- }).catch(error => status(error instanceof Error ? error.message : String(error), true));
331
- });
332
- }
333
- if (dropzonenode) wir(dropzonenode);
334
-
335
- document.querySelector<HTMLButtonElement>("#finishingsettings")?.addEventListener("click", () => { void savefinishing().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
336
- document.querySelector<HTMLButtonElement>("#shortcutsettings")?.addEventListener("click", () => { void saveshortcut().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
337
- document.querySelector<HTMLButtonElement>("#exportbundle")?.addEventListener("click", () => { void exportbundle().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
338
- document.querySelector<HTMLButtonElement>("#importbundle")?.addEventListener("click", () => { void importbundle().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
339
- document.querySelector<HTMLButtonElement>("#replaytour")?.addEventListener("click", () => { void (async () => { await request({ kind: "views", tour: { replay: true } }); status("The featuretour replays with its datagrid, compareviewer and pickeroverlay stops."); })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
340
- document.querySelector<HTMLButtonElement>("#recalltips")?.addEventListener("click", () => { void (async () => { await request({ kind: "views", tips: { recall: true } }); status("The guidedtips recalled; they show again during the picker sessions."); })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
341
- themepreferenceselect?.addEventListener("change", () => { void applythemelive().catch(() => { /* a failing theme read keeps the shipped light theme */ }); });
342
- contrastpreferenceselect?.addEventListener("change", () => { void (async () => { await savecontrastpreference(contrastpreferenceselect?.value === "high" ? "high" : "default"); await applythemelive(); })().catch(() => { /* a failing preference write keeps the shipped tokens */ }); });
343
-
344
- void loadfinishing();
345
- void applythemelive();
346
- void rendershortcuts();
347
- void rendertourstops();
348
-
349
- /**
350
- * Syncbridge options of the 1.1.66 ecosystem family: the optionspage manages the manifest sync providers and their explicit opt in state and shows the conflict records with their resolve actions; no hook ever defaults on.
351
- */
352
- const syncproviderlist = document.querySelector<HTMLElement>("#syncproviderlist");
353
- const syncconflictsroot = document.querySelector<HTMLElement>("#syncconflicts");
354
- const syncendpointinput = document.querySelector<HTMLInputElement>("#syncendpoint");
355
- const syncproviderselect = document.querySelector<HTMLSelectElement>("#syncprovider");
356
-
357
- /** Renders the syncbridge providers and hooks with their opt in states beside the open conflict records. */
358
- async function rendersyncbridge(): Promise<void> {
359
- if (syncproviderlist) {
360
- try {
361
- const providers = await request({ kind: "ecosystem", sync: { providers: true } }) as { providers: Array<{ provider: string; label: string; note: string }> };
362
- const hooks = await request({ kind: "ecosystem", sync: { hooks: true } }) as { hooks: Array<{ id: string; provider: string; direction: string; optin: boolean; endpoint: string; state: string }> };
363
- syncproviderlist.replaceChildren();
364
- for (const provider of providers.providers) {
365
- const row = document.createElement("p");
366
- row.textContent = `${provider.label}: ${provider.note}`;
367
- syncproviderlist.append(row);
368
- }
369
- for (const hook of hooks.hooks) {
370
- const row = document.createElement("p");
371
- row.textContent = `${hook.provider} hook — ${hook.endpoint} — ${hook.direction} — ${hook.optin ? "opted in" : "opted out"} — ${hook.state}`;
372
- row.append(" ", (() => { const element = document.createElement("button"); element.type = "button"; element.className = "secondary"; element.textContent = hook.optin ? "Opt out" : "Opt in"; element.addEventListener("click", () => { void request({ kind: "ecosystem", sync: { hook: { optin: { hookid: hook.id, optin: !hook.optin } } } }).then(() => rendersyncbridge()).catch(error => status(error instanceof Error ? error.message : String(error), true)); }); return element; })());
373
- syncproviderlist.append(row);
374
- }
375
- } catch (error) { syncproviderlist.textContent = error instanceof Error ? error.message : String(error); }
376
- }
377
- if (syncconflictsroot) {
378
- try {
379
- const conflicts = await request({ kind: "ecosystem", sync: { conflicts: true } }) as { conflicts: Array<{ id: string; manifestid: string; local: { version: string }; remote: { version: string }; resolution?: string }> };
380
- syncconflictsroot.replaceChildren();
381
- if (conflicts.conflicts.length === 0) { syncconflictsroot.textContent = "No syncbridge conflict stands open; both versions surface here whenever digests differ."; return; }
382
- for (const conflict of conflicts.conflicts) {
383
- const row = document.createElement("p");
384
- row.textContent = `${conflict.manifestid}: local ${conflict.local.version} vs remote ${conflict.remote.version}${conflict.resolution !== undefined ? ` — resolved ${conflict.resolution}` : ""}`;
385
- if (conflict.resolution === undefined) for (const resolution of ["local", "remote", "merge"] as const) {
386
- const element = document.createElement("button");
387
- element.type = "button";
388
- element.className = "secondary";
389
- element.textContent = resolution;
390
- element.addEventListener("click", () => { void request({ kind: "ecosystem", sync: { resolve: { conflictid: conflict.id, resolution } } }).then(() => rendersyncbridge()).catch(error => status(error instanceof Error ? error.message : String(error), true)); });
391
- row.append(" ", element);
392
- }
393
- syncconflictsroot.append(row);
394
- }
395
- } catch (error) { syncconflictsroot.textContent = error instanceof Error ? error.message : String(error); }
396
- }
397
- }
398
-
399
- document.querySelector<HTMLButtonElement>("#synccreate")?.addEventListener("click", () => { void (async () => {
400
- const endpoint = syncendpointinput?.value.trim() ?? "";
401
- if (endpoint === "") throw new Error("The syncbridge hook needs its user configured endpoint; no provider address is ever hardcoded.");
402
- await request({ kind: "ecosystem", sync: { hook: { add: { provider: syncproviderselect?.value === "web" ? "web" : "file", direction: "both", endpoint } } } });
403
- if (syncendpointinput) syncendpointinput.value = "";
404
- status(`The syncbridge hook of ${endpoint} was added opted out; no hook ever defaults on.`);
405
- await rendersyncbridge();
406
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
407
-
408
- void rendersyncbridge();
409
- /**
410
- * Model provider gateway options of the 1.1.83 family: the optionspage manages the baseurlconfig of every provider adapter (the base url the user typed, the optional path prefix of gateway deployments and the enable toggle that ships disabled), the consent stamp of the first remote call, the api key behind the vault seam with its explicit consent and one click revocation and the model choice of the discovered model list; no cloud default ever applies and the ollamalocal adapter alone carries the localhost default.
411
- */
412
- const gatewayproviderlist = document.querySelector<HTMLElement>("#gatewayproviderlist");
413
- const gatewaykindselect = document.querySelector<HTMLSelectElement>("#gatewaykind");
414
- const gatewayidinput = document.querySelector<HTMLInputElement>("#gatewayid");
415
- const gatewaybaseinput = document.querySelector<HTMLInputElement>("#gatewaybase");
416
- const gatewayprefixinput = document.querySelector<HTMLInputElement>("#gatewayprefix");
417
- const gatewaykeyinput = document.querySelector<HTMLInputElement>("#gatewaykey");
418
-
419
- /** Renders the provider gateways with their base urls, enable toggles, consent stamps, key references and discovered model lists. */
420
- async function rendergateways(): Promise<void> {
421
- if (!gatewayproviderlist) return;
422
- try {
423
- const view = await request({ kind: "gateway", view: true }) as { configs: Array<{ providerid: string; kind: string; baseurl: string; pathprefix?: string; enabled: boolean; consented?: boolean; keyref?: { name: string }; local: boolean; costpermilliontokens?: number }>; models: Array<{ providerid: string; models: Array<{ id: string; contextwindow?: number; modalities?: string[] }>; fetchedat: number }>; chat: { provider?: string; model?: string; source: string; reason?: string } };
424
- gatewayproviderlist.replaceChildren();
425
- if (view.configs.length === 0) { gatewayproviderlist.textContent = "No provider gateway configured yet; add one below — every provider starts disabled and no cloud default applies."; return; }
426
- for (const config of view.configs) {
427
- const row = document.createElement("p");
428
- const models = view.models.find(entry => entry.providerid === config.providerid);
429
- row.textContent = `${config.providerid} (${config.kind}) — ${config.baseurl}${config.pathprefix !== undefined ? ` under ${config.pathprefix}` : ""} — ${config.enabled ? "enabled" : "disabled"}${config.consented === true ? ", consented" : config.local ? ", local runtime" : ", consent pending"}${config.keyref !== undefined ? `, key ${config.keyref.name} in the vault` : ", no key"}${models !== undefined ? `, ${models.models.length} model${models.models.length === 1 ? "" : "s"} discovered` : ""}.`;
430
- const toggle = document.createElement("button");
431
- toggle.type = "button";
432
- toggle.className = "secondary";
433
- toggle.textContent = config.enabled ? "Disable" : "Enable";
434
- toggle.addEventListener("click", () => { void request({ kind: "gateway", config: { providerid: config.providerid, kind: config.kind, baseurl: config.baseurl, ...(config.pathprefix !== undefined ? { pathprefix: config.pathprefix } : {}), enabled: !config.enabled } }).then(() => rendergateways()).catch(error => status(error instanceof Error ? error.message : String(error), true)); });
435
- row.append(" ", toggle);
436
- if (!config.local) {
437
- const consent = document.createElement("button");
438
- consent.type = "button";
439
- consent.className = "secondary";
440
- consent.textContent = config.consented === true ? "Withdraw consent" : "Consent to remote calls";
441
- consent.addEventListener("click", () => { void request({ kind: "gateway", consent: { providerid: config.providerid, grant: config.consented !== true } }).then(() => rendergateways()).catch(error => status(error instanceof Error ? error.message : String(error), true)); });
442
- row.append(" ", consent);
443
- }
444
- const refresh = document.createElement("button");
445
- refresh.type = "button";
446
- refresh.className = "secondary";
447
- refresh.textContent = "Refresh models";
448
- refresh.addEventListener("click", () => { void request({ kind: "gateway", models: { providerid: config.providerid, refresh: true } }).then(() => rendergateways()).catch(error => status(error instanceof Error ? error.message : String(error), true)); });
449
- row.append(" ", refresh);
450
- if (config.keyref !== undefined) {
451
- const revoke = document.createElement("button");
452
- revoke.type = "button";
453
- revoke.className = "secondary";
454
- revoke.textContent = "Revoke key";
455
- revoke.addEventListener("click", () => { void request({ kind: "gateway", key: { providerid: config.providerid, revoke: true } }).then(() => rendergateways()).catch(error => status(error instanceof Error ? error.message : String(error), true)); });
456
- row.append(" ", revoke);
457
- }
458
- const remove = document.createElement("button");
459
- remove.type = "button";
460
- remove.className = "secondary";
461
- remove.textContent = "Remove";
462
- remove.addEventListener("click", () => { void request({ kind: "gateway", config: { providerid: config.providerid, kind: config.kind, remove: true } }).then(() => rendergateways()).catch(error => status(error instanceof Error ? error.message : String(error), true)); });
463
- row.append(" ", remove);
464
- gatewayproviderlist.append(row);
465
- }
466
- const route = document.createElement("p");
467
- route.textContent = view.chat.provider !== undefined ? `The chat task kind routes to ${view.chat.provider}${view.chat.model !== undefined ? ` with ${view.chat.model}` : ""} (${view.chat.source}).` : `The chat task kind stays unrouted — ${view.chat.reason ?? "the user picks the pair from the models section of the sidepanel."}`;
468
- gatewayproviderlist.append(route);
469
- } catch (error) { gatewayproviderlist.textContent = error instanceof Error ? error.message : String(error); }
470
- }
471
-
472
- document.querySelector<HTMLButtonElement>("#gatewaycreate")?.addEventListener("click", () => { void (async () => {
473
- const providerid = gatewayidinput?.value.trim() ?? "";
474
- const kind = gatewaykindselect?.value ?? "openaicompat";
475
- const baseurl = gatewaybaseinput?.value.trim() ?? "";
476
- const pathprefix = gatewayprefixinput?.value.trim() ?? "";
477
- const keyvalue = gatewaykeyinput?.value ?? "";
478
- if (providerid === "") throw new Error("The provider gateway needs its provider id.");
479
- await request({ kind: "gateway", config: { providerid, kind, baseurl, ...(pathprefix !== "" ? { pathprefix } : {}), enabled: false } });
480
- if (keyvalue !== "") await request({ kind: "gateway", key: { providerid, value: keyvalue, consent: true } });
481
- if (gatewayidinput) gatewayidinput.value = "";
482
- if (gatewaybaseinput) gatewaybaseinput.value = "";
483
- if (gatewayprefixinput) gatewayprefixinput.value = "";
484
- if (gatewaykeyinput) gatewaykeyinput.value = "";
485
- status(`Added the ${kind} provider gateway ${providerid}; it starts disabled and the key ${keyvalue !== "" ? "stores behind the vault seam" : "stays unset"}.`);
486
- await rendergateways();
487
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
488
-
489
- void rendergateways();
490
-
491
- document.querySelector<HTMLButtonElement>("#perfsettings")?.addEventListener("click", () => { void saveperf().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
492
- document.querySelector<HTMLButtonElement>("#schedulesettings")?.addEventListener("click", () => { void saveschedule().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
493
-
494
- /**
495
- * Site bridge options of the 1.1.82 family: the relay url stays the user's setting with no default (an empty value disables the bridge completely), the consent gate asks before the first socket connection, the page content consent flag stays off by default so plan text and statuses only cross, the idle window, the heartbeat interval, the rate cap and the pairing lifetime stay user choices, and the pairing panel shows the code with its expiry countdown while the revoke all and the kill switch close everything instantly.
496
- */
497
- const bridgestatusline = document.querySelector<HTMLElement>("#bridgestatusline");
498
-
499
- async function renderbridgeoptions(): Promise<void> {
500
- if (!bridgestatusline) return;
501
- try {
502
- const view = await request({ kind: "bridge", view: true }) as { status: { status: string; paired: boolean; queued: number }; pairing?: { code: string; countdown: { secondsleft: number; expired: boolean; label: string } }; tokens: { live: number }; queue: { depth: number }; killswitch: boolean; consent: boolean; settings: { serverurl?: string; bridgeidlewindow?: number; bridgeheartbeatinterval?: number; bridgeratelimit?: number; bridgeratewindow?: number; bridgepairinglifetime?: number; bridgepageconsent?: boolean } };
503
- const urlinput = document.querySelector<HTMLInputElement>("#serverurl");
504
- if (urlinput && document.activeElement !== urlinput) urlinput.value = view.settings.serverurl ?? "";
505
- const consentinput = document.querySelector<HTMLInputElement>("#bridgeconsent");
506
- if (consentinput) consentinput.checked = view.consent;
507
- const pageconsentinput = document.querySelector<HTMLInputElement>("#bridgepageconsent");
508
- if (pageconsentinput) pageconsentinput.checked = view.settings.bridgepageconsent === true;
509
- const numberof = (id: string, value: number | undefined): void => { const node = document.querySelector<HTMLInputElement>(id); if (node && document.activeElement !== node) node.value = value !== undefined ? String(value) : ""; };
510
- numberof("#bridgeidlewindow", view.settings.bridgeidlewindow);
511
- numberof("#bridgeheartbeatinterval", view.settings.bridgeheartbeatinterval);
512
- numberof("#bridgeratelimit", view.settings.bridgeratelimit);
513
- numberof("#bridgeratewindow", view.settings.bridgeratewindow);
514
- numberof("#bridgepairinglifetime", view.settings.bridgepairinglifetime);
515
- const killbutton = document.querySelector<HTMLButtonElement>("#bridgekill");
516
- if (killbutton) killbutton.textContent = view.killswitch ? "Release kill switch" : "Kill switch";
517
- bridgestatusline.textContent = `Bridge: ${view.status.status}${view.status.paired ? " and paired" : ""} · ${view.tokens.live} live token${view.tokens.live === 1 ? "" : "s"} · ${view.queue.depth} queued frame${view.queue.depth === 1 ? "" : "s"}${view.pairing !== undefined ? ` · pairing ${view.pairing.code} ${view.pairing.countdown.label}` : " · no pairing code minted"}.`;
518
- } catch (error) { bridgestatusline.textContent = error instanceof Error ? error.message : String(error); }
519
- }
520
-
521
- document.querySelector<HTMLButtonElement>("#bridgesettings")?.addEventListener("click", () => { void (async () => {
522
- const numberof = (id: string): number | undefined => { const raw = document.querySelector<HTMLInputElement>(id)?.value.trim() ?? ""; return raw !== "" && Number.isFinite(Number(raw)) ? Number(raw) : undefined; };
523
- const url = document.querySelector<HTMLInputElement>("#serverurl")?.value.trim() ?? "";
524
- await request({ kind: "bridge", settings: { ...(url !== "" ? { serverurl: url } : { serverurl: "" }), ...(numberof("#bridgeidlewindow") !== undefined ? { bridgeidlewindow: numberof("#bridgeidlewindow") } : {}), ...(numberof("#bridgeheartbeatinterval") !== undefined ? { bridgeheartbeatinterval: numberof("#bridgeheartbeatinterval") } : {}), ...(numberof("#bridgeratelimit") !== undefined ? { bridgeratelimit: numberof("#bridgeratelimit") } : {}), ...(numberof("#bridgeratewindow") !== undefined ? { bridgeratewindow: numberof("#bridgeratewindow") } : {}), ...(numberof("#bridgepairinglifetime") !== undefined ? { bridgepairinglifetime: numberof("#bridgepairinglifetime") } : {}), ...(document.querySelector<HTMLInputElement>("#bridgepageconsent") !== undefined ? { bridgepageconsent: document.querySelector<HTMLInputElement>("#bridgepageconsent")?.checked === true } : {}) } });
525
- status("The site bridge options saved; the relay url stays your setting with no default and an empty value disables the bridge completely.");
526
- await renderbridgeoptions();
527
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
528
-
529
- document.querySelector<HTMLButtonElement>("#bridgeconsent")?.addEventListener("change", () => { void (async () => {
530
- const granted = document.querySelector<HTMLInputElement>("#bridgeconsent")?.checked === true;
531
- await request({ kind: "bridge", consent: granted ? { grant: true } : { revoke: true } });
532
- status(granted ? "The bridge consent is granted; the first socket connection may open." : "The bridge consent is revoked; the socket closed and no further connection opens.");
533
- await renderbridgeoptions();
534
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
535
-
536
- document.querySelector<HTMLButtonElement>("#bridgeconnect")?.addEventListener("click", () => { void (async () => {
537
- const granted = document.querySelector<HTMLInputElement>("#bridgeconsent")?.checked === true;
538
- if (granted) await request({ kind: "bridge", consent: { grant: true } }).catch(() => { /* an already granted consent records no second event */ });
539
- await request({ kind: "bridge", connect: true });
540
- status("The bridge socket connected behind the consent gate; the pairing code of the origin rides the session create frame.");
541
- await renderbridgeoptions();
542
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
543
-
544
- document.querySelector<HTMLButtonElement>("#bridgedisconnect")?.addEventListener("click", () => { void (async () => {
545
- await request({ kind: "bridge", disconnect: { reason: "The user closed the bridge socket from the options." } });
546
- status("The bridge socket closed; the offline queue buffers every further frame for the replay.");
547
- await renderbridgeoptions();
548
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
549
-
550
- document.querySelector<HTMLButtonElement>("#bridgepair")?.addEventListener("click", () => { void (async () => {
551
- const minted = await request({ kind: "bridge", pair: {} }) as { minted: { code: string; countdown: { label: string } } };
552
- status(`The pairing code ${minted.minted.code} was minted inside the extension options; ${minted.minted.countdown.label}`);
553
- await renderbridgeoptions();
554
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
555
-
556
- document.querySelector<HTMLButtonElement>("#bridgerevoke")?.addEventListener("click", () => { void (async () => {
557
- await request({ kind: "bridge", revoke: { all: true } });
558
- status("Every relay session revoked with one click; the socket closed instantly and no frame passes afterwards.");
559
- await renderbridgeoptions();
560
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
561
-
562
- document.querySelector<HTMLButtonElement>("#bridgekill")?.addEventListener("click", () => { void (async () => {
563
- const view = await request({ kind: "bridge", view: true }) as { killswitch: boolean };
564
- await request({ kind: "bridge", killswitch: view.killswitch ? { release: true } : { engage: true } });
565
- status(view.killswitch ? "The bridge kill switch released; the connect opens the socket again behind the consent gate." : "The bridge kill switch engaged; the socket and the pairing disabled instantly.");
566
- await renderbridgeoptions();
567
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
568
-
569
- void renderbridgeoptions();
570
-
571
- /**
572
- * Native host bridge options of the 1.1.85 family: the panel shows the install state and the versions the handshake reported, offers the install consent that explains the scope before any host registration (the manifest write itself runs through the reviewed cli installer because the extension never writes the profile), the per class and per surface consents, the attach and the detach of the native port, the diagnostics report, the kill switch and the escape hatch key that stops every native call in one press; the idle window, the heartbeat interval and the rate cap stay user choices with no default.
573
- */
574
- const nativestatusline = document.querySelector<HTMLElement>("#nativestatusline");
575
-
576
- async function rendernativeoptions(): Promise<void> {
577
- if (!nativestatusline) return;
578
- try {
579
- const view = await request({ kind: "native", view: true }) as { state: { installed: boolean; port: string; hostname?: string; companionversion?: string; companionprotocol?: string; installerversion?: string; wsbridgeport?: number }; diagnostics: { degraded: { degraded: boolean; reason: string } }; killswitch: boolean; installconsent: boolean; transportconsent: boolean; classconsents: string[]; surfaceconsents: string[]; calls: number; settings: { nativeidlewindow?: number; nativeheartbeatinterval?: number; nativecallratelimit?: number; nativecallratewindow?: number } };
580
- const checkboxof = (id: string, value: boolean): void => { const node = document.querySelector<HTMLInputElement>(id); if (node) node.checked = value; };
581
- checkboxof("#nativeinstallconsent", view.installconsent);
582
- checkboxof("#nativetransportconsent", view.transportconsent);
583
- checkboxof("#nativereadconsent", view.classconsents.includes("read"));
584
- checkboxof("#nativeinteractionconsent", view.classconsents.includes("interaction"));
585
- checkboxof("#nativesensitiveconsent", view.classconsents.includes("sensitive"));
586
- checkboxof("#nativeosdialogconsent", view.surfaceconsents.includes("osdialog"));
587
- checkboxof("#nativenotificationconsent", view.surfaceconsents.includes("notification"));
588
- const numberof = (id: string, value: number | undefined): void => { const node = document.querySelector<HTMLInputElement>(id); if (node && document.activeElement !== node) node.value = value !== undefined ? String(value) : ""; };
589
- numberof("#nativeidlewindow", view.settings.nativeidlewindow);
590
- numberof("#nativeheartbeatinterval", view.settings.nativeheartbeatinterval);
591
- numberof("#nativecallratelimit", view.settings.nativecallratelimit);
592
- numberof("#nativecallratewindow", view.settings.nativecallratewindow);
593
- const killbutton = document.querySelector<HTMLButtonElement>("#nativekill");
594
- if (killbutton) killbutton.textContent = view.killswitch ? "Release kill switch" : "Kill switch";
595
- nativestatusline.textContent = `Native bridge: ${view.state.installed ? `installed${view.state.hostname !== undefined ? ` as ${view.state.hostname}` : ""}` : "nothing installed"} · port ${view.state.port} · companion ${view.state.companionversion ?? "absent"} of protocol ${view.state.companionprotocol ?? "none"} · wsbridge port ${view.state.wsbridgeport ?? 0} · ${view.calls} recorded call${view.calls === 1 ? "" : "s"}${view.diagnostics.degraded ? ` · degraded: ${view.diagnostics.degraded.reason}` : ""}.`;
596
- } catch (error) { nativestatusline.textContent = error instanceof Error ? error.message : String(error); }
597
- }
598
-
599
- document.querySelector<HTMLButtonElement>("#nativesettings")?.addEventListener("click", () => { void (async () => {
600
- const numberof = (id: string): number | undefined => { const raw = document.querySelector<HTMLInputElement>(id)?.value.trim() ?? ""; return raw !== "" && Number.isFinite(Number(raw)) ? Number(raw) : undefined; };
601
- await request({ kind: "native", settings: { ...(numberof("#nativeidlewindow") !== undefined ? { nativeidlewindow: numberof("#nativeidlewindow") } : {}), ...(numberof("#nativeheartbeatinterval") !== undefined ? { nativeheartbeatinterval: numberof("#nativeheartbeatinterval") } : {}), ...(numberof("#nativecallratelimit") !== undefined ? { nativecallratelimit: numberof("#nativecallratelimit") } : {}), ...(numberof("#nativecallratewindow") !== undefined ? { nativecallratewindow: numberof("#nativecallratewindow") } : {}) } });
602
- status("The native bridge options saved; the idle window, the heartbeat interval and the rate cap stay your choices with no default.");
603
- await rendernativeoptions();
604
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
605
-
606
- document.querySelector<HTMLButtonElement>("#nativeinstall")?.addEventListener("click", () => { void (async () => {
607
- const hostname = document.querySelector<HTMLInputElement>("#nativehostname")?.value.trim() ?? "";
608
- const profiledir = document.querySelector<HTMLInputElement>("#nativeprofiledir")?.value.trim() ?? "";
609
- await request({ kind: "native", installconsent: { grant: true, hostname, profiledir } });
610
- status(`The install consent for ${hostname} into ${profiledir} is recorded with the scope explained; run the reviewed installer (devthink native install) for the manifest write because the extension never writes your profile.`);
611
- await rendernativeoptions();
612
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
613
-
614
- document.querySelector<HTMLButtonElement>("#nativeuninstall")?.addEventListener("click", () => { void (async () => {
615
- await request({ kind: "native", installconsent: { revoke: true } });
616
- status("The install consent is revoked; the transport detached and the reviewed uninstaller (devthink native uninstall) removes the host manifest and its preferences.");
617
- await rendernativeoptions();
618
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
619
-
620
- for (const [id, callclass] of [["#nativereadconsent", "read"], ["#nativeinteractionconsent", "interaction"], ["#nativesensitiveconsent", "sensitive"]] as Array<[string, "read" | "interaction" | "sensitive"]>) {
621
- document.querySelector<HTMLInputElement>(id)?.addEventListener("change", () => { void (async () => {
622
- const grant = document.querySelector<HTMLInputElement>(id)?.checked === true;
623
- await request({ kind: "native", classconsent: { callclass, grant } });
624
- status(grant ? `The ${callclass} call class is granted over the native transport; the gate asks per class.` : `The ${callclass} call class is revoked over the native transport.`);
625
- await rendernativeoptions();
626
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
627
- }
628
-
629
- for (const [id, surface] of [["#nativeosdialogconsent", "osdialog"], ["#nativenotificationconsent", "notification"]] as Array<[string, "osdialog" | "notification"]>) {
630
- document.querySelector<HTMLInputElement>(id)?.addEventListener("change", () => { void (async () => {
631
- const grant = document.querySelector<HTMLInputElement>(id)?.checked === true;
632
- await request({ kind: "native", surfaceconsent: { surface, grant } });
633
- status(grant ? `The ${surface} surface holds its own consent grant; a desktop surface never opens from a class grant alone.` : `The ${surface} surface consent is revoked.`);
634
- await rendernativeoptions();
635
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
636
- }
637
-
638
- document.querySelector<HTMLInputElement>("#nativetransportconsent")?.addEventListener("change", () => { void (async () => {
639
- const grant = document.querySelector<HTMLInputElement>("#nativetransportconsent")?.checked === true;
640
- await request({ kind: "native", transportconsent: grant ? { grant: true } : { revoke: true } });
641
- status(grant ? "The consent to the first native host attach is granted; the attach may open behind the remaining gates." : "The native transport consent is revoked; the port detached and the transport stays deny by default.");
642
- await rendernativeoptions();
643
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
644
-
645
- document.querySelector<HTMLButtonElement>("#nativeattach")?.addEventListener("click", () => { void (async () => {
646
- const hostname = document.querySelector<HTMLInputElement>("#nativehostname")?.value.trim() ?? "";
647
- await request({ kind: "native", attach: { ...(hostname !== "" ? { hostname } : {}) } });
648
- status("The native port attached behind the consent gates; the handshake frame rode the port and the diagnostics report the versions.");
649
- await rendernativeoptions();
650
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
651
-
652
- document.querySelector<HTMLButtonElement>("#nativedetach")?.addEventListener("click", () => { void (async () => {
653
- await request({ kind: "native", detach: { reason: "The user closed the native transport from the options." } });
654
- status("The native port detached; the install state persists and the next attach reuses it.");
655
- await rendernativeoptions();
656
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
657
-
658
- document.querySelector<HTMLButtonElement>("#nativediagnostics")?.addEventListener("click", () => { void (async () => {
659
- await request({ kind: "native", diagnostics: true });
660
- status("The native diagnostics ran: the port state, the versions and the last errors report in the status line above.");
661
- await rendernativeoptions();
662
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
663
-
664
- document.querySelector<HTMLButtonElement>("#nativekill")?.addEventListener("click", () => { void (async () => {
665
- const view = await request({ kind: "native", view: true }) as { killswitch: boolean };
666
- await request({ kind: "native", killswitch: view.killswitch ? { release: true } : { engage: true } });
667
- status(view.killswitch ? "The native transport kill switch released; the attach opens again behind the consent gates." : "The native transport kill switch engaged: the port detached and every native call stops instantly.");
668
- await rendernativeoptions();
669
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
670
-
671
- document.querySelector<HTMLButtonElement>("#nativeescape")?.addEventListener("click", () => { void (async () => {
672
- const answer = await request({ kind: "native", escapehatch: { pressed: true } }) as { banner?: { title: string; explanation: string; halted: number } };
673
- status("The escape hatch key stopped every native call in one press; the transport detached until the user reattaches.");
674
- /* the 1.1.95 hardening: the escape hatch shows its visible confirmation banner, because a one press halt of every native call deserves a confirmation the user sees */
675
- const banner = document.querySelector<HTMLElement>("#nativebanner");
676
- if (banner) {
677
- banner.replaceChildren();
678
- if (answer.banner !== undefined) {
679
- const title = document.createElement("p");
680
- title.textContent = answer.banner.title;
681
- const explanation = document.createElement("p");
682
- explanation.textContent = answer.banner.explanation;
683
- banner.append(title, explanation);
684
- banner.hidden = false;
685
- } else banner.hidden = true;
686
- }
687
- await rendernativeoptions();
688
- })().catch(error => status(error instanceof Error ? error.message : String(error), true)); });
689
-
690
- void rendernativeoptions();
691
-