waybetter 0.1.0 → 0.6.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.
- package/README.md +143 -9
- package/bin/waybetter.mjs +46 -0
- package/build/runtime.mjs +105 -0
- package/cli/buildenv.mjs +130 -0
- package/cli/dev.mjs +97 -0
- package/cli/i18n.mjs +382 -0
- package/cli/manifests.mjs +383 -0
- package/cli/privacy.mjs +768 -0
- package/cli/release.mjs +444 -0
- package/cli/remote.mjs +260 -0
- package/cli/store.mjs +326 -0
- package/cli/strings.mjs +121 -0
- package/dist/runtime/column.d.ts +93 -0
- package/dist/runtime/column.js +451 -0
- package/dist/runtime/column.js.map +1 -0
- package/dist/runtime/commandReport.d.ts +19 -0
- package/dist/runtime/commandReport.js +47 -0
- package/dist/runtime/commandReport.js.map +1 -0
- package/dist/runtime/commands.d.ts +72 -0
- package/dist/runtime/commands.js +214 -0
- package/dist/runtime/commands.js.map +1 -0
- package/dist/runtime/devErrors.d.ts +16 -0
- package/dist/runtime/devErrors.js +30 -0
- package/dist/runtime/devErrors.js.map +1 -0
- package/dist/runtime/domHub.d.ts +21 -0
- package/dist/runtime/domHub.js +139 -4
- package/dist/runtime/domHub.js.map +1 -1
- package/dist/runtime/featureModules.d.ts +157 -0
- package/dist/runtime/featureModules.js +484 -0
- package/dist/runtime/featureModules.js.map +1 -0
- package/dist/runtime/featureTable.d.ts +144 -0
- package/dist/runtime/featureTable.js +223 -0
- package/dist/runtime/featureTable.js.map +1 -0
- package/dist/runtime/fonts.d.ts +22 -0
- package/dist/runtime/fonts.js +72 -0
- package/dist/runtime/fonts.js.map +1 -0
- package/dist/runtime/i18n.d.ts +138 -0
- package/dist/runtime/i18n.js +614 -0
- package/dist/runtime/i18n.js.map +1 -0
- package/dist/runtime/i18nCheck.d.ts +30 -0
- package/dist/runtime/i18nCheck.js +224 -0
- package/dist/runtime/i18nCheck.js.map +1 -0
- package/dist/runtime/pageLang.d.ts +8 -0
- package/dist/runtime/pageLang.js +64 -0
- package/dist/runtime/pageLang.js.map +1 -0
- package/dist/runtime/pageStrings.d.ts +35 -0
- package/dist/runtime/pageStrings.js +76 -0
- package/dist/runtime/pageStrings.js.map +1 -0
- package/dist/runtime/panelButton.d.ts +33 -0
- package/dist/runtime/panelButton.js +83 -0
- package/dist/runtime/panelButton.js.map +1 -0
- package/dist/runtime/permissions.d.ts +47 -0
- package/dist/runtime/permissions.js +141 -0
- package/dist/runtime/permissions.js.map +1 -0
- package/dist/runtime/pluginApi.d.ts +51 -2
- package/dist/runtime/pluginApi.js +137 -22
- package/dist/runtime/pluginApi.js.map +1 -1
- package/dist/runtime/pluginI18n.d.ts +29 -0
- package/dist/runtime/pluginI18n.js +119 -0
- package/dist/runtime/pluginI18n.js.map +1 -0
- package/dist/runtime/settingsForm.d.ts +66 -0
- package/dist/runtime/settingsForm.js +114 -0
- package/dist/runtime/settingsForm.js.map +1 -0
- package/dist/runtime/site.d.ts +5 -0
- package/dist/runtime/site.js +1 -1
- package/dist/runtime/site.js.map +1 -1
- package/dist/runtime/siteRuntime.d.ts +144 -0
- package/dist/runtime/siteRuntime.js +175 -0
- package/dist/runtime/siteRuntime.js.map +1 -0
- package/dist/runtime/stringPackHook.d.ts +84 -0
- package/dist/runtime/stringPackHook.js +220 -0
- package/dist/runtime/stringPackHook.js.map +1 -0
- package/dist/runtime/stringPackTranslate.d.ts +59 -0
- package/dist/runtime/stringPackTranslate.js +264 -0
- package/dist/runtime/stringPackTranslate.js.map +1 -0
- package/dist/runtime/stringPacks.d.ts +200 -0
- package/dist/runtime/stringPacks.js +474 -0
- package/dist/runtime/stringPacks.js.map +1 -0
- package/dist/runtime/styles.d.ts +15 -0
- package/dist/runtime/styles.js +81 -0
- package/dist/runtime/styles.js.map +1 -0
- package/dist/runtime/textNet.d.ts +15 -0
- package/dist/runtime/textNet.js +131 -0
- package/dist/runtime/textNet.js.map +1 -0
- package/dist/runtime/themeApi.d.ts +121 -0
- package/dist/runtime/themeApi.js +220 -0
- package/dist/runtime/themeApi.js.map +1 -0
- package/dist/runtime/themeChoice.d.ts +52 -0
- package/dist/runtime/themeChoice.js +69 -0
- package/dist/runtime/themeChoice.js.map +1 -0
- package/dist/runtime/themeComponents.d.ts +435 -0
- package/dist/runtime/themeComponents.js +463 -0
- package/dist/runtime/themeComponents.js.map +1 -0
- package/dist/runtime/themeDecor.d.ts +73 -0
- package/dist/runtime/themeDecor.js +131 -0
- package/dist/runtime/themeDecor.js.map +1 -0
- package/dist/runtime/themeDoctor.d.ts +11 -0
- package/dist/runtime/themeDoctor.js +51 -0
- package/dist/runtime/themeDoctor.js.map +1 -0
- package/dist/runtime/themeIcons.d.ts +130 -0
- package/dist/runtime/themeIcons.js +263 -0
- package/dist/runtime/themeIcons.js.map +1 -0
- package/dist/runtime/themeRuntime.d.ts +100 -0
- package/dist/runtime/themeRuntime.js +497 -0
- package/dist/runtime/themeRuntime.js.map +1 -0
- package/dist/runtime/themeSchema.d.ts +10 -0
- package/dist/runtime/themeSchema.js +150 -0
- package/dist/runtime/themeSchema.js.map +1 -0
- package/dist/runtime/themeSpec.d.ts +259 -0
- package/dist/runtime/themeSpec.js +439 -0
- package/dist/runtime/themeSpec.js.map +1 -0
- package/dist/runtime/themeTokens.d.ts +12 -0
- package/dist/runtime/themeTokens.js +14 -6
- package/dist/runtime/themeTokens.js.map +1 -1
- package/dist/runtime/themeVariants.d.ts +30 -0
- package/dist/runtime/themeVariants.js +53 -0
- package/dist/runtime/themeVariants.js.map +1 -0
- package/dist/runtime/ui.d.ts +192 -0
- package/dist/runtime/ui.js +926 -0
- package/dist/runtime/ui.js.map +1 -0
- package/dist/shell/InstallDialog.d.ts +40 -0
- package/dist/shell/InstallDialog.js +170 -0
- package/dist/shell/InstallDialog.js.map +1 -0
- package/dist/shell/StoreView.d.ts +66 -0
- package/dist/shell/StoreView.js +103 -0
- package/dist/shell/StoreView.js.map +1 -0
- package/dist/shell/addressModel.js.map +1 -1
- package/dist/shell/chrome.js.map +1 -1
- package/dist/shell/deckModel.d.ts +117 -0
- package/dist/shell/deckModel.js +267 -0
- package/dist/shell/deckModel.js.map +1 -0
- package/dist/shell/frame/AccountSwitcher.d.ts +42 -0
- package/dist/shell/frame/AccountSwitcher.js +243 -0
- package/dist/shell/frame/AccountSwitcher.js.map +1 -0
- package/dist/shell/frame/AddColumnDialog.d.ts +8 -0
- package/dist/shell/frame/AddColumnDialog.js +81 -0
- package/dist/shell/frame/AddColumnDialog.js.map +1 -0
- package/dist/shell/frame/AppMenu.d.ts +21 -0
- package/dist/shell/frame/AppMenu.js +642 -0
- package/dist/shell/frame/AppMenu.js.map +1 -0
- package/dist/shell/frame/CommandPalette.d.ts +7 -0
- package/dist/shell/frame/CommandPalette.js +118 -0
- package/dist/shell/frame/CommandPalette.js.map +1 -0
- package/dist/shell/frame/DeckView.d.ts +13 -0
- package/dist/shell/frame/DeckView.js +287 -0
- package/dist/shell/frame/DeckView.js.map +1 -0
- package/dist/shell/frame/FindBar.d.ts +10 -0
- package/dist/shell/frame/FindBar.js +108 -0
- package/dist/shell/frame/FindBar.js.map +1 -0
- package/dist/shell/frame/MediaPanel.d.ts +8 -0
- package/dist/shell/frame/MediaPanel.js +76 -0
- package/dist/shell/frame/MediaPanel.js.map +1 -0
- package/dist/shell/frame/NewTabPage.d.ts +9 -0
- package/dist/shell/frame/NewTabPage.js +71 -0
- package/dist/shell/frame/NewTabPage.js.map +1 -0
- package/dist/shell/frame/PaneTheme.d.ts +16 -0
- package/dist/shell/frame/PaneTheme.js +67 -0
- package/dist/shell/frame/PaneTheme.js.map +1 -0
- package/dist/shell/frame/ReportDialog.d.ts +3 -0
- package/dist/shell/frame/ReportDialog.js +95 -0
- package/dist/shell/frame/ReportDialog.js.map +1 -0
- package/dist/shell/frame/Shell.d.ts +8 -0
- package/dist/shell/frame/Shell.js +1131 -0
- package/dist/shell/frame/Shell.js.map +1 -0
- package/dist/shell/frame/Titlebar.d.ts +16 -0
- package/dist/shell/frame/Titlebar.js +77 -0
- package/dist/shell/frame/Titlebar.js.map +1 -0
- package/dist/shell/frame/TitlebarExtras.d.ts +4 -0
- package/dist/shell/frame/TitlebarExtras.js +202 -0
- package/dist/shell/frame/TitlebarExtras.js.map +1 -0
- package/dist/shell/frame/WebPanel.d.ts +4 -0
- package/dist/shell/frame/WebPanel.js +182 -0
- package/dist/shell/frame/WebPanel.js.map +1 -0
- package/dist/shell/frame/accountModel.d.ts +49 -0
- package/dist/shell/frame/accountModel.js +67 -0
- package/dist/shell/frame/accountModel.js.map +1 -0
- package/dist/shell/frame/accountSwitcher.css +91 -0
- package/dist/shell/frame/appI18n.d.ts +44 -0
- package/dist/shell/frame/appI18n.js +64 -0
- package/dist/shell/frame/appI18n.js.map +1 -0
- package/dist/shell/frame/appMenu.css +121 -0
- package/dist/shell/frame/backupModel.d.ts +38 -0
- package/dist/shell/frame/backupModel.js +192 -0
- package/dist/shell/frame/backupModel.js.map +1 -0
- package/dist/shell/frame/boot.d.ts +8 -0
- package/dist/shell/frame/boot.js +35 -0
- package/dist/shell/frame/boot.js.map +1 -0
- package/dist/shell/frame/brand.d.ts +1 -0
- package/dist/shell/frame/brand.js +4 -0
- package/dist/shell/frame/brand.js.map +1 -0
- package/dist/shell/frame/bridge.d.ts +35 -0
- package/dist/shell/frame/bridge.js +70 -0
- package/dist/shell/frame/bridge.js.map +1 -0
- package/dist/shell/frame/chromeTheme.d.ts +29 -0
- package/dist/shell/frame/chromeTheme.js +81 -0
- package/dist/shell/frame/chromeTheme.js.map +1 -0
- package/dist/shell/frame/deck.css +209 -0
- package/dist/shell/frame/deckStore.d.ts +89 -0
- package/dist/shell/frame/deckStore.js +239 -0
- package/dist/shell/frame/deckStore.js.map +1 -0
- package/dist/shell/frame/devHost.d.ts +35 -0
- package/dist/shell/frame/devHost.js +80 -0
- package/dist/shell/frame/devHost.js.map +1 -0
- package/dist/shell/frame/devModel.d.ts +7 -0
- package/dist/shell/frame/devModel.js +17 -0
- package/dist/shell/frame/devModel.js.map +1 -0
- package/dist/shell/frame/diag.d.ts +7 -0
- package/dist/shell/frame/diag.js +39 -0
- package/dist/shell/frame/diag.js.map +1 -0
- package/dist/shell/frame/findBar.css +14 -0
- package/dist/shell/frame/frameMessages.d.ts +248 -0
- package/dist/shell/frame/frameMessages.js +252 -0
- package/dist/shell/frame/frameMessages.js.map +1 -0
- package/dist/shell/frame/hooks.d.ts +39 -0
- package/dist/shell/frame/hooks.js +97 -0
- package/dist/shell/frame/hooks.js.map +1 -0
- package/dist/shell/frame/i18n.d.ts +27 -0
- package/dist/shell/frame/i18n.js +66 -0
- package/dist/shell/frame/i18n.js.map +1 -0
- package/dist/shell/frame/iconPack.d.ts +11 -0
- package/dist/shell/frame/iconPack.js +45 -0
- package/dist/shell/frame/iconPack.js.map +1 -0
- package/dist/shell/frame/icons.d.ts +71 -0
- package/dist/shell/frame/icons.js +75 -0
- package/dist/shell/frame/icons.js.map +1 -0
- package/dist/shell/frame/index.d.ts +34 -0
- package/dist/shell/frame/index.js +43 -0
- package/dist/shell/frame/index.js.map +1 -0
- package/dist/shell/frame/language.d.ts +58 -0
- package/dist/shell/frame/language.js +165 -0
- package/dist/shell/frame/language.js.map +1 -0
- package/dist/shell/frame/mediaModel.d.ts +34 -0
- package/dist/shell/frame/mediaModel.js +53 -0
- package/dist/shell/frame/mediaModel.js.map +1 -0
- package/dist/shell/frame/menuMessages.en.json +54 -0
- package/dist/shell/frame/menuModel.d.ts +277 -0
- package/dist/shell/frame/menuModel.js +293 -0
- package/dist/shell/frame/menuModel.js.map +1 -0
- package/dist/shell/frame/paletteModel.d.ts +139 -0
- package/dist/shell/frame/paletteModel.js +112 -0
- package/dist/shell/frame/paletteModel.js.map +1 -0
- package/dist/shell/frame/paneTheme.css +9 -0
- package/dist/shell/frame/pluginCommandStore.d.ts +23 -0
- package/dist/shell/frame/pluginCommandStore.js +70 -0
- package/dist/shell/frame/pluginCommandStore.js.map +1 -0
- package/dist/shell/frame/reportModel.d.ts +52 -0
- package/dist/shell/frame/reportModel.js +105 -0
- package/dist/shell/frame/reportModel.js.map +1 -0
- package/dist/shell/frame/safeStore.d.ts +7 -0
- package/dist/shell/frame/safeStore.js +40 -0
- package/dist/shell/frame/safeStore.js.map +1 -0
- package/dist/shell/frame/searchInput.d.ts +2 -0
- package/dist/shell/frame/searchInput.js +11 -0
- package/dist/shell/frame/searchInput.js.map +1 -0
- package/dist/shell/frame/shell.css +629 -0
- package/dist/shell/frame/site.d.ts +206 -0
- package/dist/shell/frame/site.js +22 -0
- package/dist/shell/frame/site.js.map +1 -0
- package/dist/shell/frame/state.d.ts +99 -0
- package/dist/shell/frame/state.js +284 -0
- package/dist/shell/frame/state.js.map +1 -0
- package/dist/shell/frame/syncGate.d.ts +10 -0
- package/dist/shell/frame/syncGate.js +36 -0
- package/dist/shell/frame/syncGate.js.map +1 -0
- package/dist/shell/frame/syncHost.d.ts +35 -0
- package/dist/shell/frame/syncHost.js +85 -0
- package/dist/shell/frame/syncHost.js.map +1 -0
- package/dist/shell/frame/tabStill.d.ts +2 -0
- package/dist/shell/frame/tabStill.js +62 -0
- package/dist/shell/frame/tabStill.js.map +1 -0
- package/dist/shell/frame/themeScopes.d.ts +29 -0
- package/dist/shell/frame/themeScopes.js +61 -0
- package/dist/shell/frame/themeScopes.js.map +1 -0
- package/dist/shell/frame/themeStore.d.ts +99 -0
- package/dist/shell/frame/themeStore.js +252 -0
- package/dist/shell/frame/themeStore.js.map +1 -0
- package/dist/shell/frame/windowBinding.d.ts +2 -0
- package/dist/shell/frame/windowBinding.js +7 -0
- package/dist/shell/frame/windowBinding.js.map +1 -0
- package/dist/shell/frame/workspaceModel.d.ts +82 -0
- package/dist/shell/frame/workspaceModel.js +250 -0
- package/dist/shell/frame/workspaceModel.js.map +1 -0
- package/dist/shell/frame/workspaces.css +84 -0
- package/dist/shell/frame/workspaces.d.ts +22 -0
- package/dist/shell/frame/workspaces.js +125 -0
- package/dist/shell/frame/workspaces.js.map +1 -0
- package/dist/shell/frame/zoomStore.d.ts +10 -0
- package/dist/shell/frame/zoomStore.js +65 -0
- package/dist/shell/frame/zoomStore.js.map +1 -0
- package/dist/shell/installController.d.ts +122 -0
- package/dist/shell/installController.js +146 -0
- package/dist/shell/installController.js.map +1 -0
- package/dist/shell/installerMessages.d.ts +30 -0
- package/dist/shell/installerMessages.js +36 -0
- package/dist/shell/installerMessages.js.map +1 -0
- package/dist/shell/linkDrop.js.map +1 -1
- package/dist/shell/messages.d.ts +16 -0
- package/dist/shell/messages.js +27 -0
- package/dist/shell/messages.js.map +1 -0
- package/dist/shell/palette.js.map +1 -1
- package/dist/shell/paneModel.d.ts +22 -0
- package/dist/shell/paneModel.js +24 -1
- package/dist/shell/paneModel.js.map +1 -1
- package/dist/shell/panel/Appearance.d.ts +8 -0
- package/dist/shell/panel/Appearance.js +256 -0
- package/dist/shell/panel/Appearance.js.map +1 -0
- package/dist/shell/panel/BackupPanel.d.ts +5 -0
- package/dist/shell/panel/BackupPanel.js +198 -0
- package/dist/shell/panel/BackupPanel.js.map +1 -0
- package/dist/shell/panel/DeveloperPanel.d.ts +4 -0
- package/dist/shell/panel/DeveloperPanel.js +51 -0
- package/dist/shell/panel/DeveloperPanel.js.map +1 -0
- package/dist/shell/panel/FeatureToggles.d.ts +13 -0
- package/dist/shell/panel/FeatureToggles.js +42 -0
- package/dist/shell/panel/FeatureToggles.js.map +1 -0
- package/dist/shell/panel/Home.d.ts +3 -0
- package/dist/shell/panel/Home.js +23 -0
- package/dist/shell/panel/Home.js.map +1 -0
- package/dist/shell/panel/IconPackInstall.d.ts +15 -0
- package/dist/shell/panel/IconPackInstall.js +41 -0
- package/dist/shell/panel/IconPackInstall.js.map +1 -0
- package/dist/shell/panel/LanguageSettings.d.ts +1 -0
- package/dist/shell/panel/LanguageSettings.js +54 -0
- package/dist/shell/panel/LanguageSettings.js.map +1 -0
- package/dist/shell/panel/MakePanel.d.ts +3 -0
- package/dist/shell/panel/MakePanel.js +237 -0
- package/dist/shell/panel/MakePanel.js.map +1 -0
- package/dist/shell/panel/MarkdownDoc.d.ts +6 -0
- package/dist/shell/panel/MarkdownDoc.js +35 -0
- package/dist/shell/panel/MarkdownDoc.js.map +1 -0
- package/dist/shell/panel/Modes.d.ts +40 -0
- package/dist/shell/panel/Modes.js +270 -0
- package/dist/shell/panel/Modes.js.map +1 -0
- package/dist/shell/panel/NavIcon.d.ts +4 -0
- package/dist/shell/panel/NavIcon.js +6 -0
- package/dist/shell/panel/NavIcon.js.map +1 -0
- package/dist/shell/panel/Panel.d.ts +7 -0
- package/dist/shell/panel/Panel.js +139 -0
- package/dist/shell/panel/Panel.js.map +1 -0
- package/dist/shell/panel/PluginFeatures.d.ts +24 -0
- package/dist/shell/panel/PluginFeatures.js +165 -0
- package/dist/shell/panel/PluginFeatures.js.map +1 -0
- package/dist/shell/panel/Plugins.d.ts +33 -0
- package/dist/shell/panel/Plugins.js +112 -0
- package/dist/shell/panel/Plugins.js.map +1 -0
- package/dist/shell/panel/PowerSettings.d.ts +4 -0
- package/dist/shell/panel/PowerSettings.js +52 -0
- package/dist/shell/panel/PowerSettings.js.map +1 -0
- package/dist/shell/panel/Settings.d.ts +23 -0
- package/dist/shell/panel/Settings.js +116 -0
- package/dist/shell/panel/Settings.js.map +1 -0
- package/dist/shell/panel/ShareSetup.d.ts +8 -0
- package/dist/shell/panel/ShareSetup.js +130 -0
- package/dist/shell/panel/ShareSetup.js.map +1 -0
- package/dist/shell/panel/StoreSection.d.ts +6 -0
- package/dist/shell/panel/StoreSection.js +48 -0
- package/dist/shell/panel/StoreSection.js.map +1 -0
- package/dist/shell/panel/StringPacks.d.ts +11 -0
- package/dist/shell/panel/StringPacks.js +106 -0
- package/dist/shell/panel/StringPacks.js.map +1 -0
- package/dist/shell/panel/ThemeBuilder.d.ts +5 -0
- package/dist/shell/panel/ThemeBuilder.js +205 -0
- package/dist/shell/panel/ThemeBuilder.js.map +1 -0
- package/dist/shell/panel/backup.css +34 -0
- package/dist/shell/panel/builder.css +232 -0
- package/dist/shell/panel/builderModel.d.ts +55 -0
- package/dist/shell/panel/builderModel.js +95 -0
- package/dist/shell/panel/builderModel.js.map +1 -0
- package/dist/shell/panel/compare.d.ts +10 -0
- package/dist/shell/panel/compare.js +51 -0
- package/dist/shell/panel/compare.js.map +1 -0
- package/dist/shell/panel/controls.d.ts +135 -0
- package/dist/shell/panel/controls.js +213 -0
- package/dist/shell/panel/controls.js.map +1 -0
- package/dist/shell/panel/featureModel.d.ts +69 -0
- package/dist/shell/panel/featureModel.js +79 -0
- package/dist/shell/panel/featureModel.js.map +1 -0
- package/dist/shell/panel/fonts/FontBrowser.d.ts +10 -0
- package/dist/shell/panel/fonts/FontBrowser.js +189 -0
- package/dist/shell/panel/fonts/FontBrowser.js.map +1 -0
- package/dist/shell/panel/fonts/FontRoles.d.ts +20 -0
- package/dist/shell/panel/fonts/FontRoles.js +77 -0
- package/dist/shell/panel/fonts/FontRoles.js.map +1 -0
- package/dist/shell/panel/fonts/catalog.d.ts +37 -0
- package/dist/shell/panel/fonts/catalog.js +68 -0
- package/dist/shell/panel/fonts/catalog.js.map +1 -0
- package/dist/shell/panel/fonts/googleFontsCatalog.json +1 -0
- package/dist/shell/panel/index.d.ts +20 -0
- package/dist/shell/panel/index.js +34 -0
- package/dist/shell/panel/index.js.map +1 -0
- package/dist/shell/panel/languageModel.d.ts +5 -0
- package/dist/shell/panel/languageModel.js +7 -0
- package/dist/shell/panel/languageModel.js.map +1 -0
- package/dist/shell/panel/make.css +72 -0
- package/dist/shell/panel/makeModel.d.ts +32 -0
- package/dist/shell/panel/makeModel.js +84 -0
- package/dist/shell/panel/makeModel.js.map +1 -0
- package/dist/shell/panel/markdown.d.ts +46 -0
- package/dist/shell/panel/markdown.js +100 -0
- package/dist/shell/panel/markdown.js.map +1 -0
- package/dist/shell/panel/nav.d.ts +12 -0
- package/dist/shell/panel/nav.js +50 -0
- package/dist/shell/panel/nav.js.map +1 -0
- package/dist/shell/panel/panel.css +401 -0
- package/dist/shell/panel/panelMessages.d.ts +763 -0
- package/dist/shell/panel/panelMessages.js +767 -0
- package/dist/shell/panel/panelMessages.js.map +1 -0
- package/dist/shell/panel/plugins.css +59 -0
- package/dist/shell/panel/setupModel.d.ts +87 -0
- package/dist/shell/panel/setupModel.js +152 -0
- package/dist/shell/panel/setupModel.js.map +1 -0
- package/dist/shell/panel/site.d.ts +259 -0
- package/dist/shell/panel/site.js +25 -0
- package/dist/shell/panel/site.js.map +1 -0
- package/dist/shell/panel/strings.css +7 -0
- package/dist/shell/panel/themes.d.ts +10 -0
- package/dist/shell/panel/themes.js +8 -0
- package/dist/shell/panel/themes.js.map +1 -0
- package/dist/shell/profileModel.d.ts +3 -2
- package/dist/shell/profileModel.js +7 -5
- package/dist/shell/profileModel.js.map +1 -1
- package/dist/shell/store.css +230 -0
- package/dist/shell/storeMessages.d.ts +169 -0
- package/dist/shell/storeMessages.js +172 -0
- package/dist/shell/storeMessages.js.map +1 -0
- package/dist/shell/storeModel.d.ts +181 -0
- package/dist/shell/storeModel.js +133 -0
- package/dist/shell/storeModel.js.map +1 -0
- package/dist/shell/stringPacksModel.d.ts +44 -0
- package/dist/shell/stringPacksModel.js +94 -0
- package/dist/shell/stringPacksModel.js.map +1 -0
- package/dist/shell/updateModel.d.ts +2 -1
- package/dist/shell/updateModel.js +5 -4
- package/dist/shell/updateModel.js.map +1 -1
- package/dist/shell/windowControls.d.ts +4 -0
- package/dist/shell/windowControls.js +5 -0
- package/dist/shell/windowControls.js.map +1 -1
- package/i18n-config.schema.json +29 -0
- package/i18n-meta.schema.json +26 -0
- package/i18n.schema.json +42 -0
- package/icons.schema.json +646 -0
- package/languages.schema.json +23 -0
- package/locales/ar-x-fm.json +11 -0
- package/locales/ar.json +1281 -0
- package/locales/bg.json +1280 -0
- package/locales/bn.json +1280 -0
- package/locales/ca.json +1280 -0
- package/locales/cs.json +1280 -0
- package/locales/da.json +1280 -0
- package/locales/de.json +1280 -0
- package/locales/el.json +1280 -0
- package/locales/en-GB.json +27 -0
- package/locales/en.json +1280 -0
- package/locales/en.meta.json +51 -0
- package/locales/es.json +1280 -0
- package/locales/eu.json +1280 -0
- package/locales/fa.json +1281 -0
- package/locales/fi.json +1280 -0
- package/locales/fil.json +1280 -0
- package/locales/fr.json +1280 -0
- package/locales/ga.json +1280 -0
- package/locales/gl.json +1280 -0
- package/locales/gu.json +1280 -0
- package/locales/ha.json +1280 -0
- package/locales/he.json +1281 -0
- package/locales/hi.json +1280 -0
- package/locales/hr.json +1280 -0
- package/locales/hu.json +1279 -0
- package/locales/id.json +1280 -0
- package/locales/ig.json +1280 -0
- package/locales/it.json +1280 -0
- package/locales/ja.json +1280 -0
- package/locales/kn.json +1280 -0
- package/locales/ko.json +1280 -0
- package/locales/mr.json +1280 -0
- package/locales/ms.json +1280 -0
- package/locales/nb.json +1280 -0
- package/locales/nl.json +1280 -0
- package/locales/pl.json +1280 -0
- package/locales/pt.json +1280 -0
- package/locales/ro.json +1280 -0
- package/locales/ru.json +1280 -0
- package/locales/sk.json +1280 -0
- package/locales/sr.json +1280 -0
- package/locales/sv.json +1280 -0
- package/locales/ta.json +1280 -0
- package/locales/th.json +1280 -0
- package/locales/tr.json +1280 -0
- package/locales/uk.json +1280 -0
- package/locales/ur.json +1281 -0
- package/locales/vi.json +1280 -0
- package/locales/yo.json +1280 -0
- package/locales/zh-Hans.json +1280 -0
- package/locales/zh-Hant.json +1280 -0
- package/package.json +148 -6
- package/store-catalog.schema.json +65 -0
- package/theme.schema.json +4659 -0
- package/windows/installer.nsi +926 -0
- package/windows/languages/Basque.nsh +28 -0
- package/windows/languages/Catalan.nsh +28 -0
- package/windows/languages/Croatian.nsh +28 -0
- package/windows/languages/Czech.nsh +28 -0
- package/windows/languages/Danish.nsh +28 -0
- package/windows/languages/Finnish.nsh +28 -0
- package/windows/languages/Galician.nsh +28 -0
- package/windows/languages/Greek.nsh +28 -0
- package/windows/languages/Hungarian.nsh +28 -0
- package/windows/languages/Indonesian.nsh +28 -0
- package/windows/languages/Irish.nsh +28 -0
- package/windows/languages/Malay.nsh +28 -0
- package/windows/languages/Polish.nsh +28 -0
- package/windows/languages/Romanian.nsh +28 -0
- package/windows/languages/Serbian.nsh +28 -0
- package/windows/languages/Slovak.nsh +28 -0
- package/windows/languages/Thai.nsh +28 -0
|
@@ -2,20 +2,77 @@
|
|
|
2
2
|
// re-implement DOM watching, route tracking, local storage and cleanup. Everything here is local to
|
|
3
3
|
// the page: no network requests, no account actions. A site adapter adds its own readers (e.g. a
|
|
4
4
|
// post/message parser) on top of this core.
|
|
5
|
+
//
|
|
6
|
+
// `ui` (toasts, alerts, confirmations, prompts, dialogs; ui.ts) and `theme` (the active theme as
|
|
7
|
+
// values; themeApi.ts) are part of the core: the site adapter only supplies its theme source.
|
|
8
|
+
import { createThemeApi } from "./themeApi.js";
|
|
9
|
+
import { createUiKit } from "./ui.js";
|
|
10
|
+
import { adoptStyles } from "./styles.js";
|
|
11
|
+
import { observeSelector } from "./domHub.js";
|
|
12
|
+
import { createCommandRegistry } from "./commands.js";
|
|
13
|
+
import { createPluginI18n } from "./pluginI18n.js";
|
|
5
14
|
const NAMESPACE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
6
15
|
const ROUTE_POLL_MS = 300;
|
|
7
16
|
const CHORD_MS = 1500;
|
|
8
17
|
/** True when a key event belongs to text entry, not to page shortcuts. */
|
|
9
18
|
export function isTypingTarget(target) {
|
|
10
|
-
|
|
19
|
+
let element = target;
|
|
20
|
+
// Focus inside a shadow root (the UI kit's dialogs) reports its host; look at what is focused in it.
|
|
21
|
+
while (element && element.shadowRoot?.activeElement)
|
|
22
|
+
element = element.shadowRoot.activeElement;
|
|
11
23
|
if (!element || typeof element.closest !== 'function')
|
|
12
24
|
return false;
|
|
13
25
|
return Boolean(element.isContentEditable) || /^(INPUT|TEXTAREA|SELECT)$/i.test(element.nodeName) || Boolean(element.closest('[contenteditable="true"]'));
|
|
14
26
|
}
|
|
15
|
-
export function createCorePluginApi(win, site) {
|
|
27
|
+
export function createCorePluginApi(win, site, extras = {}) {
|
|
16
28
|
const doc = win.document;
|
|
29
|
+
const theme = createThemeApi(win, extras.theme, /^#[0-9a-f]{6}$/i.test(site.toastBackground) ? site.toastBackground : undefined);
|
|
30
|
+
// Created on first use: the kit touches the DOM only when something is shown.
|
|
31
|
+
let kit = null;
|
|
32
|
+
const i18n = createPluginI18n(extras.i18n);
|
|
33
|
+
const uiKit = () => kit ??= createUiKit(win, { theme, product: site.product, hostId: site.toastId, text: key => extras.i18n?.message(key) });
|
|
34
|
+
const ui = Object.freeze({
|
|
35
|
+
toast: (...args) => uiKit().toast(...args),
|
|
36
|
+
alert: (...args) => uiKit().alert(...args),
|
|
37
|
+
confirm: (...args) => uiKit().confirm(...args),
|
|
38
|
+
prompt: (...args) => uiKit().prompt(...args),
|
|
39
|
+
dialog: (...args) => uiKit().dialog(...args),
|
|
40
|
+
modal: (...args) => uiKit().modal(...args),
|
|
41
|
+
h: (...args) => uiKit().h(...args),
|
|
42
|
+
});
|
|
17
43
|
const styleNodes = new Map();
|
|
44
|
+
const adoptedFallbacks = new Map();
|
|
45
|
+
// At document start the engine may run us before the document has an element to hold a <style>
|
|
46
|
+
// (WebView2: `documentElement` is still null). Such styles wait here and are attached, and their
|
|
47
|
+
// CSP check run, as soon as the document has a root.
|
|
48
|
+
const pendingStyles = new Set();
|
|
49
|
+
let pendingObserver = null;
|
|
50
|
+
const attachPending = () => {
|
|
51
|
+
const parent = doc.head || doc.documentElement;
|
|
52
|
+
if (!parent)
|
|
53
|
+
return;
|
|
54
|
+
pendingObserver?.disconnect();
|
|
55
|
+
pendingObserver = null;
|
|
56
|
+
const ids = [...pendingStyles];
|
|
57
|
+
pendingStyles.clear();
|
|
58
|
+
for (const id of ids) {
|
|
59
|
+
const node = styleNodes.get(id);
|
|
60
|
+
if (!node)
|
|
61
|
+
continue;
|
|
62
|
+
parent.appendChild(node);
|
|
63
|
+
api.injectCSS(id, node.textContent ?? '');
|
|
64
|
+
}
|
|
65
|
+
};
|
|
18
66
|
const report = (error) => console.error(`[${site.product}] plugin callback failed`, error);
|
|
67
|
+
const registry = createCommandRegistry({ target: doc, isTyping: event => isTypingTarget(event.target) || isTypingTarget(doc.activeElement), report });
|
|
68
|
+
const commands = Object.freeze({
|
|
69
|
+
register: registry.register,
|
|
70
|
+
list: registry.list,
|
|
71
|
+
run: registry.run,
|
|
72
|
+
onChange: registry.onChange,
|
|
73
|
+
withOwner: registry.withOwner,
|
|
74
|
+
removeOwner: registry.removeOwner,
|
|
75
|
+
});
|
|
19
76
|
const routeListeners = new Set();
|
|
20
77
|
let lastHref = win.location.href;
|
|
21
78
|
let routeTimer = null;
|
|
@@ -26,6 +83,9 @@ export function createCorePluginApi(win, site) {
|
|
|
26
83
|
const previous = lastHref;
|
|
27
84
|
lastHref = href;
|
|
28
85
|
for (const listener of [...routeListeners]) {
|
|
86
|
+
// A listener removed by an earlier one in this round (a feature stopped by the route) is not called.
|
|
87
|
+
if (!routeListeners.has(listener))
|
|
88
|
+
continue;
|
|
29
89
|
try {
|
|
30
90
|
listener(href, previous);
|
|
31
91
|
}
|
|
@@ -37,17 +97,51 @@ export function createCorePluginApi(win, site) {
|
|
|
37
97
|
const api = {
|
|
38
98
|
injectCSS(id, css) {
|
|
39
99
|
let node = styleNodes.get(id);
|
|
100
|
+
if (node && pendingStyles.has(id)) {
|
|
101
|
+
node.textContent = css;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
40
104
|
if (!node?.isConnected) {
|
|
41
105
|
node = doc.createElement('style');
|
|
42
106
|
node.dataset[site.styleDataKey] = id;
|
|
43
|
-
(doc.head || doc.documentElement).appendChild(node);
|
|
44
107
|
styleNodes.set(id, node);
|
|
108
|
+
const parent = doc.head || doc.documentElement;
|
|
109
|
+
if (!parent) {
|
|
110
|
+
node.textContent = css;
|
|
111
|
+
pendingStyles.add(id);
|
|
112
|
+
if (!pendingObserver) {
|
|
113
|
+
pendingObserver = new win.MutationObserver(attachPending);
|
|
114
|
+
pendingObserver.observe(doc, { childList: true, subtree: true });
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
parent.appendChild(node);
|
|
45
119
|
}
|
|
46
120
|
node.textContent = css;
|
|
121
|
+
// A CSP that refuses inline styles leaves the element without a sheet: the same rules as an
|
|
122
|
+
// adopted sheet instead (CSP does not govern constructed sheets).
|
|
123
|
+
const blocked = 'sheet' in node && node.sheet === null && css.trim() !== '';
|
|
124
|
+
const fallback = adoptedFallbacks.get(id);
|
|
125
|
+
if (blocked) {
|
|
126
|
+
if (fallback)
|
|
127
|
+
fallback.update(css);
|
|
128
|
+
else
|
|
129
|
+
adoptedFallbacks.set(id, adoptStyles(doc, css));
|
|
130
|
+
}
|
|
131
|
+
else if (fallback) {
|
|
132
|
+
fallback.remove();
|
|
133
|
+
adoptedFallbacks.delete(id);
|
|
134
|
+
}
|
|
47
135
|
},
|
|
48
136
|
clearCSS(id) {
|
|
137
|
+
pendingStyles.delete(id);
|
|
49
138
|
styleNodes.get(id)?.remove();
|
|
50
139
|
styleNodes.delete(id);
|
|
140
|
+
adoptedFallbacks.get(id)?.remove();
|
|
141
|
+
adoptedFallbacks.delete(id);
|
|
142
|
+
},
|
|
143
|
+
adoptCSS(root, css) {
|
|
144
|
+
return adoptStyles(root, css);
|
|
51
145
|
},
|
|
52
146
|
waitFor(selector, timeoutMs = 15000) {
|
|
53
147
|
return new Promise((resolve, reject) => {
|
|
@@ -123,6 +217,8 @@ export function createCorePluginApi(win, site) {
|
|
|
123
217
|
if (!routeTimer) {
|
|
124
218
|
lastHref = win.location.href;
|
|
125
219
|
routeTimer = setInterval(checkRoute, ROUTE_POLL_MS);
|
|
220
|
+
// Outside a browser (tests) the poll never keeps the process alive.
|
|
221
|
+
routeTimer.unref?.();
|
|
126
222
|
win.addEventListener('popstate', checkRoute);
|
|
127
223
|
}
|
|
128
224
|
return () => {
|
|
@@ -135,6 +231,10 @@ export function createCorePluginApi(win, site) {
|
|
|
135
231
|
};
|
|
136
232
|
},
|
|
137
233
|
observe(selector, callback, root) {
|
|
234
|
+
// The whole page: the shared hub (one MutationObserver and one frame for every plugin and
|
|
235
|
+
// feature). A root of the caller's own (a shadow root, a detached tree) keeps its own observer.
|
|
236
|
+
if (!root || root === doc)
|
|
237
|
+
return observeSelector(win, selector, callback, { report });
|
|
138
238
|
const seen = new WeakSet();
|
|
139
239
|
let frame = 0;
|
|
140
240
|
let disposed = false;
|
|
@@ -142,7 +242,7 @@ export function createCorePluginApi(win, site) {
|
|
|
142
242
|
frame = 0;
|
|
143
243
|
if (disposed)
|
|
144
244
|
return;
|
|
145
|
-
for (const element of Array.from(
|
|
245
|
+
for (const element of Array.from(root.querySelectorAll(selector))) {
|
|
146
246
|
if (seen.has(element))
|
|
147
247
|
continue;
|
|
148
248
|
seen.add(element);
|
|
@@ -158,7 +258,7 @@ export function createCorePluginApi(win, site) {
|
|
|
158
258
|
if (!frame)
|
|
159
259
|
frame = win.requestAnimationFrame(scan);
|
|
160
260
|
});
|
|
161
|
-
observer.observe(root
|
|
261
|
+
observer.observe(root, { childList: true, subtree: true });
|
|
162
262
|
scan();
|
|
163
263
|
return () => {
|
|
164
264
|
disposed = true;
|
|
@@ -214,31 +314,28 @@ export function createCorePluginApi(win, site) {
|
|
|
214
314
|
}
|
|
215
315
|
},
|
|
216
316
|
toast(message, ms = 2200) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
node = doc.createElement('div');
|
|
220
|
-
node.id = site.toastId;
|
|
221
|
-
node.setAttribute('role', 'status');
|
|
222
|
-
Object.assign(node.style, {
|
|
223
|
-
position: 'fixed', left: '50%', bottom: '28px', transform: 'translateX(-50%)', zIndex: '2147483646',
|
|
224
|
-
padding: '10px 16px', borderRadius: '10px', background: site.toastBackground, color: '#fff',
|
|
225
|
-
font: '600 14px/1.3 system-ui, sans-serif', boxShadow: '0 6px 24px rgba(0,0,0,.35)', maxWidth: '80vw',
|
|
226
|
-
});
|
|
227
|
-
doc.body.appendChild(node);
|
|
228
|
-
}
|
|
229
|
-
node.textContent = message;
|
|
230
|
-
if (node._wbTimer)
|
|
231
|
-
clearTimeout(node._wbTimer);
|
|
232
|
-
const current = node;
|
|
233
|
-
node._wbTimer = win.setTimeout(() => current.remove(), ms);
|
|
317
|
+
// The old toast vanished after `ms`; 0 or less meant "at once", so it never means "stay".
|
|
318
|
+
ui.toast(String(message), { duration: Number.isFinite(ms) && ms > 0 ? ms : 1 });
|
|
234
319
|
},
|
|
320
|
+
ui,
|
|
321
|
+
theme,
|
|
322
|
+
commands,
|
|
323
|
+
i18n,
|
|
235
324
|
scope() {
|
|
236
325
|
let disposers = [];
|
|
326
|
+
let scopedUi = null;
|
|
327
|
+
// A scope belongs to the plugin being started when it is created (usually in start()).
|
|
328
|
+
const owner = registry.currentOwner();
|
|
237
329
|
return {
|
|
238
330
|
css(id, css) {
|
|
239
331
|
api.injectCSS(id, css);
|
|
240
332
|
disposers.push(() => api.clearCSS(id));
|
|
241
333
|
},
|
|
334
|
+
adoptCSS(root, css) {
|
|
335
|
+
const styles = api.adoptCSS(root, css);
|
|
336
|
+
disposers.push(() => styles.remove());
|
|
337
|
+
return styles;
|
|
338
|
+
},
|
|
242
339
|
on(target, type, listener, options) {
|
|
243
340
|
target.addEventListener(type, listener, options);
|
|
244
341
|
disposers.push(() => target.removeEventListener(type, listener, options));
|
|
@@ -259,6 +356,24 @@ export function createCorePluginApi(win, site) {
|
|
|
259
356
|
defer(fn) {
|
|
260
357
|
disposers.push(fn);
|
|
261
358
|
},
|
|
359
|
+
onThemeChange(callback) {
|
|
360
|
+
disposers.push(theme.onChange(callback));
|
|
361
|
+
},
|
|
362
|
+
get ui() {
|
|
363
|
+
if (!scopedUi) {
|
|
364
|
+
const scoped = uiKit().scoped();
|
|
365
|
+
scopedUi = scoped.kit;
|
|
366
|
+
disposers.push(() => { scoped.dispose(); scopedUi = null; });
|
|
367
|
+
}
|
|
368
|
+
return scopedUi;
|
|
369
|
+
},
|
|
370
|
+
commands: Object.freeze({
|
|
371
|
+
register(definition) {
|
|
372
|
+
const dispose = registry.registerFor(owner, definition);
|
|
373
|
+
disposers.push(dispose);
|
|
374
|
+
return dispose;
|
|
375
|
+
},
|
|
376
|
+
}),
|
|
262
377
|
dispose() {
|
|
263
378
|
const pending = disposers.reverse();
|
|
264
379
|
disposers = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pluginApi.js","sourceRoot":"","sources":["../../../runtime/src/pluginApi.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,oGAAoG;AACpG,iGAAiG;AACjG,4CAA4C;AA+D5C,MAAM,SAAS,GAAG,8BAA8B,CAAC;AACjD,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC;AAEtB,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,MAA0B;IACvD,MAAM,OAAO,GAAG,MAA4D,CAAC;IAC7E,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IACpE,OAAO,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,4BAA4B,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC;AAC3J,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAA+B,EAAE,IAAmB;IACtF,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC;IACzB,MAAM,UAAU,GAAG,IAAI,GAAG,EAA4B,CAAC;IACvD,MAAM,MAAM,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,0BAA0B,EAAE,KAAK,CAAC,CAAC;IAEpG,MAAM,cAAc,GAAG,IAAI,GAAG,EAA4C,CAAC;IAC3E,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;IACjC,IAAI,UAAU,GAA0C,IAAI,CAAC;IAC7D,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAC/B,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO;QAC9B,MAAM,QAAQ,GAAG,QAAQ,CAAC;QAC1B,QAAQ,GAAG,IAAI,CAAC;QAChB,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,cAAc,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC;gBAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,GAAG,GAAkB;QACzB,SAAS,CAAC,EAAE,EAAE,GAAG;YACf,IAAI,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;gBACvB,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;gBAClC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;gBACrC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBACpD,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACzB,CAAC;QACD,QAAQ,CAAC,EAAE;YACT,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;YAC7B,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,QAAQ,EAAE,SAAS,GAAG,KAAK;YACjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;gBAC1C,IAAI,KAAK;oBAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;gBACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,EAAE;oBAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;oBAC1C,IAAI,KAAK,EAAE,CAAC;wBAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;wBAAC,YAAY,CAAC,KAAK,CAAC,CAAC;wBAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAAC,CAAC;gBAC5E,CAAC,CAAC,CAAC;gBACH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,uBAAuB,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;gBACxI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5E,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,CAAC,SAAS;YACf,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,+BAA+B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACxH,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,GAAG,SAAS,GAAG,CAAC;YACpD,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;gBAAC,OAAO,GAAG,CAAC,YAAY,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,IAAI,CAAC;YAAC,CAAC,CAAC,CAAC,CAAC;YAChF,OAAO;gBACL,GAAG,CAAC,GAAG,EAAE,QAAQ;oBACf,IAAI,CAAC;wBACH,MAAM,GAAG,GAAG,KAAK,EAAE,EAAE,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;wBAC3C,OAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAClD,CAAC;oBAAC,MAAM,CAAC;wBAAC,OAAO,QAAQ,CAAC;oBAAC,CAAC;gBAC9B,CAAC;gBACD,GAAG,CAAC,GAAG,EAAE,KAAK;oBACZ,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC;wBACvB,IAAI,CAAC,MAAM;4BAAE,OAAO,KAAK,CAAC;wBAC1B,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;wBACpD,OAAO,IAAI,CAAC;oBACd,CAAC;oBAAC,MAAM,CAAC;wBAAC,OAAO,KAAK,CAAC;oBAAC,CAAC;gBAC3B,CAAC;gBACD,MAAM,CAAC,GAAG;oBACR,IAAI,CAAC;wBAAC,KAAK,EAAE,EAAE,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;gBAChF,CAAC;gBACD,IAAI;oBACF,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC;oBACvB,IAAI,CAAC,MAAM;wBAAE,OAAO,EAAE,CAAC;oBACvB,MAAM,IAAI,GAAa,EAAE,CAAC;oBAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;wBAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;wBAC1B,IAAI,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC;4BAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;oBACnE,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;aACF,CAAC;QACJ,CAAC;QACD,aAAa,CAAC,QAAQ;YACpB,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC7B,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAC7B,UAAU,GAAG,WAAW,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;gBACpD,GAAG,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAC/C,CAAC;YACD,OAAO,GAAG,EAAE;gBACV,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAChC,IAAI,cAAc,CAAC,IAAI,KAAK,CAAC,IAAI,UAAU,EAAE,CAAC;oBAC5C,aAAa,CAAC,UAAU,CAAC,CAAC;oBAC1B,UAAU,GAAG,IAAI,CAAC;oBAClB,GAAG,CAAC,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBAClD,CAAC;YACH,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI;YAC9B,MAAM,IAAI,GAAG,IAAI,OAAO,EAAW,CAAC;YACpC,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,KAAK,GAAG,CAAC,CAAC;gBACV,IAAI,QAAQ;oBAAE,OAAO;gBACrB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;oBAC3E,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;wBAAE,SAAS;oBAChC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;oBAClB,IAAI,CAAC;wBAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;oBAAC,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBAAC,CAAC;gBAC7D,CAAC;YACH,CAAC,CAAC;YACF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,EAAE;gBAC7C,IAAI,CAAC,KAAK;oBAAE,KAAK,GAAG,GAAG,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;YACtD,CAAC,CAAC,CAAC;YACH,QAAQ,CAAC,OAAO,CAAE,IAAyB,IAAI,GAAG,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACxG,IAAI,EAAE,CAAC;YACP,OAAO,GAAG,EAAE;gBACV,QAAQ,GAAG,IAAI,CAAC;gBAChB,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACtB,IAAI,KAAK;oBAAE,GAAG,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC;YAC7C,CAAC,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,QAAQ;YACZ,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,MAAM,QAAQ,GAAG,CAAC,KAAoB,EAAE,EAAE;gBACxC,IAAI,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM;oBAAE,OAAO;gBACrG,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC;oBAAE,OAAO;gBAC9E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,GAAG,GAAG,OAAO,GAAG,QAAQ,CAAC;gBACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACpD,OAAO,GAAG,QAAQ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACzC,IAAI,OAAO,IAAI,QAAQ;oBAAE,OAAO;gBAChC,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC;gBACrE,IAAI,CAAC;oBAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBAAC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAAC,CAAC;YACjE,CAAC,CAAC;YACF,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC1C,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC5D,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,IAAI;YACjB,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC9C,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;gBAC3C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBAClB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;gBACtF,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,IAAI,EAAE,GAAG,KAAK,CAAC;gBACf,IAAI,CAAC;oBAAC,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC;oBAAC,EAAE,GAAG,KAAK,CAAC;gBAAC,CAAC;gBAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,KAAK,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI;YACtB,IAAI,IAAI,GAAG,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAiD,CAAC;YAC5F,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;gBACvB,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBACpC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE;oBACxB,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,EAAE,YAAY;oBACnG,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,MAAM;oBAC3F,IAAI,EAAE,oCAAoC,EAAE,SAAS,EAAE,4BAA4B,EAAE,QAAQ,EAAE,MAAM;iBACtG,CAAC,CAAC;gBACH,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;YACD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;YAC3B,IAAI,IAAI,CAAC,QAAQ;gBAAE,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/C,MAAM,OAAO,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK;YACH,IAAI,SAAS,GAAc,EAAE,CAAC;YAC9B,OAAO;gBACL,GAAG,CAAC,EAAE,EAAE,GAAG;oBACT,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;oBACvB,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,CAAC;gBACD,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO;oBAChC,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;oBACjD,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC5E,CAAC;gBACD,OAAO,CAAC,QAAQ,EAAE,QAAQ;oBACxB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;gBAClD,CAAC;gBACD,aAAa,CAAC,QAAQ;oBACpB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC9C,CAAC;gBACD,KAAK,CAAC,QAAQ;oBACZ,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACtC,CAAC;gBACD,GAAG,CAAC,IAAI;oBACN,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAE,IAA6B,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;oBAChE,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,KAAK,CAAC,EAAE;oBACN,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACrB,CAAC;gBACD,OAAO;oBACL,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;oBACpC,SAAS,GAAG,EAAE,CAAC;oBACf,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;wBACzB,IAAI,CAAC;4BAAC,EAAE,EAAE,CAAC;wBAAC,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BAAC,MAAM,CAAC,KAAK,CAAC,CAAC;wBAAC,CAAC;oBAChD,CAAC;gBACH,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC;IACF,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["// `window.<Runtime>.api`: small helpers shared by `*.plugin.js` files so each plugin does not\n// re-implement DOM watching, route tracking, local storage and cleanup. Everything here is local to\n// the page: no network requests, no account actions. A site adapter adds its own readers (e.g. a\n// post/message parser) on top of this core.\n\nexport type Dispose = () => void;\n\nexport type PluginStorage = {\n get<T>(key: string, fallback: T): T;\n /** Returns false when the value could not be stored (quota, storage disabled). */\n set(key: string, value: unknown): boolean;\n remove(key: string): void;\n keys(): string[];\n};\n\n/** Collects disposers so a plugin's `stop()` is a single `scope.dispose()`. */\nexport type PluginScope = {\n css(id: string, css: string): void;\n on(target: EventTarget, type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;\n observe(selector: string, callback: (element: Element) => void): void;\n onRouteChange(callback: (href: string, previous: string) => void): void;\n onKey(callback: (event: KeyboardEvent, item: Element | null) => void): void;\n /** Tracks a node the plugin inserted; it is removed on dispose. */\n add<T extends Node>(node: T): T;\n /** Runs `fn` on dispose (restore attributes, clear timers, ...). */\n defer(fn: Dispose): void;\n dispose(): void;\n};\n\nexport type CorePluginApi = {\n injectCSS(id: string, css: string): void;\n clearCSS(id: string): void;\n waitFor(selector: string, timeoutMs?: number): Promise<Element>;\n /** Namespaced JSON storage in this tab's localStorage (`<storagePrefix><namespace>:<key>`). */\n storage(namespace: string): PluginStorage;\n /** Called after the page changes route (pushState/replaceState/back/forward). */\n onRouteChange(callback: (href: string, previous: string) => void): Dispose;\n /** Calls `callback` once for every element matching `selector`, now and as the page renders more. */\n observe(selector: string, callback: (element: Element) => void, root?: ParentNode): Dispose;\n /**\n * Single-key shortcuts: `callback(event, focusedItem)` runs only when the user is not typing, no\n * Ctrl/Meta/Alt is held and the key is not the second half of one of the site's chords.\n */\n onKey(callback: (event: KeyboardEvent, item: Element | null) => void): Dispose;\n copyText(text: string): Promise<boolean>;\n toast(message: string, ms?: number): void;\n scope(): PluginScope;\n};\n\nexport type PluginApiSite = {\n /** The runtime global, for error messages (`<name>.waitFor timed out`). */\n name: string;\n /** Console prefix, e.g. \"wayBetter for X\". */\n product: string;\n /** localStorage key prefix, e.g. `bt-plugin:`. */\n storagePrefix: string;\n /** `data-*` key (camelCase) marking plugin `<style>` elements with their id. */\n styleDataKey: string;\n toastId: string;\n toastBackground: string;\n /** The item a single-key shortcut acts on: the focused element's closest match (a post, a message). */\n itemSelector: string;\n /** Keys that start a two-key chord of the site's own (their second key is never a shortcut). */\n chordKeys: readonly string[];\n};\n\nconst NAMESPACE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;\nconst ROUTE_POLL_MS = 300;\nconst CHORD_MS = 1500;\n\n/** True when a key event belongs to text entry, not to page shortcuts. */\nexport function isTypingTarget(target: EventTarget | null): boolean {\n const element = target as (Element & { isContentEditable?: boolean }) | null;\n if (!element || typeof element.closest !== 'function') return false;\n return Boolean(element.isContentEditable) || /^(INPUT|TEXTAREA|SELECT)$/i.test(element.nodeName) || Boolean(element.closest('[contenteditable=\"true\"]'));\n}\n\nexport function createCorePluginApi(win: Window & typeof globalThis, site: PluginApiSite): CorePluginApi {\n const doc = win.document;\n const styleNodes = new Map<string, HTMLStyleElement>();\n const report = (error: unknown) => console.error(`[${site.product}] plugin callback failed`, error);\n\n const routeListeners = new Set<(href: string, previous: string) => void>();\n let lastHref = win.location.href;\n let routeTimer: ReturnType<typeof setInterval> | null = null;\n const checkRoute = () => {\n const href = win.location.href;\n if (href === lastHref) return;\n const previous = lastHref;\n lastHref = href;\n for (const listener of [...routeListeners]) {\n try { listener(href, previous); } catch (error) { report(error); }\n }\n };\n\n const api: CorePluginApi = {\n injectCSS(id, css) {\n let node = styleNodes.get(id);\n if (!node?.isConnected) {\n node = doc.createElement('style');\n node.dataset[site.styleDataKey] = id;\n (doc.head || doc.documentElement).appendChild(node);\n styleNodes.set(id, node);\n }\n node.textContent = css;\n },\n clearCSS(id) {\n styleNodes.get(id)?.remove();\n styleNodes.delete(id);\n },\n waitFor(selector, timeoutMs = 15000) {\n return new Promise((resolve, reject) => {\n const found = doc.querySelector(selector);\n if (found) return resolve(found);\n const observer = new win.MutationObserver(() => {\n const match = doc.querySelector(selector);\n if (match) { observer.disconnect(); clearTimeout(timer); resolve(match); }\n });\n const timer = setTimeout(() => { observer.disconnect(); reject(new Error(`${site.name}.waitFor timed out: ${selector}`)); }, timeoutMs);\n observer.observe(doc.documentElement, { childList: true, subtree: true });\n });\n },\n storage(namespace) {\n if (!NAMESPACE.test(namespace)) throw new Error(`${site.name}.storage: invalid namespace ${JSON.stringify(namespace)}`);\n const prefix = `${site.storagePrefix}${namespace}:`;\n const store = () => { try { return win.localStorage; } catch { return null; } };\n return {\n get(key, fallback) {\n try {\n const raw = store()?.getItem(prefix + key);\n return raw == null ? fallback : JSON.parse(raw);\n } catch { return fallback; }\n },\n set(key, value) {\n try {\n const target = store();\n if (!target) return false;\n target.setItem(prefix + key, JSON.stringify(value));\n return true;\n } catch { return false; }\n },\n remove(key) {\n try { store()?.removeItem(prefix + key); } catch { /* storage unavailable */ }\n },\n keys() {\n const target = store();\n if (!target) return [];\n const keys: string[] = [];\n for (let i = 0; i < target.length; i += 1) {\n const key = target.key(i);\n if (key?.startsWith(prefix)) keys.push(key.slice(prefix.length));\n }\n return keys;\n },\n };\n },\n onRouteChange(callback) {\n routeListeners.add(callback);\n if (!routeTimer) {\n lastHref = win.location.href;\n routeTimer = setInterval(checkRoute, ROUTE_POLL_MS);\n win.addEventListener('popstate', checkRoute);\n }\n return () => {\n routeListeners.delete(callback);\n if (routeListeners.size === 0 && routeTimer) {\n clearInterval(routeTimer);\n routeTimer = null;\n win.removeEventListener('popstate', checkRoute);\n }\n };\n },\n observe(selector, callback, root) {\n const seen = new WeakSet<Element>();\n let frame = 0;\n let disposed = false;\n const scan = () => {\n frame = 0;\n if (disposed) return;\n for (const element of Array.from((root ?? doc).querySelectorAll(selector))) {\n if (seen.has(element)) continue;\n seen.add(element);\n try { callback(element); } catch (error) { report(error); }\n }\n };\n const observer = new win.MutationObserver(() => {\n if (!frame) frame = win.requestAnimationFrame(scan);\n });\n observer.observe((root as Node | undefined) ?? doc.documentElement, { childList: true, subtree: true });\n scan();\n return () => {\n disposed = true;\n observer.disconnect();\n if (frame) win.cancelAnimationFrame(frame);\n };\n },\n onKey(callback) {\n let chordAt = 0;\n const listener = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.altKey || event.repeat) return;\n if (isTypingTarget(event.target) || isTypingTarget(doc.activeElement)) return;\n const now = Date.now();\n const inChord = now - chordAt < CHORD_MS;\n const chordKey = site.chordKeys.includes(event.key);\n chordAt = chordKey && !inChord ? now : 0;\n if (inChord || chordKey) return;\n const item = doc.activeElement?.closest?.(site.itemSelector) ?? null;\n try { callback(event, item); } catch (error) { report(error); }\n };\n doc.addEventListener('keydown', listener);\n return () => doc.removeEventListener('keydown', listener);\n },\n async copyText(text) {\n try {\n await win.navigator.clipboard.writeText(text);\n return true;\n } catch {\n const area = doc.createElement('textarea');\n area.value = text;\n Object.assign(area.style, { position: 'fixed', opacity: '0', pointerEvents: 'none' });\n doc.body.appendChild(area);\n area.select();\n let ok = false;\n try { ok = doc.execCommand('copy'); } catch { ok = false; }\n area.remove();\n return ok;\n }\n },\n toast(message, ms = 2200) {\n let node = doc.getElementById(site.toastId) as (HTMLElement & { _wbTimer?: number }) | null;\n if (!node) {\n node = doc.createElement('div');\n node.id = site.toastId;\n node.setAttribute('role', 'status');\n Object.assign(node.style, {\n position: 'fixed', left: '50%', bottom: '28px', transform: 'translateX(-50%)', zIndex: '2147483646',\n padding: '10px 16px', borderRadius: '10px', background: site.toastBackground, color: '#fff',\n font: '600 14px/1.3 system-ui, sans-serif', boxShadow: '0 6px 24px rgba(0,0,0,.35)', maxWidth: '80vw',\n });\n doc.body.appendChild(node);\n }\n node.textContent = message;\n if (node._wbTimer) clearTimeout(node._wbTimer);\n const current = node;\n node._wbTimer = win.setTimeout(() => current.remove(), ms);\n },\n scope() {\n let disposers: Dispose[] = [];\n return {\n css(id, css) {\n api.injectCSS(id, css);\n disposers.push(() => api.clearCSS(id));\n },\n on(target, type, listener, options) {\n target.addEventListener(type, listener, options);\n disposers.push(() => target.removeEventListener(type, listener, options));\n },\n observe(selector, callback) {\n disposers.push(api.observe(selector, callback));\n },\n onRouteChange(callback) {\n disposers.push(api.onRouteChange(callback));\n },\n onKey(callback) {\n disposers.push(api.onKey(callback));\n },\n add(node) {\n disposers.push(() => (node as unknown as ChildNode).remove?.());\n return node;\n },\n defer(fn) {\n disposers.push(fn);\n },\n dispose() {\n const pending = disposers.reverse();\n disposers = [];\n for (const fn of pending) {\n try { fn(); } catch (error) { report(error); }\n }\n },\n };\n },\n };\n return api;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pluginApi.js","sourceRoot":"","sources":["../../../runtime/src/pluginApi.ts"],"names":[],"mappings":"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","sourcesContent":["// `window.<Runtime>.api`: small helpers shared by `*.plugin.js` files so each plugin does not\n// re-implement DOM watching, route tracking, local storage and cleanup. Everything here is local to\n// the page: no network requests, no account actions. A site adapter adds its own readers (e.g. a\n// post/message parser) on top of this core.\n//\n// `ui` (toasts, alerts, confirmations, prompts, dialogs; ui.ts) and `theme` (the active theme as\n// values; themeApi.ts) are part of the core: the site adapter only supplies its theme source.\n\nimport { createThemeApi, type ThemeApi, type ThemeSource } from './themeApi.ts';\nimport { createUiKit, type UiKit } from './ui.ts';\nimport { adoptStyles, type RootStyles } from './styles.ts';\nimport { observeSelector } from './domHub.ts';\nimport { createCommandRegistry, type CommandDefinition, type CommandInfo, type CommandRegistry } from './commands.ts';\nimport { createPluginI18n, type I18nSource, type PluginI18n, type PluginMessages } from './pluginI18n.ts';\n\nexport type { ThemeApi, ThemeSource, UiKit, CommandDefinition, CommandInfo, CommandRegistry, RootStyles, I18nSource, PluginI18n, PluginMessages };\n\nexport type Dispose = () => void;\n\nexport type PluginStorage = {\n get<T>(key: string, fallback: T): T;\n /** Returns false when the value could not be stored (quota, storage disabled). */\n set(key: string, value: unknown): boolean;\n remove(key: string): void;\n keys(): string[];\n};\n\n/** Collects disposers so a plugin's `stop()` is a single `scope.dispose()`. */\nexport type PluginScope = {\n css(id: string, css: string): void;\n /** `api.adoptCSS`, taken off the root on dispose. */\n adoptCSS(root: ShadowRoot | Document, css: string): RootStyles;\n on(target: EventTarget, type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;\n observe(selector: string, callback: (element: Element) => void): void;\n onRouteChange(callback: (href: string, previous: string) => void): void;\n onKey(callback: (event: KeyboardEvent, item: Element | null) => void): void;\n /** Tracks a node the plugin inserted; it is removed on dispose. */\n add<T extends Node>(node: T): T;\n /** Runs `fn` on dispose (restore attributes, clear timers, ...). */\n defer(fn: Dispose): void;\n /** Calls `callback(theme)` after the theme changes, until dispose. */\n onThemeChange(callback: Parameters<ThemeApi['onChange']>[0]): void;\n /** The UI kit, scoped: dialogs and toasts it opened are closed on dispose. */\n readonly ui: UiKit;\n /** Command palette entries of the plugin that created this scope, removed on dispose. */\n readonly commands: { register(definition: CommandDefinition): Dispose };\n dispose(): void;\n};\n\nexport type CorePluginApi = {\n injectCSS(id: string, css: string): void;\n clearCSS(id: string): void;\n /**\n * Styles a shadow root the plugin created (or the document) with a constructable stylesheet:\n * the page's Content-Security-Policy cannot refuse it, it is parsed once, and it stays with the\n * root wherever its host is moved. Falls back to a `<style>` element where unsupported.\n */\n adoptCSS(root: ShadowRoot | Document, css: string): RootStyles;\n waitFor(selector: string, timeoutMs?: number): Promise<Element>;\n /** Namespaced JSON storage in this tab's localStorage (`<storagePrefix><namespace>:<key>`). */\n storage(namespace: string): PluginStorage;\n /** Called after the page changes route (pushState/replaceState/back/forward). */\n onRouteChange(callback: (href: string, previous: string) => void): Dispose;\n /**\n * Calls `callback` once for every element matching `selector`, now and as the page renders more.\n * Page-wide observers share one MutationObserver (domHub.ts); pass `root` only for a tree of your own.\n */\n observe(selector: string, callback: (element: Element) => void, root?: ParentNode): Dispose;\n /**\n * Single-key shortcuts: `callback(event, focusedItem)` runs only when the user is not typing, no\n * Ctrl/Meta/Alt is held and the key is not the second half of one of the site's chords.\n */\n onKey(callback: (event: KeyboardEvent, item: Element | null) => void): Dispose;\n copyText(text: string): Promise<boolean>;\n /** A short status message (the UI kit's toast; `ms` is how long it stays). */\n toast(message: string, ms?: number): void;\n /** Toasts, alerts, confirmations, prompts and dialogs in the active theme (see ui.ts). */\n ui: UiKit;\n /** The active theme as values: `current()` and `onChange(fn)` (see themeApi.ts). */\n theme: ThemeApi;\n /**\n * Commands in the shell's command palette (Ctrl+K), listed under the plugin's name and run in\n * this page (see commands.ts). Removed when the plugin stops.\n */\n commands: CommandRegistry;\n /**\n * The app's language and the plugin's own strings in it (see pluginI18n.ts). In a plugin's view\n * it is bound to that plugin (`plugin.<file name>.*` in the app catalog); `namespace(name)` binds\n * it explicitly.\n */\n i18n: PluginI18n;\n scope(): PluginScope;\n};\n\n/** What the site adapter adds to the core: its theme engine, when it has one. */\nexport type CorePluginExtras = {\n theme?: ThemeSource;\n /** The app's active locale and catalog (plugin strings, the UI kit's own words). English without it. */\n i18n?: I18nSource;\n};\n\nexport type PluginApiSite = {\n /** The runtime global, for error messages (`<name>.waitFor timed out`). */\n name: string;\n /** Console prefix, e.g. \"wayBetter for X\". */\n product: string;\n /** localStorage key prefix, e.g. `bt-plugin:`. */\n storagePrefix: string;\n /** `data-*` key (camelCase) marking plugin `<style>` elements with their id. */\n styleDataKey: string;\n /** Id of the kit's shadow host element (`<wb-ui id=…>`), kept from the old single toast. */\n toastId: string;\n /** Accent used when the site has no theme source (and the page shows none). */\n toastBackground: string;\n /** The item a single-key shortcut acts on: the focused element's closest match (a post, a message). */\n itemSelector: string;\n /** Keys that start a two-key chord of the site's own (their second key is never a shortcut). */\n chordKeys: readonly string[];\n};\n\nconst NAMESPACE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;\nconst ROUTE_POLL_MS = 300;\nconst CHORD_MS = 1500;\n\n/** True when a key event belongs to text entry, not to page shortcuts. */\nexport function isTypingTarget(target: EventTarget | null): boolean {\n let element = target as (Element & { isContentEditable?: boolean }) | null;\n // Focus inside a shadow root (the UI kit's dialogs) reports its host; look at what is focused in it.\n while (element && (element as Element).shadowRoot?.activeElement) element = (element as Element).shadowRoot!.activeElement as Element;\n if (!element || typeof element.closest !== 'function') return false;\n return Boolean(element.isContentEditable) || /^(INPUT|TEXTAREA|SELECT)$/i.test(element.nodeName) || Boolean(element.closest('[contenteditable=\"true\"]'));\n}\n\nexport function createCorePluginApi(win: Window & typeof globalThis, site: PluginApiSite, extras: CorePluginExtras = {}): CorePluginApi {\n const doc = win.document;\n const theme = createThemeApi(win, extras.theme, /^#[0-9a-f]{6}$/i.test(site.toastBackground) ? site.toastBackground : undefined);\n // Created on first use: the kit touches the DOM only when something is shown.\n let kit: ReturnType<typeof createUiKit> | null = null;\n const i18n = createPluginI18n(extras.i18n);\n const uiKit = () => kit ??= createUiKit(win, { theme, product: site.product, hostId: site.toastId, text: key => extras.i18n?.message(key) });\n const ui: UiKit = Object.freeze({\n toast: (...args: Parameters<UiKit['toast']>) => uiKit().toast(...args),\n alert: (...args: Parameters<UiKit['alert']>) => uiKit().alert(...args),\n confirm: (...args: Parameters<UiKit['confirm']>) => uiKit().confirm(...args),\n prompt: (...args: Parameters<UiKit['prompt']>) => uiKit().prompt(...args),\n dialog: (...args: Parameters<UiKit['dialog']>) => uiKit().dialog(...args),\n modal: (...args: Parameters<UiKit['modal']>) => uiKit().modal(...args),\n h: (...args: Parameters<UiKit['h']>) => uiKit().h(...args),\n });\n const styleNodes = new Map<string, HTMLStyleElement>();\n const adoptedFallbacks = new Map<string, RootStyles>();\n // At document start the engine may run us before the document has an element to hold a <style>\n // (WebView2: `documentElement` is still null). Such styles wait here and are attached, and their\n // CSP check run, as soon as the document has a root.\n const pendingStyles = new Set<string>();\n let pendingObserver: MutationObserver | null = null;\n const attachPending = () => {\n const parent = doc.head || doc.documentElement;\n if (!parent) return;\n pendingObserver?.disconnect();\n pendingObserver = null;\n const ids = [...pendingStyles];\n pendingStyles.clear();\n for (const id of ids) {\n const node = styleNodes.get(id);\n if (!node) continue;\n parent.appendChild(node);\n api.injectCSS(id, node.textContent ?? '');\n }\n };\n const report = (error: unknown) => console.error(`[${site.product}] plugin callback failed`, error);\n const registry = createCommandRegistry({ target: doc, isTyping: event => isTypingTarget(event.target) || isTypingTarget(doc.activeElement), report });\n const commands: CommandRegistry = Object.freeze({\n register: registry.register,\n list: registry.list,\n run: registry.run,\n onChange: registry.onChange,\n withOwner: registry.withOwner,\n removeOwner: registry.removeOwner,\n });\n\n const routeListeners = new Set<(href: string, previous: string) => void>();\n let lastHref = win.location.href;\n let routeTimer: ReturnType<typeof setInterval> | null = null;\n const checkRoute = () => {\n const href = win.location.href;\n if (href === lastHref) return;\n const previous = lastHref;\n lastHref = href;\n for (const listener of [...routeListeners]) {\n // A listener removed by an earlier one in this round (a feature stopped by the route) is not called.\n if (!routeListeners.has(listener)) continue;\n try { listener(href, previous); } catch (error) { report(error); }\n }\n };\n\n const api: CorePluginApi = {\n injectCSS(id, css) {\n let node = styleNodes.get(id);\n if (node && pendingStyles.has(id)) {\n node.textContent = css;\n return;\n }\n if (!node?.isConnected) {\n node = doc.createElement('style');\n node.dataset[site.styleDataKey] = id;\n styleNodes.set(id, node);\n const parent = doc.head || doc.documentElement;\n if (!parent) {\n node.textContent = css;\n pendingStyles.add(id);\n if (!pendingObserver) {\n pendingObserver = new win.MutationObserver(attachPending);\n pendingObserver.observe(doc, { childList: true, subtree: true });\n }\n return;\n }\n parent.appendChild(node);\n }\n node.textContent = css;\n // A CSP that refuses inline styles leaves the element without a sheet: the same rules as an\n // adopted sheet instead (CSP does not govern constructed sheets).\n const blocked = 'sheet' in node && node.sheet === null && css.trim() !== '';\n const fallback = adoptedFallbacks.get(id);\n if (blocked) {\n if (fallback) fallback.update(css);\n else adoptedFallbacks.set(id, adoptStyles(doc, css));\n } else if (fallback) {\n fallback.remove();\n adoptedFallbacks.delete(id);\n }\n },\n clearCSS(id) {\n pendingStyles.delete(id);\n styleNodes.get(id)?.remove();\n styleNodes.delete(id);\n adoptedFallbacks.get(id)?.remove();\n adoptedFallbacks.delete(id);\n },\n adoptCSS(root, css) {\n return adoptStyles(root, css);\n },\n waitFor(selector, timeoutMs = 15000) {\n return new Promise((resolve, reject) => {\n const found = doc.querySelector(selector);\n if (found) return resolve(found);\n const observer = new win.MutationObserver(() => {\n const match = doc.querySelector(selector);\n if (match) { observer.disconnect(); clearTimeout(timer); resolve(match); }\n });\n const timer = setTimeout(() => { observer.disconnect(); reject(new Error(`${site.name}.waitFor timed out: ${selector}`)); }, timeoutMs);\n observer.observe(doc.documentElement, { childList: true, subtree: true });\n });\n },\n storage(namespace) {\n if (!NAMESPACE.test(namespace)) throw new Error(`${site.name}.storage: invalid namespace ${JSON.stringify(namespace)}`);\n const prefix = `${site.storagePrefix}${namespace}:`;\n const store = () => { try { return win.localStorage; } catch { return null; } };\n return {\n get(key, fallback) {\n try {\n const raw = store()?.getItem(prefix + key);\n return raw == null ? fallback : JSON.parse(raw);\n } catch { return fallback; }\n },\n set(key, value) {\n try {\n const target = store();\n if (!target) return false;\n target.setItem(prefix + key, JSON.stringify(value));\n return true;\n } catch { return false; }\n },\n remove(key) {\n try { store()?.removeItem(prefix + key); } catch { /* storage unavailable */ }\n },\n keys() {\n const target = store();\n if (!target) return [];\n const keys: string[] = [];\n for (let i = 0; i < target.length; i += 1) {\n const key = target.key(i);\n if (key?.startsWith(prefix)) keys.push(key.slice(prefix.length));\n }\n return keys;\n },\n };\n },\n onRouteChange(callback) {\n routeListeners.add(callback);\n if (!routeTimer) {\n lastHref = win.location.href;\n routeTimer = setInterval(checkRoute, ROUTE_POLL_MS);\n // Outside a browser (tests) the poll never keeps the process alive.\n (routeTimer as { unref?: () => void }).unref?.();\n win.addEventListener('popstate', checkRoute);\n }\n return () => {\n routeListeners.delete(callback);\n if (routeListeners.size === 0 && routeTimer) {\n clearInterval(routeTimer);\n routeTimer = null;\n win.removeEventListener('popstate', checkRoute);\n }\n };\n },\n observe(selector, callback, root) {\n // The whole page: the shared hub (one MutationObserver and one frame for every plugin and\n // feature). A root of the caller's own (a shadow root, a detached tree) keeps its own observer.\n if (!root || root === doc) return observeSelector(win, selector, callback, { report });\n const seen = new WeakSet<Element>();\n let frame = 0;\n let disposed = false;\n const scan = () => {\n frame = 0;\n if (disposed) return;\n for (const element of Array.from(root.querySelectorAll(selector))) {\n if (seen.has(element)) continue;\n seen.add(element);\n try { callback(element); } catch (error) { report(error); }\n }\n };\n const observer = new win.MutationObserver(() => {\n if (!frame) frame = win.requestAnimationFrame(scan);\n });\n observer.observe(root as Node, { childList: true, subtree: true });\n scan();\n return () => {\n disposed = true;\n observer.disconnect();\n if (frame) win.cancelAnimationFrame(frame);\n };\n },\n onKey(callback) {\n let chordAt = 0;\n const listener = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.ctrlKey || event.metaKey || event.altKey || event.repeat) return;\n if (isTypingTarget(event.target) || isTypingTarget(doc.activeElement)) return;\n const now = Date.now();\n const inChord = now - chordAt < CHORD_MS;\n const chordKey = site.chordKeys.includes(event.key);\n chordAt = chordKey && !inChord ? now : 0;\n if (inChord || chordKey) return;\n const item = doc.activeElement?.closest?.(site.itemSelector) ?? null;\n try { callback(event, item); } catch (error) { report(error); }\n };\n doc.addEventListener('keydown', listener);\n return () => doc.removeEventListener('keydown', listener);\n },\n async copyText(text) {\n try {\n await win.navigator.clipboard.writeText(text);\n return true;\n } catch {\n const area = doc.createElement('textarea');\n area.value = text;\n Object.assign(area.style, { position: 'fixed', opacity: '0', pointerEvents: 'none' });\n doc.body.appendChild(area);\n area.select();\n let ok = false;\n try { ok = doc.execCommand('copy'); } catch { ok = false; }\n area.remove();\n return ok;\n }\n },\n toast(message, ms = 2200) {\n // The old toast vanished after `ms`; 0 or less meant \"at once\", so it never means \"stay\".\n ui.toast(String(message), { duration: Number.isFinite(ms) && ms > 0 ? ms : 1 });\n },\n ui,\n theme,\n commands,\n i18n,\n scope() {\n let disposers: Dispose[] = [];\n let scopedUi: UiKit | null = null;\n // A scope belongs to the plugin being started when it is created (usually in start()).\n const owner = registry.currentOwner();\n return {\n css(id, css) {\n api.injectCSS(id, css);\n disposers.push(() => api.clearCSS(id));\n },\n adoptCSS(root, css) {\n const styles = api.adoptCSS(root, css);\n disposers.push(() => styles.remove());\n return styles;\n },\n on(target, type, listener, options) {\n target.addEventListener(type, listener, options);\n disposers.push(() => target.removeEventListener(type, listener, options));\n },\n observe(selector, callback) {\n disposers.push(api.observe(selector, callback));\n },\n onRouteChange(callback) {\n disposers.push(api.onRouteChange(callback));\n },\n onKey(callback) {\n disposers.push(api.onKey(callback));\n },\n add(node) {\n disposers.push(() => (node as unknown as ChildNode).remove?.());\n return node;\n },\n defer(fn) {\n disposers.push(fn);\n },\n onThemeChange(callback) {\n disposers.push(theme.onChange(callback));\n },\n get ui() {\n if (!scopedUi) {\n const scoped = uiKit().scoped();\n scopedUi = scoped.kit;\n disposers.push(() => { scoped.dispose(); scopedUi = null; });\n }\n return scopedUi;\n },\n commands: Object.freeze({\n register(definition: CommandDefinition) {\n const dispose = registry.registerFor(owner, definition);\n disposers.push(dispose);\n return dispose;\n },\n }),\n dispose() {\n const pending = disposers.reverse();\n disposers = [];\n for (const fn of pending) {\n try { fn(); } catch (error) { report(error); }\n }\n },\n };\n },\n };\n return api;\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Dir, type Params } from './i18n.ts';
|
|
2
|
+
/** What the site adapter supplies: the app's active locale and its resolved catalog. */
|
|
3
|
+
export type I18nSource = {
|
|
4
|
+
locale(): string;
|
|
5
|
+
/** The app catalog's message for `key` in the active locale (its fallback chain applied), if any. */
|
|
6
|
+
message(key: string): string | undefined;
|
|
7
|
+
onChange(listener: () => void): () => void;
|
|
8
|
+
};
|
|
9
|
+
export type PluginMessages = Record<string, Record<string, string>>;
|
|
10
|
+
export type PluginI18n = {
|
|
11
|
+
readonly locale: string;
|
|
12
|
+
readonly dir: Dir;
|
|
13
|
+
/** The plugin's message `key` in the app's language, formatted with `params`; the key when unknown. */
|
|
14
|
+
t(key: string, params?: Params): string;
|
|
15
|
+
has(key: string): boolean;
|
|
16
|
+
/** Adds the plugin's own messages, keyed by locale (`{ en: {...}, tr: {...} }`); later calls merge. */
|
|
17
|
+
register(messages: PluginMessages): void;
|
|
18
|
+
/** Calls `listener(locale)` after the app's language changes. Returns a dispose. */
|
|
19
|
+
onChange(listener: (locale: string) => void): () => void;
|
|
20
|
+
number(value: number, options?: Intl.NumberFormatOptions): string;
|
|
21
|
+
date(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
|
|
22
|
+
relative(value: number, unit: Intl.RelativeTimeFormatUnit): string;
|
|
23
|
+
list(items: readonly string[]): string;
|
|
24
|
+
/** The same API for another namespace (a plugin with several files, or code outside `start()`). */
|
|
25
|
+
namespace(name: string): PluginI18n;
|
|
26
|
+
};
|
|
27
|
+
/** `emoji-picker.plugin.js` -> `emoji-picker`. */
|
|
28
|
+
export declare function pluginNamespace(id: string): string;
|
|
29
|
+
export declare function createPluginI18n(source?: I18nSource, namespace?: string): PluginI18n;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// `api.i18n` for plugins: the app's language, and each plugin's own strings in it.
|
|
2
|
+
//
|
|
3
|
+
// const i18n = window.wayBetter.api.i18n; // bound to the running plugin
|
|
4
|
+
// i18n.register({ en: { saved: 'Saved {count, plural, one {# note} other {# notes}}' },
|
|
5
|
+
// tr: { saved: '{count, plural, one {# not} other {# not}} kaydedildi' } });
|
|
6
|
+
// i18n.t('saved', { count: 2 }); // in the app's language, English otherwise
|
|
7
|
+
// i18n.locale; i18n.dir; i18n.onChange(() => rerender());
|
|
8
|
+
// i18n.number(1234.5); i18n.date(Date.now(), { dateStyle: 'medium' }); i18n.relative(-3, 'minute');
|
|
9
|
+
//
|
|
10
|
+
// A plugin's key `k` is looked up in the plugin's own messages for the app's locale chain, then in
|
|
11
|
+
// the app's catalog as `plugin.<namespace>.k` (bundled plugins are translated there, by the same
|
|
12
|
+
// contributors as the app), then in the plugin's English. The namespace is the plugin file name
|
|
13
|
+
// without `.plugin.js`. Messages are text: placeholders are inserted as text, never parsed as HTML.
|
|
14
|
+
import { direction, fallbackChain, formatMessage, normalizeLocale, numberFormat, dateFormat } from "./i18n.js";
|
|
15
|
+
const NAMESPACE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
16
|
+
const MAX_KEYS = 2000;
|
|
17
|
+
const MAX_VALUE = 2000;
|
|
18
|
+
/** `emoji-picker.plugin.js` -> `emoji-picker`. */
|
|
19
|
+
export function pluginNamespace(id) {
|
|
20
|
+
return String(id).replace(/\.plugin\.js$/i, '').replace(/[^a-z0-9._-]/gi, '-').slice(0, 64) || 'plugin';
|
|
21
|
+
}
|
|
22
|
+
const ENGLISH = { locale: () => 'en', message: () => undefined, onChange: () => () => { } };
|
|
23
|
+
export function createPluginI18n(source = ENGLISH, namespace = '') {
|
|
24
|
+
// Shared per namespace so a plugin's `register` in start() is seen by every view of it.
|
|
25
|
+
const registry = new Map();
|
|
26
|
+
const make = (ns) => {
|
|
27
|
+
const own = () => {
|
|
28
|
+
let table = registry.get(ns);
|
|
29
|
+
if (!table) {
|
|
30
|
+
table = new Map();
|
|
31
|
+
registry.set(ns, table);
|
|
32
|
+
}
|
|
33
|
+
return table;
|
|
34
|
+
};
|
|
35
|
+
const locale = () => normalizeLocale(source.locale()) ?? 'en';
|
|
36
|
+
const lookup = (key) => {
|
|
37
|
+
const chain = fallbackChain(locale(), 'en');
|
|
38
|
+
const table = own();
|
|
39
|
+
// Own translations first (not English yet: the app catalog may translate a bundled plugin).
|
|
40
|
+
for (const tag of chain) {
|
|
41
|
+
if (tag === 'en')
|
|
42
|
+
break;
|
|
43
|
+
const value = table.get(tag)?.[key];
|
|
44
|
+
if (typeof value === 'string')
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
if (ns) {
|
|
48
|
+
const hosted = source.message(`plugin.${ns}.${key}`);
|
|
49
|
+
if (typeof hosted === 'string')
|
|
50
|
+
return hosted;
|
|
51
|
+
}
|
|
52
|
+
return table.get('en')?.[key];
|
|
53
|
+
};
|
|
54
|
+
return Object.freeze({
|
|
55
|
+
get locale() { return locale(); },
|
|
56
|
+
get dir() { return direction(locale()); },
|
|
57
|
+
t(key, params) {
|
|
58
|
+
const message = lookup(String(key));
|
|
59
|
+
return message === undefined ? String(key) : formatMessage(message, params ?? {}, locale());
|
|
60
|
+
},
|
|
61
|
+
has: (key) => lookup(String(key)) !== undefined,
|
|
62
|
+
register(messages) {
|
|
63
|
+
if (!messages || typeof messages !== 'object')
|
|
64
|
+
return;
|
|
65
|
+
const table = own();
|
|
66
|
+
for (const [tag, entries] of Object.entries(messages)) {
|
|
67
|
+
const normalized = normalizeLocale(tag);
|
|
68
|
+
if (!normalized || !entries || typeof entries !== 'object')
|
|
69
|
+
continue;
|
|
70
|
+
const target = { ...table.get(normalized) };
|
|
71
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
72
|
+
if (typeof value !== 'string' || value.length > MAX_VALUE || Object.keys(target).length >= MAX_KEYS)
|
|
73
|
+
continue;
|
|
74
|
+
target[key] = value;
|
|
75
|
+
}
|
|
76
|
+
table.set(normalized, target);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
onChange(listener) {
|
|
80
|
+
return source.onChange(() => {
|
|
81
|
+
try {
|
|
82
|
+
listener(locale());
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.error('[wayBetter] i18n listener failed', error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
number: (value, options) => numberFormat(locale(), options).format(value),
|
|
90
|
+
date: (value, options) => {
|
|
91
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
92
|
+
return Number.isFinite(date.getTime()) ? dateFormat(locale(), options).format(date) : '';
|
|
93
|
+
},
|
|
94
|
+
relative(value, unit) {
|
|
95
|
+
try {
|
|
96
|
+
return new Intl.RelativeTimeFormat(locale().split('-x-')[0], { numeric: 'auto' }).format(value, unit);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return `${value} ${unit}`;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
list(items) {
|
|
103
|
+
try {
|
|
104
|
+
return new Intl.ListFormat(locale().split('-x-')[0], { type: 'conjunction' }).format(items);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return items.join(', ');
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
namespace(name) {
|
|
111
|
+
if (!NAMESPACE.test(String(name)))
|
|
112
|
+
throw new Error(`i18n.namespace: invalid name ${JSON.stringify(name)}`);
|
|
113
|
+
return make(String(name));
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
return make(namespace);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=pluginI18n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pluginI18n.js","sourceRoot":"","sources":["../../../runtime/src/pluginI18n.ts"],"names":[],"mappings":"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","sourcesContent":["// `api.i18n` for plugins: the app's language, and each plugin's own strings in it.\n//\n// const i18n = window.wayBetter.api.i18n; // bound to the running plugin\n// i18n.register({ en: { saved: 'Saved {count, plural, one {# note} other {# notes}}' },\n// tr: { saved: '{count, plural, one {# not} other {# not}} kaydedildi' } });\n// i18n.t('saved', { count: 2 }); // in the app's language, English otherwise\n// i18n.locale; i18n.dir; i18n.onChange(() => rerender());\n// i18n.number(1234.5); i18n.date(Date.now(), { dateStyle: 'medium' }); i18n.relative(-3, 'minute');\n//\n// A plugin's key `k` is looked up in the plugin's own messages for the app's locale chain, then in\n// the app's catalog as `plugin.<namespace>.k` (bundled plugins are translated there, by the same\n// contributors as the app), then in the plugin's English. The namespace is the plugin file name\n// without `.plugin.js`. Messages are text: placeholders are inserted as text, never parsed as HTML.\n\nimport { direction, fallbackChain, formatMessage, normalizeLocale, numberFormat, dateFormat, type Dir, type Params } from './i18n.ts';\n\n/** What the site adapter supplies: the app's active locale and its resolved catalog. */\nexport type I18nSource = {\n locale(): string;\n /** The app catalog's message for `key` in the active locale (its fallback chain applied), if any. */\n message(key: string): string | undefined;\n onChange(listener: () => void): () => void;\n};\n\nexport type PluginMessages = Record<string, Record<string, string>>;\n\nexport type PluginI18n = {\n readonly locale: string;\n readonly dir: Dir;\n /** The plugin's message `key` in the app's language, formatted with `params`; the key when unknown. */\n t(key: string, params?: Params): string;\n has(key: string): boolean;\n /** Adds the plugin's own messages, keyed by locale (`{ en: {...}, tr: {...} }`); later calls merge. */\n register(messages: PluginMessages): void;\n /** Calls `listener(locale)` after the app's language changes. Returns a dispose. */\n onChange(listener: (locale: string) => void): () => void;\n number(value: number, options?: Intl.NumberFormatOptions): string;\n date(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;\n relative(value: number, unit: Intl.RelativeTimeFormatUnit): string;\n list(items: readonly string[]): string;\n /** The same API for another namespace (a plugin with several files, or code outside `start()`). */\n namespace(name: string): PluginI18n;\n};\n\nconst NAMESPACE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;\nconst MAX_KEYS = 2000;\nconst MAX_VALUE = 2000;\n\n/** `emoji-picker.plugin.js` -> `emoji-picker`. */\nexport function pluginNamespace(id: string): string {\n return String(id).replace(/\\.plugin\\.js$/i, '').replace(/[^a-z0-9._-]/gi, '-').slice(0, 64) || 'plugin';\n}\n\nconst ENGLISH: I18nSource = { locale: () => 'en', message: () => undefined, onChange: () => () => {} };\n\nexport function createPluginI18n(source: I18nSource = ENGLISH, namespace = ''): PluginI18n {\n // Shared per namespace so a plugin's `register` in start() is seen by every view of it.\n const registry = new Map<string, Map<string, Record<string, string>>>();\n\n const make = (ns: string): PluginI18n => {\n const own = () => {\n let table = registry.get(ns);\n if (!table) { table = new Map(); registry.set(ns, table); }\n return table;\n };\n const locale = () => normalizeLocale(source.locale()) ?? 'en';\n const lookup = (key: string): string | undefined => {\n const chain = fallbackChain(locale(), 'en');\n const table = own();\n // Own translations first (not English yet: the app catalog may translate a bundled plugin).\n for (const tag of chain) {\n if (tag === 'en') break;\n const value = table.get(tag)?.[key];\n if (typeof value === 'string') return value;\n }\n if (ns) {\n const hosted = source.message(`plugin.${ns}.${key}`);\n if (typeof hosted === 'string') return hosted;\n }\n return table.get('en')?.[key];\n };\n return Object.freeze({\n get locale() { return locale(); },\n get dir() { return direction(locale()); },\n t(key: string, params?: Params) {\n const message = lookup(String(key));\n return message === undefined ? String(key) : formatMessage(message, params ?? {}, locale());\n },\n has: (key: string) => lookup(String(key)) !== undefined,\n register(messages: PluginMessages) {\n if (!messages || typeof messages !== 'object') return;\n const table = own();\n for (const [tag, entries] of Object.entries(messages)) {\n const normalized = normalizeLocale(tag);\n if (!normalized || !entries || typeof entries !== 'object') continue;\n const target = { ...table.get(normalized) };\n for (const [key, value] of Object.entries(entries)) {\n if (typeof value !== 'string' || value.length > MAX_VALUE || Object.keys(target).length >= MAX_KEYS) continue;\n target[key] = value;\n }\n table.set(normalized, target);\n }\n },\n onChange(listener: (locale: string) => void) {\n return source.onChange(() => {\n try { listener(locale()); } catch (error) { console.error('[wayBetter] i18n listener failed', error); }\n });\n },\n number: (value: number, options?: Intl.NumberFormatOptions) => numberFormat(locale(), options).format(value),\n date: (value: Date | number | string, options?: Intl.DateTimeFormatOptions) => {\n const date = value instanceof Date ? value : new Date(value);\n return Number.isFinite(date.getTime()) ? dateFormat(locale(), options).format(date) : '';\n },\n relative(value: number, unit: Intl.RelativeTimeFormatUnit) {\n try { return new Intl.RelativeTimeFormat(locale().split('-x-')[0], { numeric: 'auto' }).format(value, unit); } catch { return `${value} ${unit}`; }\n },\n list(items: readonly string[]) {\n try { return new Intl.ListFormat(locale().split('-x-')[0], { type: 'conjunction' }).format(items); } catch { return items.join(', '); }\n },\n namespace(name: string) {\n if (!NAMESPACE.test(String(name))) throw new Error(`i18n.namespace: invalid name ${JSON.stringify(name)}`);\n return make(String(name));\n },\n });\n };\n return make(namespace);\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { UiKit } from './ui.ts';
|
|
2
|
+
type FieldBase = {
|
|
3
|
+
/** The key in the stored values. */
|
|
4
|
+
key: string;
|
|
5
|
+
/** Catalog key of the label. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Catalog key of a line under the label. */
|
|
8
|
+
hint?: string;
|
|
9
|
+
};
|
|
10
|
+
export type SettingField = (FieldBase & {
|
|
11
|
+
type: 'toggle';
|
|
12
|
+
default: boolean;
|
|
13
|
+
}) | (FieldBase & {
|
|
14
|
+
type: 'number';
|
|
15
|
+
default: number;
|
|
16
|
+
min?: number;
|
|
17
|
+
max?: number;
|
|
18
|
+
step?: number;
|
|
19
|
+
}) | (FieldBase & {
|
|
20
|
+
type: 'text';
|
|
21
|
+
default: string;
|
|
22
|
+
maxLength?: number;
|
|
23
|
+
multiline?: boolean;
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
}) | (FieldBase & {
|
|
26
|
+
type: 'select';
|
|
27
|
+
default: string;
|
|
28
|
+
options: ReadonlyArray<{
|
|
29
|
+
value: string;
|
|
30
|
+
label: string;
|
|
31
|
+
}>;
|
|
32
|
+
})
|
|
33
|
+
/** Lines of text (patterns, handles): one per line in the form. */
|
|
34
|
+
| (FieldBase & {
|
|
35
|
+
type: 'list';
|
|
36
|
+
default: readonly string[];
|
|
37
|
+
maxItems?: number;
|
|
38
|
+
maxLength?: number;
|
|
39
|
+
}) | {
|
|
40
|
+
type: 'divider';
|
|
41
|
+
};
|
|
42
|
+
export type SettingsSchema = readonly SettingField[];
|
|
43
|
+
export type SettingValue = boolean | number | string | string[];
|
|
44
|
+
export type SettingValues = Record<string, SettingValue>;
|
|
45
|
+
export declare const SETTINGS_LIMITS: Readonly<{
|
|
46
|
+
fields: 64;
|
|
47
|
+
text: 2000;
|
|
48
|
+
listItems: 500;
|
|
49
|
+
listItem: 500;
|
|
50
|
+
}>;
|
|
51
|
+
/** The values for `schema` from whatever was stored: every field present, every value of its type and in range. */
|
|
52
|
+
export declare function normalizeSettings(schema: SettingsSchema, stored: unknown): SettingValues;
|
|
53
|
+
/** Problems in a schema (an empty list: valid). */
|
|
54
|
+
export declare function validateSchema(schema: SettingsSchema): string[];
|
|
55
|
+
export type SettingsForm = {
|
|
56
|
+
/** The controls, in schema order: append them to a dialog's body. */
|
|
57
|
+
nodes: HTMLElement[];
|
|
58
|
+
/** The values as the controls show them now (normalized). */
|
|
59
|
+
read(): SettingValues;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The controls for `schema` showing `values`, built with the kit's element builder and styled by the
|
|
63
|
+
* kit's form classes. `text(key)` translates a label, hint or option key.
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildSettingsForm(h: UiKit['h'], schema: SettingsSchema, values: SettingValues, text: (key: string) => string): SettingsForm;
|
|
66
|
+
export {};
|