waybetter 0.1.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (537) hide show
  1. package/README.md +139 -6
  2. package/bin/waybetter.mjs +43 -0
  3. package/build/runtime.mjs +105 -0
  4. package/cli/buildenv.mjs +130 -0
  5. package/cli/dev.mjs +97 -0
  6. package/cli/i18n.mjs +382 -0
  7. package/cli/manifests.mjs +383 -0
  8. package/cli/release.mjs +437 -0
  9. package/cli/releasecheck.mjs +15 -0
  10. package/cli/remote.mjs +260 -0
  11. package/cli/store.mjs +378 -0
  12. package/cli/strings.mjs +123 -0
  13. package/dist/runtime/column.d.ts +93 -0
  14. package/dist/runtime/column.js +451 -0
  15. package/dist/runtime/column.js.map +1 -0
  16. package/dist/runtime/commandReport.d.ts +19 -0
  17. package/dist/runtime/commandReport.js +47 -0
  18. package/dist/runtime/commandReport.js.map +1 -0
  19. package/dist/runtime/commands.d.ts +72 -0
  20. package/dist/runtime/commands.js +214 -0
  21. package/dist/runtime/commands.js.map +1 -0
  22. package/dist/runtime/devErrors.d.ts +16 -0
  23. package/dist/runtime/devErrors.js +30 -0
  24. package/dist/runtime/devErrors.js.map +1 -0
  25. package/dist/runtime/domHub.d.ts +21 -0
  26. package/dist/runtime/domHub.js +139 -4
  27. package/dist/runtime/domHub.js.map +1 -1
  28. package/dist/runtime/featureModules.d.ts +157 -0
  29. package/dist/runtime/featureModules.js +484 -0
  30. package/dist/runtime/featureModules.js.map +1 -0
  31. package/dist/runtime/featureTable.d.ts +144 -0
  32. package/dist/runtime/featureTable.js +223 -0
  33. package/dist/runtime/featureTable.js.map +1 -0
  34. package/dist/runtime/fonts.d.ts +22 -0
  35. package/dist/runtime/fonts.js +72 -0
  36. package/dist/runtime/fonts.js.map +1 -0
  37. package/dist/runtime/i18n.d.ts +138 -0
  38. package/dist/runtime/i18n.js +614 -0
  39. package/dist/runtime/i18n.js.map +1 -0
  40. package/dist/runtime/i18nCheck.d.ts +30 -0
  41. package/dist/runtime/i18nCheck.js +224 -0
  42. package/dist/runtime/i18nCheck.js.map +1 -0
  43. package/dist/runtime/pageLang.d.ts +8 -0
  44. package/dist/runtime/pageLang.js +64 -0
  45. package/dist/runtime/pageLang.js.map +1 -0
  46. package/dist/runtime/pageStrings.d.ts +35 -0
  47. package/dist/runtime/pageStrings.js +76 -0
  48. package/dist/runtime/pageStrings.js.map +1 -0
  49. package/dist/runtime/panelButton.d.ts +33 -0
  50. package/dist/runtime/panelButton.js +83 -0
  51. package/dist/runtime/panelButton.js.map +1 -0
  52. package/dist/runtime/permissions.d.ts +47 -0
  53. package/dist/runtime/permissions.js +141 -0
  54. package/dist/runtime/permissions.js.map +1 -0
  55. package/dist/runtime/pluginApi.d.ts +51 -2
  56. package/dist/runtime/pluginApi.js +137 -22
  57. package/dist/runtime/pluginApi.js.map +1 -1
  58. package/dist/runtime/pluginI18n.d.ts +29 -0
  59. package/dist/runtime/pluginI18n.js +119 -0
  60. package/dist/runtime/pluginI18n.js.map +1 -0
  61. package/dist/runtime/settingsForm.d.ts +66 -0
  62. package/dist/runtime/settingsForm.js +114 -0
  63. package/dist/runtime/settingsForm.js.map +1 -0
  64. package/dist/runtime/site.d.ts +5 -0
  65. package/dist/runtime/site.js +1 -1
  66. package/dist/runtime/site.js.map +1 -1
  67. package/dist/runtime/siteRuntime.d.ts +144 -0
  68. package/dist/runtime/siteRuntime.js +175 -0
  69. package/dist/runtime/siteRuntime.js.map +1 -0
  70. package/dist/runtime/stringPackHook.d.ts +84 -0
  71. package/dist/runtime/stringPackHook.js +220 -0
  72. package/dist/runtime/stringPackHook.js.map +1 -0
  73. package/dist/runtime/stringPackTranslate.d.ts +61 -0
  74. package/dist/runtime/stringPackTranslate.js +267 -0
  75. package/dist/runtime/stringPackTranslate.js.map +1 -0
  76. package/dist/runtime/stringPacks.d.ts +248 -0
  77. package/dist/runtime/stringPacks.js +610 -0
  78. package/dist/runtime/stringPacks.js.map +1 -0
  79. package/dist/runtime/styles.d.ts +15 -0
  80. package/dist/runtime/styles.js +81 -0
  81. package/dist/runtime/styles.js.map +1 -0
  82. package/dist/runtime/textNet.d.ts +15 -0
  83. package/dist/runtime/textNet.js +131 -0
  84. package/dist/runtime/textNet.js.map +1 -0
  85. package/dist/runtime/themeApi.d.ts +121 -0
  86. package/dist/runtime/themeApi.js +220 -0
  87. package/dist/runtime/themeApi.js.map +1 -0
  88. package/dist/runtime/themeBar.d.ts +28 -0
  89. package/dist/runtime/themeBar.js +82 -0
  90. package/dist/runtime/themeBar.js.map +1 -0
  91. package/dist/runtime/themeChoice.d.ts +52 -0
  92. package/dist/runtime/themeChoice.js +69 -0
  93. package/dist/runtime/themeChoice.js.map +1 -0
  94. package/dist/runtime/themeComponents.d.ts +435 -0
  95. package/dist/runtime/themeComponents.js +476 -0
  96. package/dist/runtime/themeComponents.js.map +1 -0
  97. package/dist/runtime/themeDecor.d.ts +73 -0
  98. package/dist/runtime/themeDecor.js +133 -0
  99. package/dist/runtime/themeDecor.js.map +1 -0
  100. package/dist/runtime/themeDerive.d.ts +69 -0
  101. package/dist/runtime/themeDerive.js +117 -0
  102. package/dist/runtime/themeDerive.js.map +1 -0
  103. package/dist/runtime/themeDoctor.d.ts +52 -0
  104. package/dist/runtime/themeDoctor.js +112 -0
  105. package/dist/runtime/themeDoctor.js.map +1 -0
  106. package/dist/runtime/themeGuards.d.ts +60 -0
  107. package/dist/runtime/themeGuards.js +235 -0
  108. package/dist/runtime/themeGuards.js.map +1 -0
  109. package/dist/runtime/themeIcons.d.ts +130 -0
  110. package/dist/runtime/themeIcons.js +271 -0
  111. package/dist/runtime/themeIcons.js.map +1 -0
  112. package/dist/runtime/themeInk.d.ts +37 -0
  113. package/dist/runtime/themeInk.js +111 -0
  114. package/dist/runtime/themeInk.js.map +1 -0
  115. package/dist/runtime/themeInvariants.d.ts +39 -0
  116. package/dist/runtime/themeInvariants.js +273 -0
  117. package/dist/runtime/themeInvariants.js.map +1 -0
  118. package/dist/runtime/themeRuntime.d.ts +114 -0
  119. package/dist/runtime/themeRuntime.js +570 -0
  120. package/dist/runtime/themeRuntime.js.map +1 -0
  121. package/dist/runtime/themeSchema.d.ts +10 -0
  122. package/dist/runtime/themeSchema.js +150 -0
  123. package/dist/runtime/themeSchema.js.map +1 -0
  124. package/dist/runtime/themeSpec.d.ts +263 -0
  125. package/dist/runtime/themeSpec.js +435 -0
  126. package/dist/runtime/themeSpec.js.map +1 -0
  127. package/dist/runtime/themeTokens.d.ts +12 -0
  128. package/dist/runtime/themeTokens.js +14 -6
  129. package/dist/runtime/themeTokens.js.map +1 -1
  130. package/dist/runtime/themeVariants.d.ts +30 -0
  131. package/dist/runtime/themeVariants.js +53 -0
  132. package/dist/runtime/themeVariants.js.map +1 -0
  133. package/dist/runtime/ui.d.ts +192 -0
  134. package/dist/runtime/ui.js +926 -0
  135. package/dist/runtime/ui.js.map +1 -0
  136. package/dist/shell/InstallDialog.d.ts +40 -0
  137. package/dist/shell/InstallDialog.js +176 -0
  138. package/dist/shell/InstallDialog.js.map +1 -0
  139. package/dist/shell/StoreView.d.ts +78 -0
  140. package/dist/shell/StoreView.js +108 -0
  141. package/dist/shell/StoreView.js.map +1 -0
  142. package/dist/shell/addressModel.js.map +1 -1
  143. package/dist/shell/chrome.js.map +1 -1
  144. package/dist/shell/deckModel.d.ts +117 -0
  145. package/dist/shell/deckModel.js +267 -0
  146. package/dist/shell/deckModel.js.map +1 -0
  147. package/dist/shell/frame/AccountSwitcher.d.ts +42 -0
  148. package/dist/shell/frame/AccountSwitcher.js +243 -0
  149. package/dist/shell/frame/AccountSwitcher.js.map +1 -0
  150. package/dist/shell/frame/AddColumnDialog.d.ts +8 -0
  151. package/dist/shell/frame/AddColumnDialog.js +81 -0
  152. package/dist/shell/frame/AddColumnDialog.js.map +1 -0
  153. package/dist/shell/frame/AppMenu.d.ts +44 -0
  154. package/dist/shell/frame/AppMenu.js +721 -0
  155. package/dist/shell/frame/AppMenu.js.map +1 -0
  156. package/dist/shell/frame/CommandPalette.d.ts +7 -0
  157. package/dist/shell/frame/CommandPalette.js +118 -0
  158. package/dist/shell/frame/CommandPalette.js.map +1 -0
  159. package/dist/shell/frame/DeckView.d.ts +13 -0
  160. package/dist/shell/frame/DeckView.js +287 -0
  161. package/dist/shell/frame/DeckView.js.map +1 -0
  162. package/dist/shell/frame/FindBar.d.ts +10 -0
  163. package/dist/shell/frame/FindBar.js +108 -0
  164. package/dist/shell/frame/FindBar.js.map +1 -0
  165. package/dist/shell/frame/MediaPanel.d.ts +8 -0
  166. package/dist/shell/frame/MediaPanel.js +76 -0
  167. package/dist/shell/frame/MediaPanel.js.map +1 -0
  168. package/dist/shell/frame/NewTabPage.d.ts +9 -0
  169. package/dist/shell/frame/NewTabPage.js +71 -0
  170. package/dist/shell/frame/NewTabPage.js.map +1 -0
  171. package/dist/shell/frame/PaneTheme.d.ts +16 -0
  172. package/dist/shell/frame/PaneTheme.js +67 -0
  173. package/dist/shell/frame/PaneTheme.js.map +1 -0
  174. package/dist/shell/frame/ReportDialog.d.ts +3 -0
  175. package/dist/shell/frame/ReportDialog.js +95 -0
  176. package/dist/shell/frame/ReportDialog.js.map +1 -0
  177. package/dist/shell/frame/Shell.d.ts +9 -0
  178. package/dist/shell/frame/Shell.js +1139 -0
  179. package/dist/shell/frame/Shell.js.map +1 -0
  180. package/dist/shell/frame/Titlebar.d.ts +17 -0
  181. package/dist/shell/frame/Titlebar.js +79 -0
  182. package/dist/shell/frame/Titlebar.js.map +1 -0
  183. package/dist/shell/frame/TitlebarExtras.d.ts +4 -0
  184. package/dist/shell/frame/TitlebarExtras.js +202 -0
  185. package/dist/shell/frame/TitlebarExtras.js.map +1 -0
  186. package/dist/shell/frame/WebPanel.d.ts +4 -0
  187. package/dist/shell/frame/WebPanel.js +185 -0
  188. package/dist/shell/frame/WebPanel.js.map +1 -0
  189. package/dist/shell/frame/accountModel.d.ts +49 -0
  190. package/dist/shell/frame/accountModel.js +67 -0
  191. package/dist/shell/frame/accountModel.js.map +1 -0
  192. package/dist/shell/frame/accountSwitcher.css +91 -0
  193. package/dist/shell/frame/appI18n.d.ts +44 -0
  194. package/dist/shell/frame/appI18n.js +64 -0
  195. package/dist/shell/frame/appI18n.js.map +1 -0
  196. package/dist/shell/frame/appMenu.css +127 -0
  197. package/dist/shell/frame/backupModel.d.ts +38 -0
  198. package/dist/shell/frame/backupModel.js +192 -0
  199. package/dist/shell/frame/backupModel.js.map +1 -0
  200. package/dist/shell/frame/boot.d.ts +8 -0
  201. package/dist/shell/frame/boot.js +37 -0
  202. package/dist/shell/frame/boot.js.map +1 -0
  203. package/dist/shell/frame/brand.d.ts +1 -0
  204. package/dist/shell/frame/brand.js +4 -0
  205. package/dist/shell/frame/brand.js.map +1 -0
  206. package/dist/shell/frame/bridge.d.ts +35 -0
  207. package/dist/shell/frame/bridge.js +70 -0
  208. package/dist/shell/frame/bridge.js.map +1 -0
  209. package/dist/shell/frame/chromeTheme.d.ts +47 -0
  210. package/dist/shell/frame/chromeTheme.js +118 -0
  211. package/dist/shell/frame/chromeTheme.js.map +1 -0
  212. package/dist/shell/frame/deck.css +209 -0
  213. package/dist/shell/frame/deckStore.d.ts +89 -0
  214. package/dist/shell/frame/deckStore.js +239 -0
  215. package/dist/shell/frame/deckStore.js.map +1 -0
  216. package/dist/shell/frame/devHost.d.ts +35 -0
  217. package/dist/shell/frame/devHost.js +80 -0
  218. package/dist/shell/frame/devHost.js.map +1 -0
  219. package/dist/shell/frame/devModel.d.ts +7 -0
  220. package/dist/shell/frame/devModel.js +17 -0
  221. package/dist/shell/frame/devModel.js.map +1 -0
  222. package/dist/shell/frame/diag.d.ts +7 -0
  223. package/dist/shell/frame/diag.js +39 -0
  224. package/dist/shell/frame/diag.js.map +1 -0
  225. package/dist/shell/frame/edges.css +159 -0
  226. package/dist/shell/frame/findBar.css +14 -0
  227. package/dist/shell/frame/frameMessages.d.ts +248 -0
  228. package/dist/shell/frame/frameMessages.js +252 -0
  229. package/dist/shell/frame/frameMessages.js.map +1 -0
  230. package/dist/shell/frame/hooks.d.ts +39 -0
  231. package/dist/shell/frame/hooks.js +97 -0
  232. package/dist/shell/frame/hooks.js.map +1 -0
  233. package/dist/shell/frame/i18n.d.ts +27 -0
  234. package/dist/shell/frame/i18n.js +66 -0
  235. package/dist/shell/frame/i18n.js.map +1 -0
  236. package/dist/shell/frame/iconPack.d.ts +11 -0
  237. package/dist/shell/frame/iconPack.js +45 -0
  238. package/dist/shell/frame/iconPack.js.map +1 -0
  239. package/dist/shell/frame/icons.d.ts +71 -0
  240. package/dist/shell/frame/icons.js +75 -0
  241. package/dist/shell/frame/icons.js.map +1 -0
  242. package/dist/shell/frame/index.d.ts +34 -0
  243. package/dist/shell/frame/index.js +43 -0
  244. package/dist/shell/frame/index.js.map +1 -0
  245. package/dist/shell/frame/language.d.ts +58 -0
  246. package/dist/shell/frame/language.js +165 -0
  247. package/dist/shell/frame/language.js.map +1 -0
  248. package/dist/shell/frame/mediaModel.d.ts +47 -0
  249. package/dist/shell/frame/mediaModel.js +67 -0
  250. package/dist/shell/frame/mediaModel.js.map +1 -0
  251. package/dist/shell/frame/menuMessages.en.json +96 -0
  252. package/dist/shell/frame/menuModel.d.ts +319 -0
  253. package/dist/shell/frame/menuModel.js +301 -0
  254. package/dist/shell/frame/menuModel.js.map +1 -0
  255. package/dist/shell/frame/nativeMenu.d.ts +5 -0
  256. package/dist/shell/frame/nativeMenu.js +178 -0
  257. package/dist/shell/frame/nativeMenu.js.map +1 -0
  258. package/dist/shell/frame/nativeMenuModel.d.ts +102 -0
  259. package/dist/shell/frame/nativeMenuModel.js +186 -0
  260. package/dist/shell/frame/nativeMenuModel.js.map +1 -0
  261. package/dist/shell/frame/paletteModel.d.ts +139 -0
  262. package/dist/shell/frame/paletteModel.js +114 -0
  263. package/dist/shell/frame/paletteModel.js.map +1 -0
  264. package/dist/shell/frame/paneTheme.css +9 -0
  265. package/dist/shell/frame/pluginCommandStore.d.ts +23 -0
  266. package/dist/shell/frame/pluginCommandStore.js +70 -0
  267. package/dist/shell/frame/pluginCommandStore.js.map +1 -0
  268. package/dist/shell/frame/reportModel.d.ts +52 -0
  269. package/dist/shell/frame/reportModel.js +105 -0
  270. package/dist/shell/frame/reportModel.js.map +1 -0
  271. package/dist/shell/frame/safeStore.d.ts +7 -0
  272. package/dist/shell/frame/safeStore.js +40 -0
  273. package/dist/shell/frame/safeStore.js.map +1 -0
  274. package/dist/shell/frame/searchInput.d.ts +2 -0
  275. package/dist/shell/frame/searchInput.js +11 -0
  276. package/dist/shell/frame/searchInput.js.map +1 -0
  277. package/dist/shell/frame/shell.css +634 -0
  278. package/dist/shell/frame/site.d.ts +206 -0
  279. package/dist/shell/frame/site.js +22 -0
  280. package/dist/shell/frame/site.js.map +1 -0
  281. package/dist/shell/frame/state.d.ts +101 -0
  282. package/dist/shell/frame/state.js +286 -0
  283. package/dist/shell/frame/state.js.map +1 -0
  284. package/dist/shell/frame/syncGate.d.ts +10 -0
  285. package/dist/shell/frame/syncGate.js +36 -0
  286. package/dist/shell/frame/syncGate.js.map +1 -0
  287. package/dist/shell/frame/syncHost.d.ts +35 -0
  288. package/dist/shell/frame/syncHost.js +85 -0
  289. package/dist/shell/frame/syncHost.js.map +1 -0
  290. package/dist/shell/frame/tabStill.d.ts +2 -0
  291. package/dist/shell/frame/tabStill.js +62 -0
  292. package/dist/shell/frame/tabStill.js.map +1 -0
  293. package/dist/shell/frame/themeScopes.d.ts +29 -0
  294. package/dist/shell/frame/themeScopes.js +61 -0
  295. package/dist/shell/frame/themeScopes.js.map +1 -0
  296. package/dist/shell/frame/themeStore.d.ts +99 -0
  297. package/dist/shell/frame/themeStore.js +267 -0
  298. package/dist/shell/frame/themeStore.js.map +1 -0
  299. package/dist/shell/frame/windowBinding.d.ts +2 -0
  300. package/dist/shell/frame/windowBinding.js +7 -0
  301. package/dist/shell/frame/windowBinding.js.map +1 -0
  302. package/dist/shell/frame/workspaceModel.d.ts +82 -0
  303. package/dist/shell/frame/workspaceModel.js +250 -0
  304. package/dist/shell/frame/workspaceModel.js.map +1 -0
  305. package/dist/shell/frame/workspaces.css +84 -0
  306. package/dist/shell/frame/workspaces.d.ts +22 -0
  307. package/dist/shell/frame/workspaces.js +125 -0
  308. package/dist/shell/frame/workspaces.js.map +1 -0
  309. package/dist/shell/frame/zoomStore.d.ts +10 -0
  310. package/dist/shell/frame/zoomStore.js +65 -0
  311. package/dist/shell/frame/zoomStore.js.map +1 -0
  312. package/dist/shell/installController.d.ts +137 -0
  313. package/dist/shell/installController.js +172 -0
  314. package/dist/shell/installController.js.map +1 -0
  315. package/dist/shell/installerMessages.d.ts +30 -0
  316. package/dist/shell/installerMessages.js +36 -0
  317. package/dist/shell/installerMessages.js.map +1 -0
  318. package/dist/shell/linkDrop.js.map +1 -1
  319. package/dist/shell/messages.d.ts +16 -0
  320. package/dist/shell/messages.js +27 -0
  321. package/dist/shell/messages.js.map +1 -0
  322. package/dist/shell/palette.js.map +1 -1
  323. package/dist/shell/paneModel.d.ts +22 -0
  324. package/dist/shell/paneModel.js +24 -1
  325. package/dist/shell/paneModel.js.map +1 -1
  326. package/dist/shell/panel/Appearance.d.ts +8 -0
  327. package/dist/shell/panel/Appearance.js +265 -0
  328. package/dist/shell/panel/Appearance.js.map +1 -0
  329. package/dist/shell/panel/BackupPanel.d.ts +5 -0
  330. package/dist/shell/panel/BackupPanel.js +198 -0
  331. package/dist/shell/panel/BackupPanel.js.map +1 -0
  332. package/dist/shell/panel/DeveloperPanel.d.ts +4 -0
  333. package/dist/shell/panel/DeveloperPanel.js +51 -0
  334. package/dist/shell/panel/DeveloperPanel.js.map +1 -0
  335. package/dist/shell/panel/FeatureToggles.d.ts +13 -0
  336. package/dist/shell/panel/FeatureToggles.js +42 -0
  337. package/dist/shell/panel/FeatureToggles.js.map +1 -0
  338. package/dist/shell/panel/Home.d.ts +3 -0
  339. package/dist/shell/panel/Home.js +23 -0
  340. package/dist/shell/panel/Home.js.map +1 -0
  341. package/dist/shell/panel/IconPackInstall.d.ts +15 -0
  342. package/dist/shell/panel/IconPackInstall.js +41 -0
  343. package/dist/shell/panel/IconPackInstall.js.map +1 -0
  344. package/dist/shell/panel/LanguageSettings.d.ts +1 -0
  345. package/dist/shell/panel/LanguageSettings.js +54 -0
  346. package/dist/shell/panel/LanguageSettings.js.map +1 -0
  347. package/dist/shell/panel/MakePanel.d.ts +3 -0
  348. package/dist/shell/panel/MakePanel.js +237 -0
  349. package/dist/shell/panel/MakePanel.js.map +1 -0
  350. package/dist/shell/panel/MarkdownDoc.d.ts +6 -0
  351. package/dist/shell/panel/MarkdownDoc.js +35 -0
  352. package/dist/shell/panel/MarkdownDoc.js.map +1 -0
  353. package/dist/shell/panel/Modes.d.ts +40 -0
  354. package/dist/shell/panel/Modes.js +270 -0
  355. package/dist/shell/panel/Modes.js.map +1 -0
  356. package/dist/shell/panel/NavIcon.d.ts +4 -0
  357. package/dist/shell/panel/NavIcon.js +6 -0
  358. package/dist/shell/panel/NavIcon.js.map +1 -0
  359. package/dist/shell/panel/Panel.d.ts +7 -0
  360. package/dist/shell/panel/Panel.js +139 -0
  361. package/dist/shell/panel/Panel.js.map +1 -0
  362. package/dist/shell/panel/PluginFeatures.d.ts +24 -0
  363. package/dist/shell/panel/PluginFeatures.js +165 -0
  364. package/dist/shell/panel/PluginFeatures.js.map +1 -0
  365. package/dist/shell/panel/Plugins.d.ts +33 -0
  366. package/dist/shell/panel/Plugins.js +112 -0
  367. package/dist/shell/panel/Plugins.js.map +1 -0
  368. package/dist/shell/panel/PowerSettings.d.ts +4 -0
  369. package/dist/shell/panel/PowerSettings.js +52 -0
  370. package/dist/shell/panel/PowerSettings.js.map +1 -0
  371. package/dist/shell/panel/Settings.d.ts +23 -0
  372. package/dist/shell/panel/Settings.js +117 -0
  373. package/dist/shell/panel/Settings.js.map +1 -0
  374. package/dist/shell/panel/ShareSetup.d.ts +8 -0
  375. package/dist/shell/panel/ShareSetup.js +130 -0
  376. package/dist/shell/panel/ShareSetup.js.map +1 -0
  377. package/dist/shell/panel/StoreSection.d.ts +7 -0
  378. package/dist/shell/panel/StoreSection.js +78 -0
  379. package/dist/shell/panel/StoreSection.js.map +1 -0
  380. package/dist/shell/panel/StringPacks.d.ts +20 -0
  381. package/dist/shell/panel/StringPacks.js +131 -0
  382. package/dist/shell/panel/StringPacks.js.map +1 -0
  383. package/dist/shell/panel/ThemeBuilder.d.ts +5 -0
  384. package/dist/shell/panel/ThemeBuilder.js +205 -0
  385. package/dist/shell/panel/ThemeBuilder.js.map +1 -0
  386. package/dist/shell/panel/backup.css +34 -0
  387. package/dist/shell/panel/builder.css +240 -0
  388. package/dist/shell/panel/builderModel.d.ts +55 -0
  389. package/dist/shell/panel/builderModel.js +95 -0
  390. package/dist/shell/panel/builderModel.js.map +1 -0
  391. package/dist/shell/panel/compare.d.ts +10 -0
  392. package/dist/shell/panel/compare.js +51 -0
  393. package/dist/shell/panel/compare.js.map +1 -0
  394. package/dist/shell/panel/controls.d.ts +135 -0
  395. package/dist/shell/panel/controls.js +213 -0
  396. package/dist/shell/panel/controls.js.map +1 -0
  397. package/dist/shell/panel/featureModel.d.ts +69 -0
  398. package/dist/shell/panel/featureModel.js +79 -0
  399. package/dist/shell/panel/featureModel.js.map +1 -0
  400. package/dist/shell/panel/fonts/FontBrowser.d.ts +10 -0
  401. package/dist/shell/panel/fonts/FontBrowser.js +189 -0
  402. package/dist/shell/panel/fonts/FontBrowser.js.map +1 -0
  403. package/dist/shell/panel/fonts/FontRoles.d.ts +20 -0
  404. package/dist/shell/panel/fonts/FontRoles.js +77 -0
  405. package/dist/shell/panel/fonts/FontRoles.js.map +1 -0
  406. package/dist/shell/panel/fonts/catalog.d.ts +37 -0
  407. package/dist/shell/panel/fonts/catalog.js +68 -0
  408. package/dist/shell/panel/fonts/catalog.js.map +1 -0
  409. package/dist/shell/panel/fonts/googleFontsCatalog.json +1 -0
  410. package/dist/shell/panel/index.d.ts +20 -0
  411. package/dist/shell/panel/index.js +34 -0
  412. package/dist/shell/panel/index.js.map +1 -0
  413. package/dist/shell/panel/languageModel.d.ts +5 -0
  414. package/dist/shell/panel/languageModel.js +7 -0
  415. package/dist/shell/panel/languageModel.js.map +1 -0
  416. package/dist/shell/panel/make.css +73 -0
  417. package/dist/shell/panel/makeModel.d.ts +32 -0
  418. package/dist/shell/panel/makeModel.js +84 -0
  419. package/dist/shell/panel/makeModel.js.map +1 -0
  420. package/dist/shell/panel/markdown.d.ts +46 -0
  421. package/dist/shell/panel/markdown.js +100 -0
  422. package/dist/shell/panel/markdown.js.map +1 -0
  423. package/dist/shell/panel/nav.d.ts +12 -0
  424. package/dist/shell/panel/nav.js +50 -0
  425. package/dist/shell/panel/nav.js.map +1 -0
  426. package/dist/shell/panel/panel.css +401 -0
  427. package/dist/shell/panel/panelMessages.d.ts +803 -0
  428. package/dist/shell/panel/panelMessages.js +779 -0
  429. package/dist/shell/panel/panelMessages.js.map +1 -0
  430. package/dist/shell/panel/plugins.css +59 -0
  431. package/dist/shell/panel/setupModel.d.ts +87 -0
  432. package/dist/shell/panel/setupModel.js +152 -0
  433. package/dist/shell/panel/setupModel.js.map +1 -0
  434. package/dist/shell/panel/site.d.ts +259 -0
  435. package/dist/shell/panel/site.js +25 -0
  436. package/dist/shell/panel/site.js.map +1 -0
  437. package/dist/shell/panel/strings.css +7 -0
  438. package/dist/shell/panel/themes.d.ts +10 -0
  439. package/dist/shell/panel/themes.js +8 -0
  440. package/dist/shell/panel/themes.js.map +1 -0
  441. package/dist/shell/profileModel.d.ts +3 -2
  442. package/dist/shell/profileModel.js +7 -5
  443. package/dist/shell/profileModel.js.map +1 -1
  444. package/dist/shell/store.css +250 -0
  445. package/dist/shell/storeMessages.d.ts +193 -0
  446. package/dist/shell/storeMessages.js +196 -0
  447. package/dist/shell/storeMessages.js.map +1 -0
  448. package/dist/shell/storeModel.d.ts +205 -0
  449. package/dist/shell/storeModel.js +146 -0
  450. package/dist/shell/storeModel.js.map +1 -0
  451. package/dist/shell/stringPacksModel.d.ts +44 -0
  452. package/dist/shell/stringPacksModel.js +99 -0
  453. package/dist/shell/stringPacksModel.js.map +1 -0
  454. package/dist/shell/updateModel.d.ts +2 -1
  455. package/dist/shell/updateModel.js +5 -4
  456. package/dist/shell/updateModel.js.map +1 -1
  457. package/dist/shell/windowControls.d.ts +4 -0
  458. package/dist/shell/windowControls.js +5 -0
  459. package/dist/shell/windowControls.js.map +1 -1
  460. package/i18n-config.schema.json +29 -0
  461. package/i18n-meta.schema.json +26 -0
  462. package/i18n.schema.json +42 -0
  463. package/icons.schema.json +655 -0
  464. package/languages.schema.json +23 -0
  465. package/locales/ar-x-fm.json +11 -0
  466. package/locales/ar.json +1388 -0
  467. package/locales/bg.json +1387 -0
  468. package/locales/bn.json +1387 -0
  469. package/locales/ca.json +1387 -0
  470. package/locales/cs.json +1387 -0
  471. package/locales/da.json +1387 -0
  472. package/locales/de.json +1387 -0
  473. package/locales/el.json +1387 -0
  474. package/locales/en-GB.json +27 -0
  475. package/locales/en.json +1386 -0
  476. package/locales/en.meta.json +51 -0
  477. package/locales/es.json +1387 -0
  478. package/locales/eu.json +1387 -0
  479. package/locales/fa.json +1388 -0
  480. package/locales/fi.json +1387 -0
  481. package/locales/fil.json +1387 -0
  482. package/locales/fr.json +1387 -0
  483. package/locales/ga.json +1387 -0
  484. package/locales/gl.json +1387 -0
  485. package/locales/gu.json +1387 -0
  486. package/locales/ha.json +1387 -0
  487. package/locales/he.json +1388 -0
  488. package/locales/hi.json +1387 -0
  489. package/locales/hr.json +1387 -0
  490. package/locales/hu.json +1387 -0
  491. package/locales/id.json +1387 -0
  492. package/locales/ig.json +1387 -0
  493. package/locales/it.json +1387 -0
  494. package/locales/ja.json +1387 -0
  495. package/locales/kn.json +1387 -0
  496. package/locales/ko.json +1387 -0
  497. package/locales/mr.json +1387 -0
  498. package/locales/ms.json +1387 -0
  499. package/locales/nb.json +1387 -0
  500. package/locales/nl.json +1387 -0
  501. package/locales/pl.json +1387 -0
  502. package/locales/pt.json +1387 -0
  503. package/locales/ro.json +1387 -0
  504. package/locales/ru.json +1387 -0
  505. package/locales/sk.json +1387 -0
  506. package/locales/sr.json +1387 -0
  507. package/locales/sv.json +1387 -0
  508. package/locales/ta.json +1387 -0
  509. package/locales/th.json +1387 -0
  510. package/locales/tr.json +1387 -0
  511. package/locales/uk.json +1387 -0
  512. package/locales/ur.json +1388 -0
  513. package/locales/vi.json +1387 -0
  514. package/locales/yo.json +1387 -0
  515. package/locales/zh-Hans.json +1387 -0
  516. package/locales/zh-Hant.json +1387 -0
  517. package/package.json +138 -5
  518. package/store-catalog.schema.json +78 -0
  519. package/theme.schema.json +4668 -0
  520. package/windows/installer.nsi +946 -0
  521. package/windows/languages/Basque.nsh +28 -0
  522. package/windows/languages/Catalan.nsh +28 -0
  523. package/windows/languages/Croatian.nsh +28 -0
  524. package/windows/languages/Czech.nsh +28 -0
  525. package/windows/languages/Danish.nsh +28 -0
  526. package/windows/languages/Finnish.nsh +28 -0
  527. package/windows/languages/Galician.nsh +28 -0
  528. package/windows/languages/Greek.nsh +28 -0
  529. package/windows/languages/Hungarian.nsh +28 -0
  530. package/windows/languages/Indonesian.nsh +28 -0
  531. package/windows/languages/Irish.nsh +28 -0
  532. package/windows/languages/Malay.nsh +28 -0
  533. package/windows/languages/Polish.nsh +28 -0
  534. package/windows/languages/Romanian.nsh +28 -0
  535. package/windows/languages/Serbian.nsh +28 -0
  536. package/windows/languages/Slovak.nsh +28 -0
  537. package/windows/languages/Thai.nsh +28 -0
