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,926 @@
1
+ // `api.ui`: toasts, alerts, confirmations, prompts and dialogs for plugins, drawn in the active
2
+ // theme's colors, corners and font. Everything lives in one Shadow DOM host (`<wb-ui>`), so the
3
+ // site's CSS cannot reach it and it cannot reach the site; the host exists only while something is
4
+ // on screen, and a plugin's `scope.ui` closes whatever that plugin opened when it stops.
5
+ //
6
+ // Accessibility: dialogs are `role="dialog"` (alerts and confirmations `role="alertdialog"`),
7
+ // `aria-modal`, labelled by their title and described by their message; focus moves in, is trapped
8
+ // (Tab / Shift+Tab wrap), and returns to where it was. Esc dismisses, Enter in a single-line field
9
+ // runs the default action. Stacked dialogs make the ones below inert. Toasts are a polite live
10
+ // region (danger toasts `role="alert"`), pause while hovered or focused, and stack (four at most).
11
+ // Motion is skipped with `prefers-reduced-motion`. Text is always set as text (never parsed as HTML).
12
+ import { adoptStyles } from "./styles.js";
13
+ import { themeVars } from "./themeApi.js";
14
+ /** The kit's default words (English source; `kit.*` keys in the framework catalogs). */
15
+ export const KIT_MESSAGES = {
16
+ 'kit.ok': 'OK',
17
+ 'kit.cancel': 'Cancel',
18
+ 'kit.delete': 'Delete',
19
+ 'kit.close': 'Close',
20
+ 'kit.dismiss': 'Dismiss',
21
+ 'kit.dialog': 'Dialog',
22
+ 'kit.value': 'Value',
23
+ 'kit.checkValue': 'Please check this value.',
24
+ 'kit.failed': 'Something went wrong.',
25
+ // Feature modules (featureModules.ts): the settings dialog and the health notes.
26
+ 'kit.save': 'Save',
27
+ 'kit.feature.paused': '{feature} ({area}) is paused: this page changed.',
28
+ 'kit.feature.pausedOne': 'A feature of {area} is paused: this page changed.',
29
+ 'kit.feature.pausedList': 'Paused features of {area}',
30
+ 'kit.feature.needsEnglish': '{feature} needs the site in English.',
31
+ 'kit.feature.settings': '{name} settings',
32
+ };
33
+ // ---------------------------------------------------------------------------------------------
34
+ // Pure helpers (tested without a DOM)
35
+ export const WIDTHS = { sm: 380, md: 480, lg: 640 };
36
+ export const MAX_TOASTS = 4;
37
+ /** Dialog width in px for an option value. */
38
+ export function dialogWidth(width) {
39
+ if (typeof width === 'number' && Number.isFinite(width))
40
+ return Math.max(260, Math.min(960, Math.round(width)));
41
+ return WIDTHS[width] ?? WIDTHS.md;
42
+ }
43
+ /** How long a toast stays: explicit ms, 0 = until closed; defaults 3200 / 6000 with an action. */
44
+ export function toastDuration(options) {
45
+ const value = options.duration;
46
+ if (typeof value === 'number' && Number.isFinite(value))
47
+ return value <= 0 ? 0 : Math.max(800, Math.min(60_000, value));
48
+ if (value === Infinity)
49
+ return 0;
50
+ return options.action ? 6000 : 3200;
51
+ }
52
+ /** Tab / Shift+Tab inside a trap: the index to focus next, or -1 to let the browser move focus. */
53
+ export function trapIndex(count, current, backwards) {
54
+ if (count === 0)
55
+ return -1;
56
+ if (current < 0)
57
+ return backwards ? count - 1 : 0;
58
+ if (backwards && current === 0)
59
+ return count - 1;
60
+ if (!backwards && current === count - 1)
61
+ return 0;
62
+ return -1;
63
+ }
64
+ /** A validator's answer as an error message (null = valid). */
65
+ export function validationMessage(result, fallback = KIT_MESSAGES['kit.checkValue']) {
66
+ if (typeof result === 'string')
67
+ return result.trim() ? result : null;
68
+ if (result === false)
69
+ return fallback;
70
+ return null;
71
+ }
72
+ const BLOCKED_PROPS = new Set(['innerhtml', 'outerhtml', 'srcdoc', 'insertadjacenthtml']);
73
+ const PROPERTY_PROPS = new Set(['value', 'checked', 'disabled', 'selected', 'indeterminate', 'multiple', 'readOnly', 'required', 'rows', 'maxLength', 'spellcheck', 'tabIndex']);
74
+ const URL_ATTRS = /^(href|src|srcset|action|formaction|xlink:href|poster|ping|cite|background)$/i;
75
+ const SAFE_SCHEMES = new Set(['http', 'https', 'mailto']);
76
+ /**
77
+ * Whether a URL attribute value is safe: relative, or http(s)/mailto. Checked the way browsers read
78
+ * a URL (tabs and newlines anywhere and leading control characters/spaces are ignored), so
79
+ * `java\tscript:` or `\x01javascript:` do not slip through.
80
+ */
81
+ export function isSafeUrl(value) {
82
+ const normalized = String(value).replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+/, '');
83
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(normalized);
84
+ return !scheme || SAFE_SCHEMES.has(scheme[1].toLowerCase());
85
+ }
86
+ /** The builder behind `ui.h`, for any document. */
87
+ export function buildElement(doc, tag, props, ...children) {
88
+ if (!/^[a-z][a-z0-9-]*$/i.test(tag) || /^(script|style|iframe|object|embed)$/i.test(tag))
89
+ throw new Error(`ui.h: unsupported tag ${JSON.stringify(tag)}`);
90
+ const element = doc.createElement(tag);
91
+ for (const [key, value] of Object.entries(props ?? {})) {
92
+ if (value === undefined || value === null || value === false && !PROPERTY_PROPS.has(key))
93
+ continue;
94
+ if (BLOCKED_PROPS.has(key.toLowerCase()))
95
+ continue;
96
+ if (key === 'class' || key === 'className')
97
+ element.className = String(value);
98
+ else if (key === 'text')
99
+ element.textContent = String(value);
100
+ else if (key === 'style' && typeof value === 'object')
101
+ Object.assign(element.style, value);
102
+ else if (key === 'dataset' && typeof value === 'object')
103
+ Object.assign(element.dataset, value);
104
+ else if (/^on[a-z]/i.test(key)) {
105
+ if (typeof value === 'function')
106
+ element.addEventListener(key.slice(2).toLowerCase(), value);
107
+ }
108
+ else if (PROPERTY_PROPS.has(key))
109
+ element[key] = value;
110
+ else if (URL_ATTRS.test(key) && !isSafeUrl(String(value)))
111
+ continue;
112
+ else
113
+ element.setAttribute(key, value === true ? '' : String(value));
114
+ }
115
+ const append = (child) => {
116
+ if (child === null || child === undefined || child === false)
117
+ return;
118
+ if (Array.isArray(child)) {
119
+ for (const item of child)
120
+ append(item);
121
+ return;
122
+ }
123
+ element.appendChild(typeof child === 'object' ? child : doc.createTextNode(String(child)));
124
+ };
125
+ for (const child of children)
126
+ append(child);
127
+ return element;
128
+ }
129
+ // ---------------------------------------------------------------------------------------------
130
+ // Styles (inside the shadow root; every value comes from `--wb-*` set on the host)
131
+ const DANGER = '#f4212e';
132
+ const TONES = { neutral: 'var(--wb-accent)', info: 'var(--wb-accent)', success: '#00ba7c', warning: '#ffad1f', danger: DANGER };
133
+ // Toast colors keep their label at 4.5:1 or more (neutral toasts use the theme's inverse pair,
134
+ // which the theme engine keeps legible).
135
+ const TOAST_TONES = { success: ['#00805a', '#ffffff'], warning: ['#ffd400', '#0f1419'], danger: ['#d0121f', '#ffffff'] };
136
+ export const UI_CSS = `
137
+ :host { all: initial; }
138
+ .wb-root { font-family: var(--wb-font-dialog, var(--wb-font)); color: var(--wb-text); -webkit-font-smoothing: antialiased; }
139
+ .wb-root *, .wb-root *::before, .wb-root *::after { box-sizing: border-box; }
140
+ .wb-layer { position: fixed; inset: 0; display: grid; place-items: center; padding: 16px; pointer-events: auto; overscroll-behavior: contain; }
141
+ .wb-backdrop { position: absolute; inset: 0; background: var(--wb-scrim); animation: wb-fade .16s ease-out; }
142
+ .wb-dialog { position: relative; width: min(var(--wb-dialog-width, 480px), 100%); max-height: calc(100vh - 32px); overflow: auto; overscroll-behavior: contain;
143
+ display: flex; flex-direction: column; gap: 0; padding: 20px 24px 22px; background: var(--wb-bg); color: var(--wb-text);
144
+ border: 1px solid var(--wb-border); border-radius: var(--wb-radius); box-shadow: 0 24px 64px -12px rgba(0,0,0,.45), 0 2px 8px rgba(0,0,0,.12);
145
+ font: 400 15px/1.45 var(--wb-font-dialog, var(--wb-font)); outline: none; animation: wb-in .18s cubic-bezier(.2,.9,.3,1.2); }
146
+ .wb-layer.wb-closing .wb-dialog { animation: wb-out .12s ease-in forwards; }
147
+ .wb-layer.wb-closing .wb-backdrop { animation: wb-fade-out .12s ease-in forwards; }
148
+ .wb-layer[inert] .wb-dialog { filter: saturate(.9); }
149
+ .wb-head { display: flex; align-items: flex-start; gap: 10px; margin: 0 0 4px; }
150
+ .wb-title { flex: 1; margin: 0; font: 800 20px/1.25 var(--wb-font-dialog, var(--wb-font)); letter-spacing: -.01em; color: var(--wb-text); overflow-wrap: anywhere; }
151
+ .wb-icon { flex: none; width: 24px; height: 24px; margin-top: 1px; color: var(--wb-tone); }
152
+ .wb-icon svg { display: block; width: 24px; height: 24px; }
153
+ .wb-close { flex: none; display: grid; place-items: center; width: 32px; height: 32px; margin: -6px -10px 0 0; padding: 0; border: 0; border-radius: 999px;
154
+ background: transparent; color: var(--wb-text); cursor: pointer; }
155
+ .wb-close:hover { background: var(--wb-hover); }
156
+ .wb-close svg { width: 18px; height: 18px; }
157
+ .wb-message { margin: 4px 0 0; color: var(--wb-muted); white-space: pre-line; overflow-wrap: anywhere; }
158
+ .wb-body { margin-top: 14px; min-width: 0; }
159
+ .wb-body:empty { display: none; }
160
+ .wb-error { margin: 12px 0 0; color: var(--wb-danger); font-size: 13px; font-weight: 600; }
161
+ .wb-error[hidden] { display: none; }
162
+ .wb-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; margin-top: 22px; }
163
+ .wb-actions:empty { display: none; }
164
+ .wb-actions.wb-stack { flex-direction: column; align-items: stretch; gap: 12px; margin-top: 24px; }
165
+ .wb-actions.wb-stack .wb-btn { height: 44px; font-size: 16px; }
166
+ .wb-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-width: 72px; height: 36px; padding: 0 18px;
167
+ border: 1px solid transparent; border-radius: var(--wb-button-radius); font: 700 15px/1 var(--wb-font-dialog, var(--wb-font)); cursor: pointer; white-space: nowrap;
168
+ transition: background-color .12s, filter .12s, opacity .12s; }
169
+ .wb-btn:disabled, .wb-btn[aria-disabled="true"] { opacity: .5; cursor: default; }
170
+ .wb-btn:focus-visible, .wb-close:focus-visible, .wb-toast button:focus-visible { outline: 2px solid var(--wb-accent); outline-offset: 2px; }
171
+ .wb-primary { background: var(--wb-inverse); color: var(--wb-on-inverse); }
172
+ .wb-primary:not(:disabled):not([aria-disabled="true"]):hover { filter: brightness(.92) contrast(1.05); }
173
+ .wb-danger { background: var(--wb-danger); color: #fff; }
174
+ .wb-danger:not(:disabled):hover { filter: brightness(.92); }
175
+ .wb-secondary { background: transparent; color: var(--wb-text); border-color: var(--wb-border-strong); }
176
+ .wb-secondary:not(:disabled):hover { background: var(--wb-hover); }
177
+ .wb-plain { background: transparent; color: var(--wb-accent); min-width: 0; padding: 0 10px; }
178
+ .wb-plain:not(:disabled):hover { background: var(--wb-hover); }
179
+ .wb-btn[aria-busy="true"] { cursor: progress; }
180
+ /* Form parts for dialog content. */
181
+ .wb-field { display: flex; flex-direction: column; gap: 6px; margin: 0 0 12px; }
182
+ .wb-field:last-child { margin-bottom: 0; }
183
+ .wb-label { font-size: 13px; font-weight: 600; color: var(--wb-muted); }
184
+ .wb-hint { margin: 0; font-size: 13px; color: var(--wb-muted); }
185
+ .wb-input, .wb-dialog input[type="text"], .wb-dialog input[type="search"], .wb-dialog input[type="url"], .wb-dialog input[type="number"], .wb-dialog textarea, .wb-dialog select {
186
+ width: 100%; min-height: 40px; padding: 9px 12px; border: 1px solid var(--wb-border-strong); border-radius: var(--wb-field-radius);
187
+ background: var(--wb-bg); color: var(--wb-text); font: 400 15px/1.4 var(--wb-font-dialog, var(--wb-font)); outline: none; transition: border-color .12s, box-shadow .12s; }
188
+ .wb-dialog textarea { min-height: 96px; resize: vertical; }
189
+ .wb-dialog textarea.wb-mono { font: 400 13px/1.5 var(--wb-font-mono, ui-monospace, "Cascadia Mono", Consolas, Menlo, monospace); }
190
+ .wb-input::placeholder, .wb-dialog input::placeholder, .wb-dialog textarea::placeholder { color: var(--wb-muted); opacity: .8; }
191
+ .wb-input:focus, .wb-dialog input:focus, .wb-dialog textarea:focus, .wb-dialog select:focus { border-color: var(--wb-accent); box-shadow: 0 0 0 1px var(--wb-accent); }
192
+ .wb-dialog [aria-invalid="true"] { border-color: var(--wb-danger) !important; box-shadow: 0 0 0 1px var(--wb-danger) !important; }
193
+ .wb-check { display: flex; align-items: flex-start; gap: 10px; padding: 7px 0; cursor: pointer; line-height: 1.35; }
194
+ .wb-check input { flex: none; width: 18px; height: 18px; margin: 1px 0 0; accent-color: var(--wb-accent); cursor: pointer; }
195
+ .wb-check small { display: block; color: var(--wb-muted); font-size: 13px; }
196
+ .wb-divider { height: 1px; margin: 12px 0; background: var(--wb-border); border: 0; }
197
+ .wb-row { display: flex; align-items: center; gap: 10px; }
198
+ .wb-muted { color: var(--wb-muted); }
199
+ .wb-dialog a { color: var(--wb-accent); }
200
+ /* Toasts */
201
+ .wb-toasts { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 8px;
202
+ width: max-content; max-width: min(560px, calc(100vw - 32px)); pointer-events: none; }
203
+ .wb-toast { display: flex; align-items: center; gap: 12px; min-height: 44px; max-width: 100%; padding: 10px 10px 10px 16px; border-radius: min(var(--wb-radius), 12px);
204
+ background: var(--wb-toast-bg); color: var(--wb-toast-fg); box-shadow: 0 10px 30px -6px rgba(0,0,0,.4); pointer-events: auto;
205
+ font: 500 15px/1.35 var(--wb-font-dialog, var(--wb-font)); animation: wb-toast-in .2s cubic-bezier(.2,.9,.3,1.2); }
206
+ .wb-toast.wb-closing { animation: wb-toast-out .14s ease-in forwards; }
207
+ ${Object.entries(TOAST_TONES).map(([tone, [bg, fg]]) => `.wb-toast[data-tone="${tone}"] { --wb-toast-bg: ${bg}; --wb-toast-fg: ${fg}; }`).join(' ')}
208
+ .wb-toast-icon { flex: none; display: grid; margin-left: -4px; }
209
+ .wb-toast-icon svg { width: 20px; height: 20px; }
210
+ .wb-toast[data-tone="info"] .wb-toast-icon { color: var(--wb-accent); }
211
+ .wb-toast-text { flex: 1; min-width: 0; overflow-wrap: anywhere; white-space: pre-line; padding-right: 4px; }
212
+ .wb-toast button { flex: none; border: 0; cursor: pointer; color: inherit; font: 700 14px/1 var(--wb-font-dialog, var(--wb-font)); }
213
+ .wb-toast-action { height: 30px; padding: 0 12px; border-radius: var(--wb-button-radius); background: color-mix(in srgb, currentColor 16%, transparent); }
214
+ .wb-toast-action:hover { background: color-mix(in srgb, currentColor 26%, transparent); }
215
+ .wb-toast-close { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border-radius: 999px; background: transparent; opacity: .8; }
216
+ .wb-toast-close:hover { opacity: 1; background: color-mix(in srgb, currentColor 16%, transparent); }
217
+ .wb-toast-close svg { width: 16px; height: 16px; }
218
+ @keyframes wb-fade { from { opacity: 0; } }
219
+ @keyframes wb-fade-out { to { opacity: 0; } }
220
+ @keyframes wb-in { from { opacity: 0; transform: translateY(6px) scale(.97); } }
221
+ @keyframes wb-out { to { opacity: 0; transform: scale(.98); } }
222
+ @keyframes wb-toast-in { from { opacity: 0; transform: translateY(10px) scale(.98); } }
223
+ @keyframes wb-toast-out { to { opacity: 0; transform: translateY(6px); } }
224
+ @media (prefers-reduced-motion: reduce) {
225
+ .wb-root *, .wb-root *::before, .wb-root *::after { animation: none !important; transition: none !important; }
226
+ }
227
+ `;
228
+ const SVG_NS = 'http://www.w3.org/2000/svg';
229
+ const ICONS = {
230
+ info: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm1 15h-2v-6h2v6Zm0-8h-2V7h2v2Z',
231
+ success: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm-1.2 14.2-4-4 1.4-1.4 2.6 2.6 5-5 1.4 1.4-6.4 6.4Z',
232
+ warning: 'M12 2 1 21h22L12 2Zm1 16h-2v-2h2v2Zm0-4h-2v-5h2v5Z',
233
+ danger: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm1 15h-2v-2h2v2Zm0-4h-2V7h2v6Z',
234
+ close: 'M10.59 12 4.54 5.96l1.42-1.42L12 10.59l6.04-6.05 1.42 1.42L13.41 12l6.05 6.04-1.42 1.42L12 13.41l-6.04 6.05-1.42-1.42L10.59 12Z',
235
+ };
236
+ const FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]):not([type="hidden"]),select:not([disabled]),textarea:not([disabled]),[contenteditable]:not([contenteditable="false"]),[tabindex]:not([tabindex="-1"])';
237
+ /** The deepest focused element, through open shadow roots. */
238
+ export function deepActiveElement(doc) {
239
+ let active = doc.activeElement;
240
+ while (active?.shadowRoot?.activeElement)
241
+ active = active.shadowRoot.activeElement;
242
+ return active;
243
+ }
244
+ export function createUiKit(win, options) {
245
+ const doc = win.document;
246
+ const hostTag = options.hostTag ?? 'wb-ui';
247
+ const product = options.product ?? 'wayBetter';
248
+ const report = (error) => console.error(`[${product}] ui callback failed`, error);
249
+ const word = (key) => {
250
+ try {
251
+ const value = options.text?.(key);
252
+ if (typeof value === 'string' && value)
253
+ return value;
254
+ }
255
+ catch { /* a broken translator must not break a dialog */ }
256
+ return KIT_MESSAGES[key];
257
+ };
258
+ const isShadowRoot = (node) => typeof win.ShadowRoot === 'function' && node instanceof win.ShadowRoot;
259
+ const reducedMotion = () => { try {
260
+ return !!win.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
261
+ }
262
+ catch {
263
+ return false;
264
+ } };
265
+ let host = null;
266
+ let root = null;
267
+ let layers = null;
268
+ let toastRegion = null;
269
+ let offTheme = null;
270
+ const stack = [];
271
+ const toasts = [];
272
+ let seq = 0;
273
+ let pendingExits = 0;
274
+ const ownerIds = new WeakMap();
275
+ const paint = (theme) => {
276
+ if (!host)
277
+ return;
278
+ const vars = themeVars(theme);
279
+ const dark = theme.mode === 'dark';
280
+ vars['--wb-scrim'] = dark ? 'rgba(91, 112, 131, 0.4)' : 'rgba(15, 20, 25, 0.4)';
281
+ vars['--wb-hover'] = `color-mix(in srgb, ${theme.colors.text} ${dark ? 12 : 8}%, transparent)`;
282
+ vars['--wb-border-strong'] = `color-mix(in srgb, ${theme.colors.border} 70%, ${theme.colors.muted})`;
283
+ vars['--wb-field-radius'] = `${Math.min(10, Math.max(2, Math.round(theme.radius / 2)))}px`;
284
+ vars['--wb-danger'] = DANGER;
285
+ vars['--wb-toast-bg'] = theme.colors.inverse;
286
+ vars['--wb-toast-fg'] = theme.colors.onInverse;
287
+ for (const [name, value] of Object.entries(vars))
288
+ host.style.setProperty(name, value);
289
+ host.style.setProperty('color-scheme', theme.mode);
290
+ };
291
+ const ensureRoot = () => {
292
+ if (host?.isConnected && root && layers && toastRegion)
293
+ return;
294
+ if (host) {
295
+ // Someone removed the host while things were open: close them (their promises resolve).
296
+ offTheme?.();
297
+ offTheme = null;
298
+ for (const entry of [...stack])
299
+ entry.dismiss();
300
+ for (const item of [...toasts])
301
+ item.close();
302
+ }
303
+ host?.remove();
304
+ host = doc.createElement(hostTag);
305
+ host.setAttribute('data-wb-ui', '');
306
+ if (options.hostId)
307
+ host.id = options.hostId;
308
+ // The host sits above the site; it takes no clicks itself (its layers and toasts do).
309
+ host.style.cssText = `all:initial;position:fixed;inset:0;z-index:${options.zIndex ?? 2147483600};pointer-events:none;display:block;`;
310
+ root = typeof host.attachShadow === 'function' ? host.attachShadow({ mode: 'open' }) : host;
311
+ const wrap = doc.createElement('div');
312
+ wrap.className = 'wb-root';
313
+ layers = doc.createElement('div');
314
+ toastRegion = doc.createElement('div');
315
+ toastRegion.className = 'wb-toasts';
316
+ toastRegion.setAttribute('aria-live', 'polite');
317
+ toastRegion.setAttribute('aria-relevant', 'additions');
318
+ wrap.append(layers, toastRegion);
319
+ root.append(wrap);
320
+ // An adopted sheet (see styles.ts): the site's CSP cannot refuse it, and it stays with the root
321
+ // wherever the host is moved. A host without a shadow root keeps a <style> child.
322
+ if (isShadowRoot(root))
323
+ adoptStyles(root, UI_CSS);
324
+ else {
325
+ const style = doc.createElement('style');
326
+ style.textContent = UI_CSS;
327
+ root.prepend(style);
328
+ }
329
+ // Keys typed in a dialog are ours: the site's (and plugins') page shortcuts never see them.
330
+ for (const type of ['keydown', 'keyup', 'keypress'])
331
+ root.addEventListener(type, event => event.stopPropagation());
332
+ (doc.body ?? doc.documentElement).appendChild(host);
333
+ paint(options.theme.current());
334
+ offTheme = options.theme.onChange(paint);
335
+ };
336
+ /** Removes the host once nothing is on screen, so a stopped plugin leaves the page as it was. */
337
+ const maybeTeardown = () => {
338
+ if (stack.length || toasts.length || pendingExits)
339
+ return;
340
+ offTheme?.();
341
+ offTheme = null;
342
+ host?.remove();
343
+ host = root = layers = toastRegion = null;
344
+ };
345
+ const later = (fn, ms) => {
346
+ if (ms <= 0) {
347
+ fn();
348
+ return;
349
+ }
350
+ pendingExits += 1;
351
+ win.setTimeout(() => { pendingExits -= 1; fn(); maybeTeardown(); }, ms);
352
+ };
353
+ const svg = (path) => {
354
+ const icon = doc.createElementNS(SVG_NS, 'svg');
355
+ icon.setAttribute('viewBox', '0 0 24 24');
356
+ icon.setAttribute('aria-hidden', 'true');
357
+ const shape = doc.createElementNS(SVG_NS, 'path');
358
+ shape.setAttribute('d', path);
359
+ shape.setAttribute('fill', 'currentColor');
360
+ icon.appendChild(shape);
361
+ return icon;
362
+ };
363
+ const tabbables = (panel) => Array.from(panel.querySelectorAll(FOCUSABLE))
364
+ .filter(el => el.tabIndex >= 0 && !el.closest('[hidden],[inert]') && el.getClientRects().length > 0);
365
+ const focus = (el) => { try {
366
+ el?.focus({ preventScroll: true });
367
+ }
368
+ catch { /* Detached. */ } };
369
+ // One focus guard for all dialogs: focus that lands outside the kit goes back to the top dialog.
370
+ let guardWindow = 0;
371
+ let guardCount = 0;
372
+ const onFocusIn = (event) => {
373
+ const top = stack[stack.length - 1];
374
+ if (!top || event.target === host)
375
+ return;
376
+ // Another focus trap (the site's own modal) fighting ours must not loop forever: give up after 20 tries a second.
377
+ const now = Date.now();
378
+ if (now - guardWindow > 1000) {
379
+ guardWindow = now;
380
+ guardCount = 0;
381
+ }
382
+ if (++guardCount > 20)
383
+ return;
384
+ focus(top.lastFocus?.isConnected && top.panel.contains(top.lastFocus) ? top.lastFocus : tabbables(top.panel)[0] ?? top.panel);
385
+ };
386
+ const syncStack = () => {
387
+ stack.forEach((entry, index) => {
388
+ const top = index === stack.length - 1;
389
+ entry.layer.toggleAttribute('inert', !top);
390
+ if (top)
391
+ entry.layer.removeAttribute('aria-hidden');
392
+ else
393
+ entry.layer.setAttribute('aria-hidden', 'true');
394
+ });
395
+ if (stack.length)
396
+ doc.addEventListener('focusin', onFocusIn, true);
397
+ else
398
+ doc.removeEventListener('focusin', onFocusIn, true);
399
+ };
400
+ function modal(input, owner) {
401
+ ensureRoot();
402
+ const opts = input ?? {};
403
+ const id = `wb-${++seq}`;
404
+ const dismissible = opts.dismissible !== false;
405
+ const layer = doc.createElement('div');
406
+ layer.className = 'wb-layer';
407
+ const backdrop = doc.createElement('div');
408
+ backdrop.className = 'wb-backdrop';
409
+ const panel = doc.createElement('div');
410
+ panel.className = 'wb-dialog';
411
+ panel.setAttribute('role', opts.role === 'alertdialog' ? 'alertdialog' : 'dialog');
412
+ panel.setAttribute('aria-modal', 'true');
413
+ panel.tabIndex = -1;
414
+ panel.style.setProperty('--wb-dialog-width', `${dialogWidth(opts.width)}px`);
415
+ if (opts.tone)
416
+ panel.style.setProperty('--wb-tone', TONES[opts.tone]);
417
+ if (opts.title || (dismissible && opts.closeButton)) {
418
+ const head = doc.createElement('div');
419
+ head.className = 'wb-head';
420
+ if (opts.tone && opts.tone !== 'neutral') {
421
+ const icon = doc.createElement('span');
422
+ icon.className = 'wb-icon';
423
+ icon.appendChild(svg(ICONS[opts.tone]));
424
+ head.appendChild(icon);
425
+ }
426
+ const title = doc.createElement('h2');
427
+ title.className = 'wb-title';
428
+ title.id = `${id}-title`;
429
+ title.textContent = opts.title ?? '';
430
+ head.appendChild(title);
431
+ if (dismissible && (opts.closeButton ?? !!opts.title)) {
432
+ const x = doc.createElement('button');
433
+ x.type = 'button';
434
+ x.className = 'wb-close';
435
+ x.setAttribute('aria-label', word('kit.close'));
436
+ x.appendChild(svg(ICONS.close));
437
+ x.addEventListener('click', () => dismiss());
438
+ head.appendChild(x);
439
+ }
440
+ panel.appendChild(head);
441
+ if (opts.title)
442
+ panel.setAttribute('aria-labelledby', title.id);
443
+ }
444
+ if (!opts.title)
445
+ panel.setAttribute('aria-label', opts.label || opts.message || word('kit.dialog'));
446
+ if (opts.message) {
447
+ const message = doc.createElement('p');
448
+ message.className = 'wb-message';
449
+ message.id = `${id}-message`;
450
+ message.textContent = opts.message;
451
+ panel.appendChild(message);
452
+ panel.setAttribute('aria-describedby', message.id);
453
+ }
454
+ // The dialog's own CSS, on the kit's root while the dialog is open (adopted where possible).
455
+ let ownStyles = null;
456
+ if (opts.css) {
457
+ if (root && isShadowRoot(root))
458
+ ownStyles = adoptStyles(root, opts.css);
459
+ else {
460
+ const style = doc.createElement('style');
461
+ style.textContent = opts.css;
462
+ panel.appendChild(style);
463
+ }
464
+ }
465
+ const body = doc.createElement('div');
466
+ body.className = 'wb-body';
467
+ panel.appendChild(body);
468
+ const error = doc.createElement('p');
469
+ error.className = 'wb-error';
470
+ error.id = `${id}-error`;
471
+ error.setAttribute('role', 'alert');
472
+ error.hidden = true;
473
+ panel.appendChild(error);
474
+ const actionRow = doc.createElement('div');
475
+ actionRow.className = opts.stackActions ? 'wb-actions wb-stack' : 'wb-actions';
476
+ panel.appendChild(actionRow);
477
+ let isOpen = true;
478
+ let resolveClosed;
479
+ const closed = new Promise(resolve => { resolveClosed = resolve; });
480
+ const buttons = [];
481
+ let defaultAction = null;
482
+ let autoFocus = null;
483
+ // Busy buttons are aria-disabled, not disabled: a disabled button would drop keyboard focus.
484
+ let busy = false;
485
+ const setBusy = (value) => {
486
+ busy = value;
487
+ for (const button of buttons) {
488
+ if (value) {
489
+ button.setAttribute('aria-disabled', 'true');
490
+ button.setAttribute('aria-busy', 'true');
491
+ }
492
+ else {
493
+ button.removeAttribute('aria-busy');
494
+ if (!button.dataset.disabled)
495
+ button.removeAttribute('aria-disabled');
496
+ }
497
+ }
498
+ };
499
+ const handle = {
500
+ element: panel,
501
+ body,
502
+ get open() { return isOpen; },
503
+ closed,
504
+ close: result => close(result),
505
+ setError(message) {
506
+ error.textContent = message ?? '';
507
+ error.hidden = !message;
508
+ },
509
+ setBusy,
510
+ };
511
+ const run = async (action) => {
512
+ if (!isOpen || busy)
513
+ return;
514
+ let keep = false;
515
+ if (action.onClick) {
516
+ let result;
517
+ try {
518
+ result = action.onClick(handle);
519
+ if (result && typeof result.then === 'function') {
520
+ setBusy(true);
521
+ try {
522
+ result = await result;
523
+ }
524
+ finally {
525
+ if (isOpen)
526
+ setBusy(false);
527
+ }
528
+ }
529
+ }
530
+ catch (err) {
531
+ report(err);
532
+ handle.setError(err instanceof Error && err.message ? err.message : word('kit.failed'));
533
+ return;
534
+ }
535
+ keep = result === false;
536
+ }
537
+ if (!keep && isOpen)
538
+ close('value' in action ? action.value : action.label);
539
+ };
540
+ for (const action of opts.actions ?? []) {
541
+ const button = doc.createElement('button');
542
+ button.type = 'button';
543
+ button.className = `wb-btn wb-${action.tone ?? 'secondary'}`;
544
+ button.textContent = action.label;
545
+ if (action.disabled) {
546
+ button.disabled = true;
547
+ button.dataset.disabled = '1';
548
+ }
549
+ button.addEventListener('click', () => { void run(action); });
550
+ buttons.push(button);
551
+ actionRow.appendChild(button);
552
+ if (action.default && !defaultAction)
553
+ defaultAction = { action, button };
554
+ if (action.autoFocus && !autoFocus)
555
+ autoFocus = button;
556
+ }
557
+ const fill = (content) => {
558
+ if (content === null || content === undefined)
559
+ return;
560
+ if (typeof content === 'string') {
561
+ const p = doc.createElement('p');
562
+ p.className = 'wb-message';
563
+ p.textContent = content;
564
+ body.appendChild(p);
565
+ }
566
+ else if (typeof content === 'function')
567
+ content(body);
568
+ else
569
+ body.appendChild(content);
570
+ };
571
+ try {
572
+ fill(opts.content);
573
+ }
574
+ catch (err) {
575
+ report(err);
576
+ }
577
+ const dismiss = () => { if (isOpen)
578
+ close(opts.dismissValue); };
579
+ const entry = { layer, panel, handle, dismiss, returnTo: deepActiveElement(doc), lastFocus: null };
580
+ const ownerEntry = { close: () => dismiss() };
581
+ owner?.add(ownerEntry);
582
+ function close(result) {
583
+ if (!isOpen)
584
+ return;
585
+ isOpen = false;
586
+ owner?.delete(ownerEntry);
587
+ const index = stack.indexOf(entry);
588
+ const wasTop = index === stack.length - 1;
589
+ if (index >= 0)
590
+ stack.splice(index, 1);
591
+ syncStack();
592
+ layer.setAttribute('inert', '');
593
+ layer.classList.add('wb-closing');
594
+ later(() => { layer.remove(); ownStyles?.remove(); }, reducedMotion() ? 0 : 130);
595
+ // Dialogs opened from this one return focus where this one would have.
596
+ for (const other of stack)
597
+ if (other.returnTo && layer.contains(other.returnTo))
598
+ other.returnTo = entry.returnTo;
599
+ // Focus never lingers in a dialog that is animating out (keys would still go to its fields).
600
+ const active = deepActiveElement(doc);
601
+ if (active && layer.contains(active))
602
+ active.blur();
603
+ if (wasTop) {
604
+ const next = stack[stack.length - 1];
605
+ const back = entry.returnTo;
606
+ if (back?.isConnected && (!next || next.panel.contains(back)))
607
+ focus(back);
608
+ if (next && deepActiveElement(doc) !== back)
609
+ focus(next.lastFocus?.isConnected && next.panel.contains(next.lastFocus) ? next.lastFocus : next.panel);
610
+ }
611
+ try {
612
+ opts.onClose?.(result);
613
+ }
614
+ catch (err) {
615
+ report(err);
616
+ }
617
+ resolveClosed(result);
618
+ maybeTeardown();
619
+ }
620
+ // Keyboard: Tab stays inside; Esc dismisses; Enter runs the default action from a single-line field.
621
+ layer.addEventListener('keydown', event => {
622
+ if (stack[stack.length - 1] !== entry)
623
+ return;
624
+ // Keys that belong to an input method (composing CJK text) are not ours.
625
+ if (event.isComposing || event.keyCode === 229)
626
+ return;
627
+ if (event.key === 'Tab') {
628
+ const list = tabbables(panel);
629
+ const active = deepActiveElement(doc);
630
+ const next = trapIndex(list.length, active ? list.indexOf(active) : -1, event.shiftKey);
631
+ if (list.length === 0) {
632
+ event.preventDefault();
633
+ focus(panel);
634
+ }
635
+ else if (next >= 0) {
636
+ event.preventDefault();
637
+ focus(list[next]);
638
+ }
639
+ }
640
+ else if (event.key === 'Escape') {
641
+ event.preventDefault();
642
+ if (dismissible)
643
+ dismiss();
644
+ }
645
+ else if (event.key === 'Enter' && defaultAction && !defaultAction.button.disabled && !busy) {
646
+ const target = event.composedPath()[0];
647
+ const tag = target?.nodeName;
648
+ const singleLine = tag === 'INPUT' && !/^(checkbox|radio|button|submit|reset|file)$/i.test(target.type);
649
+ const multilineSubmit = tag === 'TEXTAREA' && (event.ctrlKey || event.metaKey);
650
+ if (singleLine || multilineSubmit || target === panel) {
651
+ event.preventDefault();
652
+ void run(defaultAction.action);
653
+ }
654
+ }
655
+ });
656
+ layer.addEventListener('focusin', event => { entry.lastFocus = event.composedPath()[0]; });
657
+ // A backdrop click counts only when the press also started on it (a text selection dragged out
658
+ // of a field must not close the dialog).
659
+ let pressedBackdrop = false;
660
+ backdrop.addEventListener('pointerdown', () => { pressedBackdrop = true; });
661
+ // Pressing the backdrop must not move focus out of the dialog (to <body>, where keys reach the page).
662
+ backdrop.addEventListener('mousedown', event => event.preventDefault());
663
+ // Focus that drops to nothing (a click on a non-focusable spot, a removed element) comes back.
664
+ layer.addEventListener('focusout', event => {
665
+ if (event.relatedTarget)
666
+ return;
667
+ win.setTimeout(() => {
668
+ if (!isOpen || stack[stack.length - 1] !== entry)
669
+ return;
670
+ const active = deepActiveElement(doc);
671
+ if (!active || active === doc.body || active === doc.documentElement)
672
+ focus(entry.lastFocus?.isConnected ? entry.lastFocus : panel);
673
+ }, 0);
674
+ });
675
+ layer.addEventListener('pointerup', event => {
676
+ const onBackdrop = pressedBackdrop && event.target === backdrop;
677
+ pressedBackdrop = false;
678
+ if (onBackdrop && opts.closeOnBackdrop && dismissible)
679
+ dismiss();
680
+ });
681
+ // The page behind never scrolls while a dialog is open.
682
+ layer.addEventListener('wheel', event => {
683
+ let node = event.target;
684
+ while (node && node !== layer) {
685
+ if (node.scrollHeight > node.clientHeight + 1 && /auto|scroll/.test(win.getComputedStyle(node).overflowY)) {
686
+ const down = event.deltaY > 0;
687
+ if (down ? node.scrollTop + node.clientHeight < node.scrollHeight - 1 : node.scrollTop > 0)
688
+ return;
689
+ }
690
+ node = node.parentElement;
691
+ }
692
+ event.preventDefault();
693
+ }, { passive: false });
694
+ layer.append(backdrop, panel);
695
+ layers.appendChild(layer);
696
+ stack.push(entry);
697
+ syncStack();
698
+ let initial = null;
699
+ try {
700
+ initial = typeof opts.initialFocus === 'string' ? panel.querySelector(opts.initialFocus) : opts.initialFocus ?? null;
701
+ }
702
+ catch (err) {
703
+ report(err);
704
+ }
705
+ const firstField = panel.querySelector('.wb-body input:not([type="hidden"]):not([disabled]), .wb-body textarea:not([disabled]), .wb-body select:not([disabled])');
706
+ focus(initial ?? autoFocus ?? firstField ?? defaultAction?.button ?? buttons[0] ?? panel);
707
+ return handle;
708
+ }
709
+ function toast(message, input, owner) {
710
+ ensureRoot();
711
+ const opts = typeof input === 'number' ? { duration: input } : input ?? {};
712
+ const tone = opts.tone ?? 'neutral';
713
+ const text = String(message ?? '');
714
+ let ownerId = 0;
715
+ if (owner) {
716
+ ownerId = ownerIds.get(owner) ?? ++seq;
717
+ ownerIds.set(owner, ownerId);
718
+ }
719
+ const key = `${ownerId}|${tone}|${text}|${opts.action?.label ?? ''}`;
720
+ // The same message again restarts the visible one instead of stacking a copy.
721
+ const same = toasts.find(item => item.key === key);
722
+ if (same) {
723
+ same.restart();
724
+ return { element: same.element, close: same.close, closed: same.closed };
725
+ }
726
+ const element = doc.createElement('div');
727
+ element.className = 'wb-toast';
728
+ element.dataset.tone = tone;
729
+ element.setAttribute('role', tone === 'danger' ? 'alert' : 'status');
730
+ if (tone !== 'neutral') {
731
+ const icon = doc.createElement('span');
732
+ icon.className = 'wb-toast-icon';
733
+ icon.appendChild(svg(ICONS[tone]));
734
+ element.appendChild(icon);
735
+ }
736
+ const label = doc.createElement('span');
737
+ label.className = 'wb-toast-text';
738
+ label.textContent = text;
739
+ element.appendChild(label);
740
+ let resolveClosed;
741
+ const closed = new Promise(resolve => { resolveClosed = resolve; });
742
+ let timer = 0;
743
+ let open = true;
744
+ const duration = toastDuration(opts);
745
+ const ownerEntry = { close: () => close() };
746
+ let held = false;
747
+ const start = () => { win.clearTimeout(timer); if (duration > 0 && !held)
748
+ timer = win.setTimeout(close, duration); };
749
+ const hold = (value) => { held = value; if (value)
750
+ win.clearTimeout(timer);
751
+ else
752
+ start(); };
753
+ function close() {
754
+ if (!open)
755
+ return;
756
+ open = false;
757
+ win.clearTimeout(timer);
758
+ // A focused toast going away hands focus back to the open dialog (or lets go of it).
759
+ const active = deepActiveElement(doc);
760
+ if (active && element.contains(active)) {
761
+ const top = stack[stack.length - 1];
762
+ if (top)
763
+ focus(top.lastFocus?.isConnected && top.panel.contains(top.lastFocus) ? top.lastFocus : top.panel);
764
+ else
765
+ active.blur();
766
+ }
767
+ owner?.delete(ownerEntry);
768
+ const index = toasts.indexOf(item);
769
+ if (index >= 0)
770
+ toasts.splice(index, 1);
771
+ element.classList.add('wb-closing');
772
+ later(() => element.remove(), reducedMotion() ? 0 : 150);
773
+ resolveClosed();
774
+ maybeTeardown();
775
+ }
776
+ if (opts.action) {
777
+ const action = opts.action;
778
+ const button = doc.createElement('button');
779
+ button.type = 'button';
780
+ button.className = 'wb-toast-action';
781
+ button.textContent = action.label;
782
+ button.addEventListener('click', () => {
783
+ try {
784
+ action.onClick();
785
+ }
786
+ catch (err) {
787
+ report(err);
788
+ }
789
+ close();
790
+ });
791
+ element.appendChild(button);
792
+ }
793
+ const dismissButton = doc.createElement('button');
794
+ dismissButton.type = 'button';
795
+ dismissButton.className = 'wb-toast-close';
796
+ dismissButton.setAttribute('aria-label', word('kit.dismiss'));
797
+ dismissButton.appendChild(svg(ICONS.close));
798
+ dismissButton.addEventListener('click', () => close());
799
+ element.appendChild(dismissButton);
800
+ // Hovering or focusing a toast holds it on screen.
801
+ element.addEventListener('pointerenter', () => hold(true));
802
+ element.addEventListener('pointerleave', () => hold(false));
803
+ element.addEventListener('focusin', () => hold(true));
804
+ element.addEventListener('focusout', () => hold(false));
805
+ element.addEventListener('keydown', event => { if (event.key === 'Escape')
806
+ close(); });
807
+ const item = { element, key, restart: start, close, closed };
808
+ toasts.push(item);
809
+ owner?.add(ownerEntry);
810
+ toastRegion.appendChild(element);
811
+ while (toasts.length > MAX_TOASTS)
812
+ toasts[0].close();
813
+ start();
814
+ return { element, close, closed };
815
+ }
816
+ const text = (value) => (typeof value === 'string' ? value : value == null ? '' : String(value));
817
+ function alert(input, owner) {
818
+ const opts = typeof input === 'string' ? { message: input } : input ?? {};
819
+ const handle = modal({
820
+ role: 'alertdialog', title: text(opts.title) || undefined, message: text(opts.message) || undefined, tone: opts.tone,
821
+ width: 'sm', closeOnBackdrop: true, closeButton: false, stackActions: true,
822
+ actions: [{ label: opts.okLabel || word('kit.ok'), tone: 'primary', default: true, autoFocus: true, value: undefined }],
823
+ }, owner);
824
+ return handle.closed.then(() => undefined);
825
+ }
826
+ function confirm(input, owner) {
827
+ const opts = typeof input === 'string' ? { message: input } : input ?? {};
828
+ const danger = !!opts.danger;
829
+ const handle = modal({
830
+ role: 'alertdialog', title: text(opts.title) || undefined, message: text(opts.message) || undefined,
831
+ width: 'sm', closeOnBackdrop: true, closeButton: false, stackActions: true, dismissValue: false,
832
+ actions: [
833
+ // A destructive confirmation starts on Cancel, so Enter never destroys by accident.
834
+ { label: opts.confirmLabel || (danger ? word('kit.delete') : word('kit.ok')), tone: danger ? 'danger' : 'primary', value: true, default: !danger, autoFocus: !danger },
835
+ { label: opts.cancelLabel || word('kit.cancel'), tone: 'secondary', value: false, autoFocus: danger },
836
+ ],
837
+ }, owner);
838
+ return handle.closed.then(result => result === true);
839
+ }
840
+ function prompt(input, owner) {
841
+ const opts = typeof input === 'string' ? { title: input } : input ?? {};
842
+ const field = doc.createElement(opts.multiline ? 'textarea' : 'input');
843
+ if (field instanceof win.HTMLInputElement)
844
+ field.type = 'text';
845
+ field.className = opts.multiline ? '' : 'wb-input';
846
+ field.value = text(opts.value);
847
+ if (opts.placeholder)
848
+ field.placeholder = opts.placeholder;
849
+ if (opts.maxLength && opts.maxLength > 0)
850
+ field.maxLength = Math.floor(opts.maxLength);
851
+ field.spellcheck = true;
852
+ let tried = false;
853
+ let handle;
854
+ const check = () => {
855
+ let message = null;
856
+ try {
857
+ message = validationMessage(opts.validate?.(field.value), word('kit.checkValue'));
858
+ }
859
+ catch (err) {
860
+ message = err instanceof Error ? err.message : word('kit.checkValue');
861
+ }
862
+ field.setAttribute('aria-invalid', message ? 'true' : 'false');
863
+ handle.setError(message);
864
+ return !message;
865
+ };
866
+ field.addEventListener('input', () => { if (tried)
867
+ check(); });
868
+ handle = modal({
869
+ role: 'dialog', title: text(opts.title) || undefined, message: text(opts.message) || undefined, width: opts.width ?? 'sm', dismissValue: null,
870
+ content: body => {
871
+ const wrap = doc.createElement('label');
872
+ wrap.className = 'wb-field';
873
+ if (opts.label) {
874
+ const caption = doc.createElement('span');
875
+ caption.className = 'wb-label';
876
+ caption.textContent = opts.label;
877
+ wrap.appendChild(caption);
878
+ }
879
+ else
880
+ field.setAttribute('aria-label', text(opts.title) || word('kit.value'));
881
+ wrap.appendChild(field);
882
+ body.appendChild(wrap);
883
+ },
884
+ actions: [
885
+ { label: opts.cancelLabel || word('kit.cancel'), tone: 'secondary', value: null },
886
+ { label: opts.confirmLabel || word('kit.ok'), tone: 'primary', default: true, onClick: () => { tried = true; return check() ? undefined : false; }, value: undefined },
887
+ ],
888
+ initialFocus: field,
889
+ }, owner);
890
+ field.setAttribute('aria-describedby', `${handle.element.querySelector('.wb-error')?.id ?? ''}`);
891
+ // The OK action's result is the field's value at the moment it closed.
892
+ return handle.closed.then(result => (result === null || !tried ? null : field.value));
893
+ }
894
+ const make = (owner) => Object.freeze({
895
+ toast: (message, options) => toast(message, options, owner),
896
+ alert: (options) => alert(options, owner),
897
+ confirm: (options) => confirm(options, owner),
898
+ prompt: (options) => prompt(options, owner),
899
+ dialog: (options) => modal({ closeOnBackdrop: false, ...options, role: 'dialog' }, owner),
900
+ modal: (options) => modal(options, owner),
901
+ h: (tag, props, ...children) => buildElement(doc, tag, props, ...children),
902
+ });
903
+ const base = make();
904
+ return Object.freeze({
905
+ ...base,
906
+ scoped() {
907
+ const owner = new Set();
908
+ return {
909
+ kit: make(owner),
910
+ dispose() {
911
+ for (const entry of [...owner]) {
912
+ try {
913
+ entry.close();
914
+ }
915
+ catch (err) {
916
+ report(err);
917
+ }
918
+ }
919
+ owner.clear();
920
+ },
921
+ };
922
+ },
923
+ openModals: () => stack.length,
924
+ });
925
+ }
926
+ //# sourceMappingURL=ui.js.map