@@ -0,0 +1,144 @@
1
+ /** Flags a store may hold (the native cap, waybetter-host `settings::MAX_FEATURES`). */
2
+ export declare const FLAG_LIMIT = 256;
3
+ /** Characters in a flag key (the native cap). */
4
+ export declare const FLAG_KEY_LIMIT = 64;
5
+ export type FeatureBacking = 'module' | 'app' | 'plugin';
6
+ export type FeatureSpec = {
7
+ /** The flag key. Existing names stay as they are; new ones are `<area>.<feature>`. */
8
+ id: string;
9
+ /** The owning area, or null for a core flag. */
10
+ area: string | null;
11
+ /** The rows it is listed with inside its card (words: `features.group.<group>`). */
12
+ group?: string;
13
+ /** Its value when nothing is stored. */
14
+ default: boolean;
15
+ /**
16
+ * Where its code lives: `module` = a feature module (featureModules.ts); `app` = a part of the
17
+ * app runtime that installs on the flag; `plugin` = an old plugin file until it is ported.
18
+ */
19
+ backing: FeatureBacking;
20
+ /** `backing: 'plugin'`: that file. Its enabled state is the switch; `id` is the flag it becomes. */
21
+ file?: string;
22
+ /** The old plugin file this feature replaces: the one-time migration turns its enabled state into this flag. */
23
+ supersedes?: string;
24
+ /** Surfaces it runs on (ids from the app's surface classifier); omitted = every page. */
25
+ surfaces?: readonly string[];
26
+ /** Also runs on the site's heavy pages (domHub `heavyPath`). */
27
+ heavy?: boolean;
28
+ /** Catalog key of its label (default `features.<id>.label`). */
29
+ label?: string;
30
+ /** Catalog key of its description (default `features.<id>.description`). */
31
+ description?: string;
32
+ /** It has settings of its own (a settings schema on its module): the card offers "Settings". */
33
+ settings?: boolean;
34
+ /** Extra words the panel search matches (an old plugin's name). */
35
+ keywords?: string;
36
+ /** The gated plugin API it uses (shown on the card). */
37
+ permissions?: readonly string[];
38
+ };
39
+ export type AreaSpec = {
40
+ /** `navigation`: lowercase letters, digits and `-`. */
41
+ id: string;
42
+ /** The card's name: a product name, the same in every language. */
43
+ name: string;
44
+ /** Catalog key of the card's description. */
45
+ description: string;
46
+ /** A 16x16 stroke path for the card's icon. */
47
+ icon?: string;
48
+ /**
49
+ * The owner of its palette commands and the file it ships as once it is a plugin file
50
+ * (`bt-navigation-plus.plugin.js`). A valid plugin file name.
51
+ */
52
+ owner: string;
53
+ /** Its words in the page: `plugin.<namespace>.*` (`navigation-plus`). */
54
+ namespace: string;
55
+ };
56
+ export type ModeSpec = {
57
+ id: string;
58
+ /** Catalog keys of its name and one-line description. */
59
+ name: string;
60
+ description: string;
61
+ /** The preselected one on the first-run screen. */
62
+ recommended?: boolean;
63
+ /** Flag values that differ from the table's defaults. */
64
+ flags: Readonly<Record<string, boolean>>;
65
+ };
66
+ export type FeatureTable = {
67
+ areas: readonly AreaSpec[];
68
+ features: readonly FeatureSpec[];
69
+ modes: readonly ModeSpec[];
70
+ };
71
+ export type Flags = Record<string, boolean>;
72
+ /** The flag that is an area's master switch. */
73
+ export declare const areaFlag: (area: string) => string;
74
+ /** Features that are flags (everything but the ones still backed by an old plugin file). */
75
+ export declare const flagFeatures: (table: FeatureTable) => FeatureSpec[];
76
+ export declare const areaFeatures: (table: FeatureTable, area: string) => FeatureSpec[];
77
+ export declare const coreFeatures: (table: FeatureTable) => FeatureSpec[];
78
+ /** The catalog keys of a feature's label and description. */
79
+ export declare function featureWords(feature: FeatureSpec): {
80
+ label: string;
81
+ description: string;
82
+ };
83
+ /** Every flag with its default: the features that are flags, and each area's switch (on). */
84
+ export declare function tableDefaults(table: FeatureTable): Flags;
85
+ /** The full flag map for `input`: unknown keys are dropped, anything not a boolean takes the default. */
86
+ export declare function normalizeFlags(table: FeatureTable, input: unknown): Flags;
87
+ /** What is stored: only the flags whose value differs from the default. */
88
+ export declare function sparseFlags(table: FeatureTable, flags: Readonly<Record<string, unknown>>): Flags;
89
+ /**
90
+ * What runs: a feature owned by an area is off while that area's switch is off. Core flags and the
91
+ * area switches themselves pass through.
92
+ */
93
+ export declare function effectiveFlags(table: FeatureTable, flags: Readonly<Flags>): Flags;
94
+ export declare const modeById: (table: FeatureTable, id: string | null | undefined) => ModeSpec | null;
95
+ /** The full flag map a mode starts from (an unknown or null mode: the defaults). */
96
+ export declare function modeFlags(table: FeatureTable, mode: string | null | undefined): Flags;
97
+ /** The flags whose value is not what `mode` starts from: the owner's own changes. */
98
+ export declare function manualChanges(table: FeatureTable, flags: Readonly<Flags>, mode: string | null | undefined): string[];
99
+ /**
100
+ * The flags after choosing mode `to` while on mode `from` (null = never chosen). `keep`: the
101
+ * owner's own changes (whatever differs from `from`) stay on top of the new mode; otherwise the
102
+ * new mode's values apply as they are.
103
+ */
104
+ export declare function switchMode(table: FeatureTable, flags: Readonly<Flags>, from: string | null | undefined, to: string, keep: boolean): Flags;
105
+ export type ModeStatus = {
106
+ /** The flags differ from what the mode starts from: "Custom (based on <mode>)". */
107
+ custom: boolean;
108
+ /** What applying the mode again would turn on and off (flag ids, in table order). */
109
+ on: string[];
110
+ off: string[];
111
+ /**
112
+ * Of those, the ones the mode gained since it was applied (an update made its preset larger).
113
+ * Empty when nothing is known about the preset of that time.
114
+ */
115
+ added: string[];
116
+ };
117
+ /**
118
+ * How the stored flags relate to mode `mode` (null or unknown: no mode, nothing to say). A mode is
119
+ * a starting point: nothing here applies anything. `applied` is the mode's `flags` as they were
120
+ * when it was applied (the shell stores it with the choice); without it a difference is simply the
121
+ * owner's own change.
122
+ */
123
+ export declare function modeStatus(table: FeatureTable, flags: Readonly<Flags>, mode: string | null | undefined, applied?: Readonly<Record<string, boolean>> | null): ModeStatus;
124
+ /**
125
+ * Feature health, as the panel learns it. A feature that paused itself (featureModules.ts: its
126
+ * selector probes failed) is named by its area's `health` palette command in the page: the entry
127
+ * opens the notes there, and its keywords carry the paused flags. The panel reads the focused
128
+ * tab's command list (it already has it for the palette), so no new report crosses to the host.
129
+ */
130
+ export declare const HEALTH_COMMAND = "health";
131
+ /** The keywords of an area's health command: the paused flags of that area. */
132
+ export declare const healthKeywords: (ids: readonly string[]) => string;
133
+ /** The flags of `table` a tab's reported commands say are paused (unknown words are ignored). */
134
+ export declare function pausedFeatures(table: FeatureTable, commands: ReadonlyArray<{
135
+ id: string;
136
+ keywords?: string;
137
+ }> | null | undefined): Set<string>;
138
+ /** The old plugin files the table's features replace, for the app's native migration list. */
139
+ export declare function supersededFiles(table: FeatureTable): {
140
+ file: string;
141
+ flag: string;
142
+ }[];
143
+ /** Problems in a table (an empty list: valid). Run it in the app's tests. */
144
+ export declare function validateTable(table: FeatureTable): string[];
@@ -0,0 +1,223 @@
1
+ // The feature table: one data description of everything an app lets its owner switch, shared by the
2
+ // page runtime (which flags exist, who owns them), the panel (the area cards, the Fixes list, the
3
+ // onboarding modes) and the app's native side (the migration list it generates from it). Pure data
4
+ // and pure functions: no DOM, no storage.
5
+ //
6
+ // - An AREA is one card in the Plugins list ("Navigation+"): a master switch and the features it
7
+ // owns. Its switch is the flag `area.<id>`.
8
+ // - A FEATURE is one on/off flag. It belongs to an area, or to nobody (`area: null`: a core flag
9
+ // such as a reliability fix, which no plugin switch can turn off). Where its code lives is its
10
+ // `backing`: a feature module in the registry (featureModules.ts), a part of the app runtime
11
+ // (request hubs, native commands, document-start timing), or an old plugin file that has not
12
+ // been ported yet (then it is not a flag at all: its switch is that file's enabled state).
13
+ // - A MODE is a preset (Basic / Advanced / Power User): the flag values it starts from. A mode is a
14
+ // starting point, not a lock: every flag stays individually switchable afterwards.
15
+ //
16
+ // Flags are stored sparsely (`sparseFlags`): only values that differ from the table's defaults.
17
+ /** Flags a store may hold (the native cap, waybetter-host `settings::MAX_FEATURES`). */
18
+ export const FLAG_LIMIT = 256;
19
+ /** Characters in a flag key (the native cap). */
20
+ export const FLAG_KEY_LIMIT = 64;
21
+ /** The flag that is an area's master switch. */
22
+ export const areaFlag = (area) => `area.${area}`;
23
+ /** Features that are flags (everything but the ones still backed by an old plugin file). */
24
+ export const flagFeatures = (table) => table.features.filter(feature => feature.backing !== 'plugin');
25
+ export const areaFeatures = (table, area) => table.features.filter(feature => feature.area === area);
26
+ export const coreFeatures = (table) => table.features.filter(feature => feature.area === null);
27
+ /** The catalog keys of a feature's label and description. */
28
+ export function featureWords(feature) {
29
+ return { label: feature.label ?? `features.${feature.id}.label`, description: feature.description ?? `features.${feature.id}.description` };
30
+ }
31
+ /** Every flag with its default: the features that are flags, and each area's switch (on). */
32
+ export function tableDefaults(table) {
33
+ const out = {};
34
+ for (const feature of flagFeatures(table))
35
+ out[feature.id] = feature.default;
36
+ for (const area of table.areas)
37
+ out[areaFlag(area.id)] = true;
38
+ return out;
39
+ }
40
+ /** The full flag map for `input`: unknown keys are dropped, anything not a boolean takes the default. */
41
+ export function normalizeFlags(table, input) {
42
+ const source = input && typeof input === 'object' ? input : {};
43
+ const out = tableDefaults(table);
44
+ for (const key of Object.keys(out))
45
+ if (typeof source[key] === 'boolean')
46
+ out[key] = source[key];
47
+ return out;
48
+ }
49
+ /** What is stored: only the flags whose value differs from the default. */
50
+ export function sparseFlags(table, flags) {
51
+ const defaults = tableDefaults(table);
52
+ const out = {};
53
+ for (const [key, fallback] of Object.entries(defaults)) {
54
+ const value = flags[key];
55
+ if (typeof value === 'boolean' && value !== fallback)
56
+ out[key] = value;
57
+ }
58
+ return out;
59
+ }
60
+ /**
61
+ * What runs: a feature owned by an area is off while that area's switch is off. Core flags and the
62
+ * area switches themselves pass through.
63
+ */
64
+ export function effectiveFlags(table, flags) {
65
+ const out = { ...flags };
66
+ for (const feature of flagFeatures(table)) {
67
+ if (feature.area !== null && flags[areaFlag(feature.area)] === false)
68
+ out[feature.id] = false;
69
+ }
70
+ return out;
71
+ }
72
+ export const modeById = (table, id) => table.modes.find(mode => mode.id === id) ?? null;
73
+ /** The full flag map a mode starts from (an unknown or null mode: the defaults). */
74
+ export function modeFlags(table, mode) {
75
+ const out = tableDefaults(table);
76
+ const preset = modeById(table, mode)?.flags ?? {};
77
+ for (const [key, value] of Object.entries(preset))
78
+ if (key in out)
79
+ out[key] = value;
80
+ return out;
81
+ }
82
+ /** The flags whose value is not what `mode` starts from: the owner's own changes. */
83
+ export function manualChanges(table, flags, mode) {
84
+ const preset = modeFlags(table, mode);
85
+ return Object.keys(preset).filter(key => typeof flags[key] === 'boolean' && flags[key] !== preset[key]);
86
+ }
87
+ /**
88
+ * The flags after choosing mode `to` while on mode `from` (null = never chosen). `keep`: the
89
+ * owner's own changes (whatever differs from `from`) stay on top of the new mode; otherwise the
90
+ * new mode's values apply as they are.
91
+ */
92
+ export function switchMode(table, flags, from, to, keep) {
93
+ const next = modeFlags(table, to);
94
+ if (keep)
95
+ for (const key of manualChanges(table, flags, from))
96
+ next[key] = flags[key];
97
+ return next;
98
+ }
99
+ /**
100
+ * How the stored flags relate to mode `mode` (null or unknown: no mode, nothing to say). A mode is
101
+ * a starting point: nothing here applies anything. `applied` is the mode's `flags` as they were
102
+ * when it was applied (the shell stores it with the choice); without it a difference is simply the
103
+ * owner's own change.
104
+ */
105
+ export function modeStatus(table, flags, mode, applied) {
106
+ const spec = modeById(table, mode);
107
+ if (!spec)
108
+ return { custom: false, on: [], off: [], added: [] };
109
+ const preset = modeFlags(table, spec.id);
110
+ const defaults = tableDefaults(table);
111
+ const differs = Object.keys(preset).filter(key => typeof flags[key] === 'boolean' && flags[key] !== preset[key]);
112
+ const was = (key) => typeof applied?.[key] === 'boolean' ? applied[key] : defaults[key];
113
+ return {
114
+ custom: differs.length > 0,
115
+ on: differs.filter(key => preset[key]),
116
+ off: differs.filter(key => !preset[key]),
117
+ added: applied ? differs.filter(key => was(key) !== preset[key]) : [],
118
+ };
119
+ }
120
+ /**
121
+ * Feature health, as the panel learns it. A feature that paused itself (featureModules.ts: its
122
+ * selector probes failed) is named by its area's `health` palette command in the page: the entry
123
+ * opens the notes there, and its keywords carry the paused flags. The panel reads the focused
124
+ * tab's command list (it already has it for the palette), so no new report crosses to the host.
125
+ */
126
+ export const HEALTH_COMMAND = 'health';
127
+ const HEALTH_WORD = 'paused';
128
+ /** The keywords of an area's health command: the paused flags of that area. */
129
+ export const healthKeywords = (ids) => [HEALTH_WORD, ...ids].join(' ');
130
+ /** The flags of `table` a tab's reported commands say are paused (unknown words are ignored). */
131
+ export function pausedFeatures(table, commands) {
132
+ const paused = new Set();
133
+ for (const area of table.areas) {
134
+ const words = commands?.find(command => command.id === `${area.owner}:${HEALTH_COMMAND}`)?.keywords?.split(' ') ?? [];
135
+ if (words[0] !== HEALTH_WORD)
136
+ continue;
137
+ for (const feature of table.features)
138
+ if (feature.area === area.id && words.includes(feature.id))
139
+ paused.add(feature.id);
140
+ }
141
+ return paused;
142
+ }
143
+ /** The old plugin files the table's features replace, for the app's native migration list. */
144
+ export function supersededFiles(table) {
145
+ return table.features.filter(feature => feature.supersedes && feature.backing !== 'plugin').map(feature => ({ file: feature.supersedes, flag: feature.id }));
146
+ }
147
+ const FLAG_ID = /^[A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)?$/;
148
+ const AREA_ID = /^[a-z][a-z0-9-]{0,31}$/;
149
+ const MODE_ID = /^[a-z][a-z0-9-]{0,31}$/;
150
+ const PLUGIN_FILE = /^[A-Za-z0-9_\- ][A-Za-z0-9_.\- ]{0,117}\.plugin\.js$/;
151
+ /** Problems in a table (an empty list: valid). Run it in the app's tests. */
152
+ export function validateTable(table) {
153
+ const problems = [];
154
+ const areas = new Set();
155
+ for (const area of table.areas) {
156
+ if (!AREA_ID.test(area.id))
157
+ problems.push(`area ${JSON.stringify(area.id)}: invalid id`);
158
+ if (areas.has(area.id))
159
+ problems.push(`area ${area.id}: listed twice`);
160
+ areas.add(area.id);
161
+ if (!PLUGIN_FILE.test(area.owner))
162
+ problems.push(`area ${area.id}: owner must be a plugin file name`);
163
+ if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(area.namespace))
164
+ problems.push(`area ${area.id}: invalid namespace`);
165
+ if (!area.name.trim() || !area.description.trim())
166
+ problems.push(`area ${area.id}: name and description are required`);
167
+ }
168
+ const ids = new Set();
169
+ const files = new Set();
170
+ for (const feature of table.features) {
171
+ if (!FLAG_ID.test(feature.id) || feature.id.length > FLAG_KEY_LIMIT)
172
+ problems.push(`feature ${JSON.stringify(feature.id)}: invalid id`);
173
+ if (feature.id.startsWith('area.'))
174
+ problems.push(`feature ${feature.id}: "area." is kept for the area switches`);
175
+ if (ids.has(feature.id))
176
+ problems.push(`feature ${feature.id}: owned twice`);
177
+ ids.add(feature.id);
178
+ if (feature.area !== null && !areas.has(feature.area))
179
+ problems.push(`feature ${feature.id}: unknown area ${feature.area}`);
180
+ if (feature.backing === 'plugin') {
181
+ if (!feature.file || !PLUGIN_FILE.test(feature.file))
182
+ problems.push(`feature ${feature.id}: a plugin-backed feature names its file`);
183
+ if (feature.area === null)
184
+ problems.push(`feature ${feature.id}: a plugin-backed feature belongs to an area`);
185
+ if (feature.default)
186
+ problems.push(`feature ${feature.id}: a plugin file is off until its owner turns it on`);
187
+ }
188
+ else if (feature.file)
189
+ problems.push(`feature ${feature.id}: only a plugin-backed feature names a file`);
190
+ for (const file of [feature.file, feature.supersedes]) {
191
+ if (!file)
192
+ continue;
193
+ if (!PLUGIN_FILE.test(file))
194
+ problems.push(`feature ${feature.id}: invalid file ${JSON.stringify(file)}`);
195
+ if (files.has(file))
196
+ problems.push(`feature ${feature.id}: ${file} is named twice`);
197
+ files.add(file);
198
+ }
199
+ if (feature.backing !== 'module' && feature.settings)
200
+ problems.push(`feature ${feature.id}: only a feature module has a settings schema`);
201
+ }
202
+ const defaults = tableDefaults(table);
203
+ if (Object.keys(defaults).length > FLAG_LIMIT)
204
+ problems.push(`${Object.keys(defaults).length} flags: at most ${FLAG_LIMIT}`);
205
+ const modes = new Set();
206
+ for (const mode of table.modes) {
207
+ if (!MODE_ID.test(mode.id))
208
+ problems.push(`mode ${JSON.stringify(mode.id)}: invalid id`);
209
+ if (modes.has(mode.id))
210
+ problems.push(`mode ${mode.id}: listed twice`);
211
+ modes.add(mode.id);
212
+ for (const [key, value] of Object.entries(mode.flags)) {
213
+ if (!(key in defaults))
214
+ problems.push(`mode ${mode.id}: unknown flag ${key}`);
215
+ else if (value === defaults[key])
216
+ problems.push(`mode ${mode.id}: ${key} is already the default`);
217
+ }
218
+ }
219
+ if (table.modes.length && table.modes.filter(mode => mode.recommended).length !== 1)
220
+ problems.push('exactly one mode is the recommended one');
221
+ return problems;
222
+ }
223
+ //# sourceMappingURL=featureTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featureTable.js","sourceRoot":"","sources":["../../../runtime/src/featureTable.ts"],"names":[],"mappings":"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","sourcesContent":["// The feature table: one data description of everything an app lets its owner switch, shared by the\n// page runtime (which flags exist, who owns them), the panel (the area cards, the Fixes list, the\n// onboarding modes) and the app's native side (the migration list it generates from it). Pure data\n// and pure functions: no DOM, no storage.\n//\n// - An AREA is one card in the Plugins list (\"Navigation+\"): a master switch and the features it\n// owns. Its switch is the flag `area.<id>`.\n// - A FEATURE is one on/off flag. It belongs to an area, or to nobody (`area: null`: a core flag\n// such as a reliability fix, which no plugin switch can turn off). Where its code lives is its\n// `backing`: a feature module in the registry (featureModules.ts), a part of the app runtime\n// (request hubs, native commands, document-start timing), or an old plugin file that has not\n// been ported yet (then it is not a flag at all: its switch is that file's enabled state).\n// - A MODE is a preset (Basic / Advanced / Power User): the flag values it starts from. A mode is a\n// starting point, not a lock: every flag stays individually switchable afterwards.\n//\n// Flags are stored sparsely (`sparseFlags`): only values that differ from the table's defaults.\n\n/** Flags a store may hold (the native cap, waybetter-host `settings::MAX_FEATURES`). */\nexport const FLAG_LIMIT = 256;\n/** Characters in a flag key (the native cap). */\nexport const FLAG_KEY_LIMIT = 64;\n\nexport type FeatureBacking = 'module' | 'app' | 'plugin';\n\nexport type FeatureSpec = {\n /** The flag key. Existing names stay as they are; new ones are `<area>.<feature>`. */\n id: string;\n /** The owning area, or null for a core flag. */\n area: string | null;\n /** The rows it is listed with inside its card (words: `features.group.<group>`). */\n group?: string;\n /** Its value when nothing is stored. */\n default: boolean;\n /**\n * Where its code lives: `module` = a feature module (featureModules.ts); `app` = a part of the\n * app runtime that installs on the flag; `plugin` = an old plugin file until it is ported.\n */\n backing: FeatureBacking;\n /** `backing: 'plugin'`: that file. Its enabled state is the switch; `id` is the flag it becomes. */\n file?: string;\n /** The old plugin file this feature replaces: the one-time migration turns its enabled state into this flag. */\n supersedes?: string;\n /** Surfaces it runs on (ids from the app's surface classifier); omitted = every page. */\n surfaces?: readonly string[];\n /** Also runs on the site's heavy pages (domHub `heavyPath`). */\n heavy?: boolean;\n /** Catalog key of its label (default `features.<id>.label`). */\n label?: string;\n /** Catalog key of its description (default `features.<id>.description`). */\n description?: string;\n /** It has settings of its own (a settings schema on its module): the card offers \"Settings\". */\n settings?: boolean;\n /** Extra words the panel search matches (an old plugin's name). */\n keywords?: string;\n /** The gated plugin API it uses (shown on the card). */\n permissions?: readonly string[];\n};\n\nexport type AreaSpec = {\n /** `navigation`: lowercase letters, digits and `-`. */\n id: string;\n /** The card's name: a product name, the same in every language. */\n name: string;\n /** Catalog key of the card's description. */\n description: string;\n /** A 16x16 stroke path for the card's icon. */\n icon?: string;\n /**\n * The owner of its palette commands and the file it ships as once it is a plugin file\n * (`bt-navigation-plus.plugin.js`). A valid plugin file name.\n */\n owner: string;\n /** Its words in the page: `plugin.<namespace>.*` (`navigation-plus`). */\n namespace: string;\n};\n\nexport type ModeSpec = {\n id: string;\n /** Catalog keys of its name and one-line description. */\n name: string;\n description: string;\n /** The preselected one on the first-run screen. */\n recommended?: boolean;\n /** Flag values that differ from the table's defaults. */\n flags: Readonly<Record<string, boolean>>;\n};\n\nexport type FeatureTable = {\n areas: readonly AreaSpec[];\n features: readonly FeatureSpec[];\n modes: readonly ModeSpec[];\n};\n\nexport type Flags = Record<string, boolean>;\n\n/** The flag that is an area's master switch. */\nexport const areaFlag = (area: string) => `area.${area}`;\n\n/** Features that are flags (everything but the ones still backed by an old plugin file). */\nexport const flagFeatures = (table: FeatureTable) => table.features.filter(feature => feature.backing !== 'plugin');\n\nexport const areaFeatures = (table: FeatureTable, area: string) => table.features.filter(feature => feature.area === area);\nexport const coreFeatures = (table: FeatureTable) => table.features.filter(feature => feature.area === null);\n\n/** The catalog keys of a feature's label and description. */\nexport function featureWords(feature: FeatureSpec): { label: string; description: string } {\n return { label: feature.label ?? `features.${feature.id}.label`, description: feature.description ?? `features.${feature.id}.description` };\n}\n\n/** Every flag with its default: the features that are flags, and each area's switch (on). */\nexport function tableDefaults(table: FeatureTable): Flags {\n const out: Flags = {};\n for (const feature of flagFeatures(table)) out[feature.id] = feature.default;\n for (const area of table.areas) out[areaFlag(area.id)] = true;\n return out;\n}\n\n/** The full flag map for `input`: unknown keys are dropped, anything not a boolean takes the default. */\nexport function normalizeFlags(table: FeatureTable, input: unknown): Flags {\n const source = input && typeof input === 'object' ? input as Record<string, unknown> : {};\n const out = tableDefaults(table);\n for (const key of Object.keys(out)) if (typeof source[key] === 'boolean') out[key] = source[key] as boolean;\n return out;\n}\n\n/** What is stored: only the flags whose value differs from the default. */\nexport function sparseFlags(table: FeatureTable, flags: Readonly<Record<string, unknown>>): Flags {\n const defaults = tableDefaults(table);\n const out: Flags = {};\n for (const [key, fallback] of Object.entries(defaults)) {\n const value = flags[key];\n if (typeof value === 'boolean' && value !== fallback) out[key] = value;\n }\n return out;\n}\n\n/**\n * What runs: a feature owned by an area is off while that area's switch is off. Core flags and the\n * area switches themselves pass through.\n */\nexport function effectiveFlags(table: FeatureTable, flags: Readonly<Flags>): Flags {\n const out: Flags = { ...flags };\n for (const feature of flagFeatures(table)) {\n if (feature.area !== null && flags[areaFlag(feature.area)] === false) out[feature.id] = false;\n }\n return out;\n}\n\nexport const modeById = (table: FeatureTable, id: string | null | undefined) => table.modes.find(mode => mode.id === id) ?? null;\n\n/** The full flag map a mode starts from (an unknown or null mode: the defaults). */\nexport function modeFlags(table: FeatureTable, mode: string | null | undefined): Flags {\n const out = tableDefaults(table);\n const preset = modeById(table, mode)?.flags ?? {};\n for (const [key, value] of Object.entries(preset)) if (key in out) out[key] = value;\n return out;\n}\n\n/** The flags whose value is not what `mode` starts from: the owner's own changes. */\nexport function manualChanges(table: FeatureTable, flags: Readonly<Flags>, mode: string | null | undefined): string[] {\n const preset = modeFlags(table, mode);\n return Object.keys(preset).filter(key => typeof flags[key] === 'boolean' && flags[key] !== preset[key]);\n}\n\n/**\n * The flags after choosing mode `to` while on mode `from` (null = never chosen). `keep`: the\n * owner's own changes (whatever differs from `from`) stay on top of the new mode; otherwise the\n * new mode's values apply as they are.\n */\nexport function switchMode(table: FeatureTable, flags: Readonly<Flags>, from: string | null | undefined, to: string, keep: boolean): Flags {\n const next = modeFlags(table, to);\n if (keep) for (const key of manualChanges(table, flags, from)) next[key] = flags[key]!;\n return next;\n}\n\nexport type ModeStatus = {\n /** The flags differ from what the mode starts from: \"Custom (based on <mode>)\". */\n custom: boolean;\n /** What applying the mode again would turn on and off (flag ids, in table order). */\n on: string[];\n off: string[];\n /**\n * Of those, the ones the mode gained since it was applied (an update made its preset larger).\n * Empty when nothing is known about the preset of that time.\n */\n added: string[];\n};\n\n/**\n * How the stored flags relate to mode `mode` (null or unknown: no mode, nothing to say). A mode is\n * a starting point: nothing here applies anything. `applied` is the mode's `flags` as they were\n * when it was applied (the shell stores it with the choice); without it a difference is simply the\n * owner's own change.\n */\nexport function modeStatus(table: FeatureTable, flags: Readonly<Flags>, mode: string | null | undefined, applied?: Readonly<Record<string, boolean>> | null): ModeStatus {\n const spec = modeById(table, mode);\n if (!spec) return { custom: false, on: [], off: [], added: [] };\n const preset = modeFlags(table, spec.id);\n const defaults = tableDefaults(table);\n const differs = Object.keys(preset).filter(key => typeof flags[key] === 'boolean' && flags[key] !== preset[key]);\n const was = (key: string) => typeof applied?.[key] === 'boolean' ? applied[key] : defaults[key];\n return {\n custom: differs.length > 0,\n on: differs.filter(key => preset[key]),\n off: differs.filter(key => !preset[key]),\n added: applied ? differs.filter(key => was(key) !== preset[key]) : [],\n };\n}\n\n/**\n * Feature health, as the panel learns it. A feature that paused itself (featureModules.ts: its\n * selector probes failed) is named by its area's `health` palette command in the page: the entry\n * opens the notes there, and its keywords carry the paused flags. The panel reads the focused\n * tab's command list (it already has it for the palette), so no new report crosses to the host.\n */\nexport const HEALTH_COMMAND = 'health';\nconst HEALTH_WORD = 'paused';\n\n/** The keywords of an area's health command: the paused flags of that area. */\nexport const healthKeywords = (ids: readonly string[]) => [HEALTH_WORD, ...ids].join(' ');\n\n/** The flags of `table` a tab's reported commands say are paused (unknown words are ignored). */\nexport function pausedFeatures(table: FeatureTable, commands: ReadonlyArray<{ id: string; keywords?: string }> | null | undefined): Set<string> {\n const paused = new Set<string>();\n for (const area of table.areas) {\n const words = commands?.find(command => command.id === `${area.owner}:${HEALTH_COMMAND}`)?.keywords?.split(' ') ?? [];\n if (words[0] !== HEALTH_WORD) continue;\n for (const feature of table.features) if (feature.area === area.id && words.includes(feature.id)) paused.add(feature.id);\n }\n return paused;\n}\n\n/** The old plugin files the table's features replace, for the app's native migration list. */\nexport function supersededFiles(table: FeatureTable): { file: string; flag: string }[] {\n return table.features.filter(feature => feature.supersedes && feature.backing !== 'plugin').map(feature => ({ file: feature.supersedes!, flag: feature.id }));\n}\n\nconst FLAG_ID = /^[A-Za-z][A-Za-z0-9]*(?:\\.[A-Za-z][A-Za-z0-9]*)?$/;\nconst AREA_ID = /^[a-z][a-z0-9-]{0,31}$/;\nconst MODE_ID = /^[a-z][a-z0-9-]{0,31}$/;\nconst PLUGIN_FILE = /^[A-Za-z0-9_\\- ][A-Za-z0-9_.\\- ]{0,117}\\.plugin\\.js$/;\n\n/** Problems in a table (an empty list: valid). Run it in the app's tests. */\nexport function validateTable(table: FeatureTable): string[] {\n const problems: string[] = [];\n const areas = new Set<string>();\n for (const area of table.areas) {\n if (!AREA_ID.test(area.id)) problems.push(`area ${JSON.stringify(area.id)}: invalid id`);\n if (areas.has(area.id)) problems.push(`area ${area.id}: listed twice`);\n areas.add(area.id);\n if (!PLUGIN_FILE.test(area.owner)) problems.push(`area ${area.id}: owner must be a plugin file name`);\n if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(area.namespace)) problems.push(`area ${area.id}: invalid namespace`);\n if (!area.name.trim() || !area.description.trim()) problems.push(`area ${area.id}: name and description are required`);\n }\n const ids = new Set<string>();\n const files = new Set<string>();\n for (const feature of table.features) {\n if (!FLAG_ID.test(feature.id) || feature.id.length > FLAG_KEY_LIMIT) problems.push(`feature ${JSON.stringify(feature.id)}: invalid id`);\n if (feature.id.startsWith('area.')) problems.push(`feature ${feature.id}: \"area.\" is kept for the area switches`);\n if (ids.has(feature.id)) problems.push(`feature ${feature.id}: owned twice`);\n ids.add(feature.id);\n if (feature.area !== null && !areas.has(feature.area)) problems.push(`feature ${feature.id}: unknown area ${feature.area}`);\n if (feature.backing === 'plugin') {\n if (!feature.file || !PLUGIN_FILE.test(feature.file)) problems.push(`feature ${feature.id}: a plugin-backed feature names its file`);\n if (feature.area === null) problems.push(`feature ${feature.id}: a plugin-backed feature belongs to an area`);\n if (feature.default) problems.push(`feature ${feature.id}: a plugin file is off until its owner turns it on`);\n } else if (feature.file) problems.push(`feature ${feature.id}: only a plugin-backed feature names a file`);\n for (const file of [feature.file, feature.supersedes]) {\n if (!file) continue;\n if (!PLUGIN_FILE.test(file)) problems.push(`feature ${feature.id}: invalid file ${JSON.stringify(file)}`);\n if (files.has(file)) problems.push(`feature ${feature.id}: ${file} is named twice`);\n files.add(file);\n }\n if (feature.backing !== 'module' && feature.settings) problems.push(`feature ${feature.id}: only a feature module has a settings schema`);\n }\n const defaults = tableDefaults(table);\n if (Object.keys(defaults).length > FLAG_LIMIT) problems.push(`${Object.keys(defaults).length} flags: at most ${FLAG_LIMIT}`);\n const modes = new Set<string>();\n for (const mode of table.modes) {\n if (!MODE_ID.test(mode.id)) problems.push(`mode ${JSON.stringify(mode.id)}: invalid id`);\n if (modes.has(mode.id)) problems.push(`mode ${mode.id}: listed twice`);\n modes.add(mode.id);\n for (const [key, value] of Object.entries(mode.flags)) {\n if (!(key in defaults)) problems.push(`mode ${mode.id}: unknown flag ${key}`);\n else if (value === defaults[key]) problems.push(`mode ${mode.id}: ${key} is already the default`);\n }\n }\n if (table.modes.length && table.modes.filter(mode => mode.recommended).length !== 1) problems.push('exactly one mode is the recommended one');\n return problems;\n}\n"]}
@@ -0,0 +1,22 @@
1
+ /** Unicode ranges of the Latin and Latin Extended subsets (@fontsource file names `latin`, `latin-ext`). */
2
+ export declare const LATIN_RANGE = "U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD";
3
+ export declare const LATIN_EXT_RANGE = "U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF";
4
+ /** A bundled family's files: base64 woff2 (variable, weights 100-900) with the unicode range each covers. */
5
+ export type BundledFontFiles = Record<string, Array<[base64: string, unicodeRange: string]>>;
6
+ /** Registers the bundled families `names` (BUNDLED_FONTS) from `files`, once per document. */
7
+ export declare function loadBundledFonts(doc: Document, names: string[], files: BundledFontFiles): void;
8
+ /**
9
+ * Google Fonts the owner downloaded: native code validates and caches the woff2 files and hands
10
+ * their bytes to site tabs whose theme uses them. The page never requests a font; each family is
11
+ * registered once per document (again only when its bytes change, e.g. after picking other weights).
12
+ */
13
+ export type ProvidedFont = {
14
+ family: string;
15
+ faces: Array<{
16
+ weight: [number, number];
17
+ unicodeRange: string | null;
18
+ data: string;
19
+ }>;
20
+ };
21
+ /** Registers the provided families (validated again; at most 8 families of 24 faces); returns the families added. */
22
+ export declare function provideFonts(doc: Document, fonts: unknown): string[];
@@ -0,0 +1,72 @@
1
+ // Theme fonts in a site's page. Sites usually block remote and `data:` font URLs (CSP `font-src`),
2
+ // but a FontFace built from bytes performs no fetch, so bundled families (Geist and Geist Mono: the
3
+ // app embeds their woff2 files in its page runtime) are registered only when a theme uses them, and
4
+ // Google Fonts the owner downloaded arrive as bytes from native code (`provideFonts`).
5
+ import { BUNDLED_FONTS } from "./themeSpec.js";
6
+ /** Unicode ranges of the Latin and Latin Extended subsets (@fontsource file names `latin`, `latin-ext`). */
7
+ export const LATIN_RANGE = 'U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD';
8
+ export const LATIN_EXT_RANGE = 'U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF';
9
+ const loaded = new WeakMap();
10
+ const log = (family, error) => console.error(`[wayBetter] font ${family} failed`, error);
11
+ /** Registers the bundled families `names` (BUNDLED_FONTS) from `files`, once per document. */
12
+ export function loadBundledFonts(doc, names, files) {
13
+ let done = loaded.get(doc);
14
+ if (!done)
15
+ loaded.set(doc, done = new Set());
16
+ for (const name of names) {
17
+ const faces = files[name];
18
+ const family = BUNDLED_FONTS[name];
19
+ if (!faces || !family || done.has(name))
20
+ continue;
21
+ done.add(name);
22
+ for (const [base64, unicodeRange] of faces) {
23
+ const bytes = Uint8Array.from(atob(base64), char => char.charCodeAt(0));
24
+ const face = new FontFace(family, bytes.buffer, { weight: '100 900', style: 'normal', unicodeRange, display: 'swap' });
25
+ doc.fonts.add(face);
26
+ face.load().catch(error => log(name, error));
27
+ }
28
+ }
29
+ }
30
+ const provided = new WeakMap();
31
+ const FAMILY = /^[A-Za-z0-9][A-Za-z0-9 ]{0,47}$/;
32
+ /** Registers the provided families (validated again; at most 8 families of 24 faces); returns the families added. */
33
+ export function provideFonts(doc, fonts) {
34
+ const added = [];
35
+ if (!Array.isArray(fonts))
36
+ return added;
37
+ let known = provided.get(doc);
38
+ if (!known)
39
+ provided.set(doc, known = new Map());
40
+ for (const font of fonts.slice(0, 8)) {
41
+ if (!font || typeof font.family !== 'string' || !FAMILY.test(font.family) || !Array.isArray(font.faces))
42
+ continue;
43
+ const key = font.faces.map(face => `${face?.weight}|${typeof face?.data === 'string' ? face.data.length : 0}`).join(',');
44
+ if (known.get(font.family) === key)
45
+ continue;
46
+ // Other weights of a family replace its earlier faces.
47
+ for (const face of [...doc.fonts])
48
+ if (face.family.replace(/"/g, '') === font.family && face.wbGoogle)
49
+ doc.fonts.delete(face);
50
+ known.set(font.family, key);
51
+ for (const face of font.faces.slice(0, 24)) {
52
+ if (!face || typeof face.data !== 'string' || !Array.isArray(face.weight))
53
+ continue;
54
+ try {
55
+ const bytes = Uint8Array.from(atob(face.data), char => char.charCodeAt(0));
56
+ const [min, max] = face.weight.map(Number);
57
+ const descriptors = { weight: min === max ? String(min) : `${min} ${max}`, style: 'normal', display: 'swap' };
58
+ if (typeof face.unicodeRange === 'string' && /^[U+0-9A-Fa-f?, -]{1,4000}$/.test(face.unicodeRange))
59
+ descriptors.unicodeRange = face.unicodeRange;
60
+ const fontFace = Object.assign(new FontFace(font.family, bytes.buffer, descriptors), { wbGoogle: true });
61
+ doc.fonts.add(fontFace);
62
+ fontFace.load().catch(error => log(font.family, error));
63
+ }
64
+ catch (error) {
65
+ log(font.family, error);
66
+ }
67
+ }
68
+ added.push(font.family);
69
+ }
70
+ return added;
71
+ }
72
+ //# sourceMappingURL=fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../../runtime/src/fonts.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,oGAAoG;AACpG,oGAAoG;AACpG,uFAAuF;AAEvF,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,4GAA4G;AAC5G,MAAM,CAAC,MAAM,WAAW,GAAG,0JAA0J,CAAC;AACtL,MAAM,CAAC,MAAM,eAAe,GAAG,oLAAoL,CAAC;AAKpN,MAAM,MAAM,GAAG,IAAI,OAAO,EAAyB,CAAC;AACpD,MAAM,GAAG,GAAG,CAAC,MAAc,EAAE,KAAc,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,MAAM,SAAS,EAAE,KAAK,CAAC,CAAC;AAE1G,8FAA8F;AAC9F,MAAM,UAAU,gBAAgB,CAAC,GAAa,EAAE,KAAe,EAAE,KAAuB;IACtF,IAAI,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAClD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,KAAK,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,KAAK,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YACxE,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YACvH,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpB,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;AACH,CAAC;AAQD,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAiC,CAAC;AAC9D,MAAM,MAAM,GAAG,iCAAiC,CAAC;AAEjD,qHAAqH;AACrH,MAAM,UAAU,YAAY,CAAC,GAAa,EAAE,KAAc;IACxD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;IACjD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAmB,EAAE,CAAC;QACvD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QAClH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,IAAI,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzH,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG;YAAE,SAAS;QAC7C,uDAAuD;QACvD,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;YAAE,IAAK,IAAiB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,MAAM,IAAK,IAA0C,CAAC,QAAQ;gBAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAgB,CAAC,CAAC;QAC/L,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC5B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,SAAS;YACpF,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3E,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAqB,CAAC;gBAC/D,MAAM,WAAW,GAAwB,EAAE,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;gBACnI,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC;oBAAE,WAAW,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;gBACjJ,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACxB,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;YAC1D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAAC,CAAC;QAC9C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["// Theme fonts in a site's page. Sites usually block remote and `data:` font URLs (CSP `font-src`),\n// but a FontFace built from bytes performs no fetch, so bundled families (Geist and Geist Mono: the\n// app embeds their woff2 files in its page runtime) are registered only when a theme uses them, and\n// Google Fonts the owner downloaded arrive as bytes from native code (`provideFonts`).\n\nimport { BUNDLED_FONTS } from './themeSpec.ts';\n\n/** Unicode ranges of the Latin and Latin Extended subsets (@fontsource file names `latin`, `latin-ext`). */\nexport const LATIN_RANGE = 'U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD';\nexport const LATIN_EXT_RANGE = 'U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF';\n\n/** A bundled family's files: base64 woff2 (variable, weights 100-900) with the unicode range each covers. */\nexport type BundledFontFiles = Record<string, Array<[base64: string, unicodeRange: string]>>;\n\nconst loaded = new WeakMap<Document, Set<string>>();\nconst log = (family: string, error: unknown) => console.error(`[wayBetter] font ${family} failed`, error);\n\n/** Registers the bundled families `names` (BUNDLED_FONTS) from `files`, once per document. */\nexport function loadBundledFonts(doc: Document, names: string[], files: BundledFontFiles): void {\n let done = loaded.get(doc);\n if (!done) loaded.set(doc, done = new Set());\n for (const name of names) {\n const faces = files[name];\n const family = BUNDLED_FONTS[name];\n if (!faces || !family || done.has(name)) continue;\n done.add(name);\n for (const [base64, unicodeRange] of faces) {\n const bytes = Uint8Array.from(atob(base64), char => char.charCodeAt(0));\n const face = new FontFace(family, bytes.buffer, { weight: '100 900', style: 'normal', unicodeRange, display: 'swap' });\n doc.fonts.add(face);\n face.load().catch(error => log(name, error));\n }\n }\n}\n\n/**\n * Google Fonts the owner downloaded: native code validates and caches the woff2 files and hands\n * their bytes to site tabs whose theme uses them. The page never requests a font; each family is\n * registered once per document (again only when its bytes change, e.g. after picking other weights).\n */\nexport type ProvidedFont = { family: string; faces: Array<{ weight: [number, number]; unicodeRange: string | null; data: string }> };\nconst provided = new WeakMap<Document, Map<string, string>>();\nconst FAMILY = /^[A-Za-z0-9][A-Za-z0-9 ]{0,47}$/;\n\n/** Registers the provided families (validated again; at most 8 families of 24 faces); returns the families added. */\nexport function provideFonts(doc: Document, fonts: unknown): string[] {\n const added: string[] = [];\n if (!Array.isArray(fonts)) return added;\n let known = provided.get(doc);\n if (!known) provided.set(doc, known = new Map());\n for (const font of fonts.slice(0, 8) as ProvidedFont[]) {\n if (!font || typeof font.family !== 'string' || !FAMILY.test(font.family) || !Array.isArray(font.faces)) continue;\n const key = font.faces.map(face => `${face?.weight}|${typeof face?.data === 'string' ? face.data.length : 0}`).join(',');\n if (known.get(font.family) === key) continue;\n // Other weights of a family replace its earlier faces.\n for (const face of [...doc.fonts]) if ((face as FontFace).family.replace(/\"/g, '') === font.family && (face as FontFace & { wbGoogle?: boolean }).wbGoogle) doc.fonts.delete(face as FontFace);\n known.set(font.family, key);\n for (const face of font.faces.slice(0, 24)) {\n if (!face || typeof face.data !== 'string' || !Array.isArray(face.weight)) continue;\n try {\n const bytes = Uint8Array.from(atob(face.data), char => char.charCodeAt(0));\n const [min, max] = face.weight.map(Number) as [number, number];\n const descriptors: FontFaceDescriptors = { weight: min === max ? String(min) : `${min} ${max}`, style: 'normal', display: 'swap' };\n if (typeof face.unicodeRange === 'string' && /^[U+0-9A-Fa-f?, -]{1,4000}$/.test(face.unicodeRange)) descriptors.unicodeRange = face.unicodeRange;\n const fontFace = Object.assign(new FontFace(font.family, bytes.buffer, descriptors), { wbGoogle: true });\n doc.fonts.add(fontFace);\n fontFace.load().catch(error => log(font.family, error));\n } catch (error) { log(font.family, error); }\n }\n added.push(font.family);\n }\n return added;\n}\n"]}