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.
- package/README.md +139 -6
- package/bin/waybetter.mjs +43 -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/release.mjs +437 -0
- package/cli/releasecheck.mjs +15 -0
- package/cli/remote.mjs +260 -0
- package/cli/store.mjs +378 -0
- package/cli/strings.mjs +123 -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 +61 -0
- package/dist/runtime/stringPackTranslate.js +267 -0
- package/dist/runtime/stringPackTranslate.js.map +1 -0
- package/dist/runtime/stringPacks.d.ts +248 -0
- package/dist/runtime/stringPacks.js +610 -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/themeBar.d.ts +28 -0
- package/dist/runtime/themeBar.js +82 -0
- package/dist/runtime/themeBar.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 +476 -0
- package/dist/runtime/themeComponents.js.map +1 -0
- package/dist/runtime/themeDecor.d.ts +73 -0
- package/dist/runtime/themeDecor.js +133 -0
- package/dist/runtime/themeDecor.js.map +1 -0
- package/dist/runtime/themeDerive.d.ts +69 -0
- package/dist/runtime/themeDerive.js +117 -0
- package/dist/runtime/themeDerive.js.map +1 -0
- package/dist/runtime/themeDoctor.d.ts +52 -0
- package/dist/runtime/themeDoctor.js +112 -0
- package/dist/runtime/themeDoctor.js.map +1 -0
- package/dist/runtime/themeGuards.d.ts +60 -0
- package/dist/runtime/themeGuards.js +235 -0
- package/dist/runtime/themeGuards.js.map +1 -0
- package/dist/runtime/themeIcons.d.ts +130 -0
- package/dist/runtime/themeIcons.js +271 -0
- package/dist/runtime/themeIcons.js.map +1 -0
- package/dist/runtime/themeInk.d.ts +37 -0
- package/dist/runtime/themeInk.js +111 -0
- package/dist/runtime/themeInk.js.map +1 -0
- package/dist/runtime/themeInvariants.d.ts +39 -0
- package/dist/runtime/themeInvariants.js +273 -0
- package/dist/runtime/themeInvariants.js.map +1 -0
- package/dist/runtime/themeRuntime.d.ts +114 -0
- package/dist/runtime/themeRuntime.js +570 -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 +263 -0
- package/dist/runtime/themeSpec.js +435 -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 +176 -0
- package/dist/shell/InstallDialog.js.map +1 -0
- package/dist/shell/StoreView.d.ts +78 -0
- package/dist/shell/StoreView.js +108 -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 +44 -0
- package/dist/shell/frame/AppMenu.js +721 -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 +9 -0
- package/dist/shell/frame/Shell.js +1139 -0
- package/dist/shell/frame/Shell.js.map +1 -0
- package/dist/shell/frame/Titlebar.d.ts +17 -0
- package/dist/shell/frame/Titlebar.js +79 -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 +185 -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 +127 -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 +37 -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 +47 -0
- package/dist/shell/frame/chromeTheme.js +118 -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/edges.css +159 -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 +47 -0
- package/dist/shell/frame/mediaModel.js +67 -0
- package/dist/shell/frame/mediaModel.js.map +1 -0
- package/dist/shell/frame/menuMessages.en.json +96 -0
- package/dist/shell/frame/menuModel.d.ts +319 -0
- package/dist/shell/frame/menuModel.js +301 -0
- package/dist/shell/frame/menuModel.js.map +1 -0
- package/dist/shell/frame/nativeMenu.d.ts +5 -0
- package/dist/shell/frame/nativeMenu.js +178 -0
- package/dist/shell/frame/nativeMenu.js.map +1 -0
- package/dist/shell/frame/nativeMenuModel.d.ts +102 -0
- package/dist/shell/frame/nativeMenuModel.js +186 -0
- package/dist/shell/frame/nativeMenuModel.js.map +1 -0
- package/dist/shell/frame/paletteModel.d.ts +139 -0
- package/dist/shell/frame/paletteModel.js +114 -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 +634 -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 +101 -0
- package/dist/shell/frame/state.js +286 -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 +267 -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 +137 -0
- package/dist/shell/installController.js +172 -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 +265 -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 +117 -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 +7 -0
- package/dist/shell/panel/StoreSection.js +78 -0
- package/dist/shell/panel/StoreSection.js.map +1 -0
- package/dist/shell/panel/StringPacks.d.ts +20 -0
- package/dist/shell/panel/StringPacks.js +131 -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 +240 -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 +73 -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 +803 -0
- package/dist/shell/panel/panelMessages.js +779 -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 +250 -0
- package/dist/shell/storeMessages.d.ts +193 -0
- package/dist/shell/storeMessages.js +196 -0
- package/dist/shell/storeMessages.js.map +1 -0
- package/dist/shell/storeModel.d.ts +205 -0
- package/dist/shell/storeModel.js +146 -0
- package/dist/shell/storeModel.js.map +1 -0
- package/dist/shell/stringPacksModel.d.ts +44 -0
- package/dist/shell/stringPacksModel.js +99 -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 +655 -0
- package/languages.schema.json +23 -0
- package/locales/ar-x-fm.json +11 -0
- package/locales/ar.json +1388 -0
- package/locales/bg.json +1387 -0
- package/locales/bn.json +1387 -0
- package/locales/ca.json +1387 -0
- package/locales/cs.json +1387 -0
- package/locales/da.json +1387 -0
- package/locales/de.json +1387 -0
- package/locales/el.json +1387 -0
- package/locales/en-GB.json +27 -0
- package/locales/en.json +1386 -0
- package/locales/en.meta.json +51 -0
- package/locales/es.json +1387 -0
- package/locales/eu.json +1387 -0
- package/locales/fa.json +1388 -0
- package/locales/fi.json +1387 -0
- package/locales/fil.json +1387 -0
- package/locales/fr.json +1387 -0
- package/locales/ga.json +1387 -0
- package/locales/gl.json +1387 -0
- package/locales/gu.json +1387 -0
- package/locales/ha.json +1387 -0
- package/locales/he.json +1388 -0
- package/locales/hi.json +1387 -0
- package/locales/hr.json +1387 -0
- package/locales/hu.json +1387 -0
- package/locales/id.json +1387 -0
- package/locales/ig.json +1387 -0
- package/locales/it.json +1387 -0
- package/locales/ja.json +1387 -0
- package/locales/kn.json +1387 -0
- package/locales/ko.json +1387 -0
- package/locales/mr.json +1387 -0
- package/locales/ms.json +1387 -0
- package/locales/nb.json +1387 -0
- package/locales/nl.json +1387 -0
- package/locales/pl.json +1387 -0
- package/locales/pt.json +1387 -0
- package/locales/ro.json +1387 -0
- package/locales/ru.json +1387 -0
- package/locales/sk.json +1387 -0
- package/locales/sr.json +1387 -0
- package/locales/sv.json +1387 -0
- package/locales/ta.json +1387 -0
- package/locales/th.json +1387 -0
- package/locales/tr.json +1387 -0
- package/locales/uk.json +1387 -0
- package/locales/ur.json +1388 -0
- package/locales/vi.json +1387 -0
- package/locales/yo.json +1387 -0
- package/locales/zh-Hans.json +1387 -0
- package/locales/zh-Hant.json +1387 -0
- package/package.json +138 -5
- package/store-catalog.schema.json +78 -0
- package/theme.schema.json +4668 -0
- package/windows/installer.nsi +946 -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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// `api.theme`: the active theme as plain values, so plugins (and the UI kit) can match it. The site
|
|
2
|
+
// adapter resolves its own theme document into a `ResolvedTheme` (colors by role, radius, font,
|
|
3
|
+
// mode) and says when it changes; this module turns that into `--wb-*` CSS custom properties and
|
|
4
|
+
// a small `current()` / `onChange()` API. When the site keeps its own colors (no theme, e.g. X
|
|
5
|
+
// default), the adapter still resolves the site's palette, so plugins always see real colors.
|
|
6
|
+
//
|
|
7
|
+
// Pure except `createThemeApi` / `readPageTheme`, which read the page.
|
|
8
|
+
/**
|
|
9
|
+
* Font roles: `interface` (the site's UI), `post` (main content text), `heading` (names and
|
|
10
|
+
* headings), `numbers` (counts), `mono` (code), `chrome` (the app's own window: tabs, address bar,
|
|
11
|
+
* panel) and `dialog` (plugin dialogs and toasts from the UI kit).
|
|
12
|
+
*/
|
|
13
|
+
export const FONT_ROLE_NAMES = ['interface', 'post', 'heading', 'numbers', 'mono', 'chrome', 'dialog'];
|
|
14
|
+
/** Monospaced families, in order: what a `mono` role that inherits (or a source without fonts) gets. */
|
|
15
|
+
export const MONO_FAMILIES = ['Cascadia Mono', 'SF Mono', 'Consolas', 'Menlo', 'ui-monospace', 'monospace'];
|
|
16
|
+
export const MONO_FONT = MONO_FAMILIES.map(name => (/^(ui-monospace|monospace)$/.test(name) ? name : `"${name}"`)).join(', ');
|
|
17
|
+
/** Where the theme file JSON Schema is published with the `waybetter` npm package (`"$schema"` in theme files). */
|
|
18
|
+
export const THEME_SCHEMA_URL = 'https://unpkg.com/waybetter/theme.schema.json';
|
|
19
|
+
export const FALLBACK_FONT = 'system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';
|
|
20
|
+
const HEX = /^#[0-9a-f]{6}$/i;
|
|
21
|
+
/** `#rrggbb` → [r, g, b]. */
|
|
22
|
+
export function hexToRgb(hex) {
|
|
23
|
+
const n = parseInt(hex.slice(1, 7), 16);
|
|
24
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
25
|
+
}
|
|
26
|
+
/** WCAG relative luminance of `#rrggbb`. */
|
|
27
|
+
export function relativeLuminance(hex) {
|
|
28
|
+
const [r, g, b] = hexToRgb(hex).map(v => { const c = v / 255; return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4; });
|
|
29
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
30
|
+
}
|
|
31
|
+
/** WCAG contrast ratio of two `#rrggbb` colors (1–21). */
|
|
32
|
+
export function contrastRatio(a, b) {
|
|
33
|
+
const [x, y] = [relativeLuminance(a), relativeLuminance(b)].sort((m, n) => n - m);
|
|
34
|
+
return (x + 0.05) / (y + 0.05);
|
|
35
|
+
}
|
|
36
|
+
/** White when it reaches 4.5:1 on `hex`, otherwise whichever of black and white reads better. */
|
|
37
|
+
export function readableOn(hex) {
|
|
38
|
+
const white = contrastRatio(hex, '#ffffff');
|
|
39
|
+
return white >= 4.5 || white >= contrastRatio(hex, '#000000') ? '#ffffff' : '#000000';
|
|
40
|
+
}
|
|
41
|
+
/** `rgb(…)` / `rgba(…)` / `#rgb` / `#rrggbb` to `#rrggbb`; null when it is transparent or unparsable. */
|
|
42
|
+
export function toHex(value) {
|
|
43
|
+
const text = (value ?? '').trim();
|
|
44
|
+
if (HEX.test(text))
|
|
45
|
+
return text.toLowerCase();
|
|
46
|
+
const short = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(text);
|
|
47
|
+
if (short)
|
|
48
|
+
return `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}`.toLowerCase();
|
|
49
|
+
const match = /^rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/.exec(text);
|
|
50
|
+
if (!match)
|
|
51
|
+
return null;
|
|
52
|
+
if (match[4] !== undefined && parseFloat(match[4]) === 0)
|
|
53
|
+
return null;
|
|
54
|
+
return `#${[match[1], match[2], match[3]].map(v => Math.min(255, Number(v)).toString(16).padStart(2, '0')).join('')}`;
|
|
55
|
+
}
|
|
56
|
+
/** `a` moved toward `b` by `t` (0–1), per channel. */
|
|
57
|
+
export function mix(a, b, t) {
|
|
58
|
+
const [x, y] = [hexToRgb(a), hexToRgb(b)];
|
|
59
|
+
return `#${x.map((v, i) => Math.round(v + (y[i] - v) * t).toString(16).padStart(2, '0')).join('')}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A complete resolved theme from a few page colors (background, text, accent), for sites or moments
|
|
63
|
+
* without a theme engine. Surfaces, border and muted text sit between background and text.
|
|
64
|
+
*/
|
|
65
|
+
export function themeFromColors(bg, text, accent, extra = {}) {
|
|
66
|
+
const dark = relativeLuminance(bg) < relativeLuminance(text);
|
|
67
|
+
const colors = {
|
|
68
|
+
bg, text, accent,
|
|
69
|
+
surface: mix(bg, text, dark ? 0.05 : 0.03),
|
|
70
|
+
elevated: mix(bg, text, dark ? 0.1 : 0.07),
|
|
71
|
+
border: mix(bg, text, dark ? 0.2 : 0.15),
|
|
72
|
+
muted: mix(bg, text, 0.62),
|
|
73
|
+
onAccent: readableOn(accent),
|
|
74
|
+
inverse: text,
|
|
75
|
+
onInverse: bg,
|
|
76
|
+
page: bg,
|
|
77
|
+
};
|
|
78
|
+
return { id: 'page', name: 'Page colors', mode: dark ? 'dark' : 'light', siteDefault: true, radius: 16, buttonRadius: 9999, font: FALLBACK_FONT, ...extra, colors };
|
|
79
|
+
}
|
|
80
|
+
/** The page's own colors (computed body background and text), used when no theme source exists. */
|
|
81
|
+
export function readPageTheme(win, accent = '#1d9bf0') {
|
|
82
|
+
let bg = null;
|
|
83
|
+
let text = null;
|
|
84
|
+
let font = '';
|
|
85
|
+
try {
|
|
86
|
+
const body = win.document.body;
|
|
87
|
+
if (body) {
|
|
88
|
+
const style = win.getComputedStyle(body);
|
|
89
|
+
bg = toHex(style.backgroundColor) ?? toHex(win.getComputedStyle(win.document.documentElement).backgroundColor);
|
|
90
|
+
text = toHex(style.color);
|
|
91
|
+
font = style.fontFamily;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch { /* No layout yet. */ }
|
|
95
|
+
const dark = !!win.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
96
|
+
bg ??= dark ? '#000000' : '#ffffff';
|
|
97
|
+
text ??= relativeLuminance(bg) < 0.2 ? '#e7e9ea' : '#0f1419';
|
|
98
|
+
if (contrastRatio(bg, text) < 3)
|
|
99
|
+
text = readableOn(bg);
|
|
100
|
+
return themeFromColors(bg, text, accent, font ? { font } : {});
|
|
101
|
+
}
|
|
102
|
+
/** Fills in any role a source left out and freezes the result. */
|
|
103
|
+
export function normalizeResolved(theme) {
|
|
104
|
+
const bg = toHex(theme.colors?.bg) ?? '#ffffff';
|
|
105
|
+
let text = toHex(theme.colors?.text) ?? readableOn(bg);
|
|
106
|
+
if (contrastRatio(bg, text) < 3)
|
|
107
|
+
text = readableOn(bg);
|
|
108
|
+
const base = themeFromColors(bg, text, toHex(theme.colors?.accent) ?? '#1d9bf0');
|
|
109
|
+
const colors = { ...base.colors };
|
|
110
|
+
for (const key of Object.keys(colors)) {
|
|
111
|
+
const value = theme.colors?.[key];
|
|
112
|
+
if (key !== 'bg' && key !== 'text' && typeof value === 'string' && HEX.test(value))
|
|
113
|
+
colors[key] = value.toLowerCase();
|
|
114
|
+
}
|
|
115
|
+
if (!HEX.test(theme.colors.onAccent ?? ''))
|
|
116
|
+
colors.onAccent = readableOn(colors.accent);
|
|
117
|
+
const radius = Number.isFinite(theme.radius) ? Math.max(0, Math.min(28, theme.radius)) : 16;
|
|
118
|
+
const buttonRadius = Number.isFinite(theme.buttonRadius) ? Math.max(0, theme.buttonRadius) : 9999;
|
|
119
|
+
const font = theme.font || FALLBACK_FONT;
|
|
120
|
+
return Object.freeze({
|
|
121
|
+
id: String(theme.id || 'theme'), name: String(theme.name || ''), mode: theme.mode === 'light' ? 'light' : 'dark',
|
|
122
|
+
siteDefault: !!theme.siteDefault, radius, buttonRadius, font,
|
|
123
|
+
colors: Object.freeze(colors),
|
|
124
|
+
fonts: normalizeFonts(theme.fonts, font),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const cssSafe = (value) => value.replace(/[;{}<>\\]/g, '');
|
|
128
|
+
const finite = (value, min, max) => typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : null;
|
|
129
|
+
/** Every role filled in: missing roles inherit the interface font (`mono` a monospaced stack). */
|
|
130
|
+
export function normalizeFonts(fonts, font) {
|
|
131
|
+
const out = {};
|
|
132
|
+
const base = fonts?.interface?.stack ? cssSafe(fonts.interface.stack) : font;
|
|
133
|
+
for (const role of FONT_ROLE_NAMES) {
|
|
134
|
+
const given = fonts?.[role];
|
|
135
|
+
const stack = given?.stack ? cssSafe(given.stack) : role === 'mono' ? MONO_FONT : base;
|
|
136
|
+
out[role] = Object.freeze({
|
|
137
|
+
stack,
|
|
138
|
+
weight: finite(given?.weight, 100, 900),
|
|
139
|
+
size: finite(given?.size, 0.5, 2) ?? 1,
|
|
140
|
+
tracking: finite(given?.tracking, -0.1, 0.2),
|
|
141
|
+
tabular: !!given?.tabular,
|
|
142
|
+
inherited: given ? !!given.inherited : true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return Object.freeze(out);
|
|
146
|
+
}
|
|
147
|
+
const VAR_NAMES = {
|
|
148
|
+
bg: '--wb-bg', surface: '--wb-surface', elevated: '--wb-elevated', border: '--wb-border', text: '--wb-text', muted: '--wb-muted',
|
|
149
|
+
accent: '--wb-accent', onAccent: '--wb-on-accent', inverse: '--wb-inverse', onInverse: '--wb-on-inverse', page: '--wb-page',
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* The theme as CSS custom properties: `--wb-bg`, `--wb-surface`, `--wb-elevated`, `--wb-border`,
|
|
153
|
+
* `--wb-text`, `--wb-muted`, `--wb-accent`, `--wb-on-accent`, `--wb-inverse`, `--wb-on-inverse`,
|
|
154
|
+
* `--wb-page`, `--wb-radius`, `--wb-button-radius`, `--wb-font`, `--wb-mode` (`light`/`dark`), and
|
|
155
|
+
* one stack per font role: `--wb-font-interface`, `--wb-font-post`, `--wb-font-heading`,
|
|
156
|
+
* `--wb-font-numbers`, `--wb-font-mono`, `--wb-font-chrome`, `--wb-font-dialog`.
|
|
157
|
+
*/
|
|
158
|
+
export function themeVars(theme) {
|
|
159
|
+
const vars = {};
|
|
160
|
+
for (const [key, name] of Object.entries(VAR_NAMES))
|
|
161
|
+
vars[name] = theme.colors[key];
|
|
162
|
+
vars['--wb-radius'] = `${theme.radius}px`;
|
|
163
|
+
vars['--wb-button-radius'] = `${theme.buttonRadius}px`;
|
|
164
|
+
vars['--wb-font'] = theme.font;
|
|
165
|
+
const fonts = theme.fonts ?? normalizeFonts(undefined, theme.font);
|
|
166
|
+
for (const role of FONT_ROLE_NAMES)
|
|
167
|
+
vars[`--wb-font-${role}`] = fonts[role].stack;
|
|
168
|
+
vars['--wb-mode'] = theme.mode;
|
|
169
|
+
return vars;
|
|
170
|
+
}
|
|
171
|
+
/** `:root{--wb-…}` for a `<style>` element (values come from a resolved theme, never raw input). */
|
|
172
|
+
export function themeVarsCss(theme, selector = ':root') {
|
|
173
|
+
const safe = (value) => value.replace(/[;{}<>\\]/g, '');
|
|
174
|
+
return `${selector}{${Object.entries(themeVars(theme)).map(([name, value]) => `${name}:${safe(value)}`).join(';')}}`;
|
|
175
|
+
}
|
|
176
|
+
/** A stable key: equal keys mean nothing visible changed. */
|
|
177
|
+
export const themeKey = (theme) => JSON.stringify(theme);
|
|
178
|
+
/**
|
|
179
|
+
* `api.theme`. With a site `source` it reports the site's resolved theme (falling back to the page's
|
|
180
|
+
* computed colors while the source has none); without one, only the page's colors.
|
|
181
|
+
*/
|
|
182
|
+
export function createThemeApi(win, source, fallbackAccent) {
|
|
183
|
+
const report = (error) => console.error('[wayBetter] theme listener failed', error);
|
|
184
|
+
const current = () => {
|
|
185
|
+
let resolved = null;
|
|
186
|
+
try {
|
|
187
|
+
resolved = source?.current() ?? null;
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
report(error);
|
|
191
|
+
}
|
|
192
|
+
return normalizeResolved(resolved ?? readPageTheme(win, fallbackAccent));
|
|
193
|
+
};
|
|
194
|
+
return Object.freeze({
|
|
195
|
+
current,
|
|
196
|
+
onChange(listener) {
|
|
197
|
+
if (!source)
|
|
198
|
+
return () => { };
|
|
199
|
+
let last = '';
|
|
200
|
+
try {
|
|
201
|
+
last = themeKey(current());
|
|
202
|
+
}
|
|
203
|
+
catch { /* Reported by current(). */ }
|
|
204
|
+
return source.onChange(() => {
|
|
205
|
+
const next = current();
|
|
206
|
+
const key = themeKey(next);
|
|
207
|
+
if (key === last)
|
|
208
|
+
return;
|
|
209
|
+
last = key;
|
|
210
|
+
try {
|
|
211
|
+
listener(next);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
report(error);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=themeApi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeApi.js","sourceRoot":"","sources":["../../../runtime/src/themeApi.ts"],"names":[],"mappings":"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","sourcesContent":["// `api.theme`: the active theme as plain values, so plugins (and the UI kit) can match it. The site\n// adapter resolves its own theme document into a `ResolvedTheme` (colors by role, radius, font,\n// mode) and says when it changes; this module turns that into `--wb-*` CSS custom properties and\n// a small `current()` / `onChange()` API. When the site keeps its own colors (no theme, e.g. X\n// default), the adapter still resolves the site's palette, so plugins always see real colors.\n//\n// Pure except `createThemeApi` / `readPageTheme`, which read the page.\n\nexport type ThemeMode = 'light' | 'dark';\n\n/** Every color role a theme has, resolved (derived roles filled in, legibility enforced). */\nexport type ResolvedColors = {\n /** The main surface behind content (the site's column background). */\n bg: string;\n /** A raised surface (cards, inputs). */\n surface: string;\n /** Hover / selected surface, one step above `surface`. */\n elevated: string;\n border: string;\n text: string;\n /** Secondary text. */\n muted: string;\n accent: string;\n /** Readable text on `accent`. */\n onAccent: string;\n /** The site's filled primary button and its label. */\n inverse: string;\n onInverse: string;\n /** The canvas behind the columns (equals `bg` unless the theme paints a page). */\n page: string;\n};\n\nexport type ResolvedTheme = {\n /** Theme id (`x` for the site's own look). */\n id: string;\n name: string;\n mode: ThemeMode;\n /** True when the site's own colors are shown (no theme colors). */\n siteDefault: boolean;\n colors: ResolvedColors;\n /** Card / dialog corner radius in px. */\n radius: number;\n /** Button corner radius in px (9999 = pill). */\n buttonRadius: number;\n /** CSS `font-family` stack of the interface. */\n font: string;\n /**\n * Which font goes where: every role resolved (a role that inherits carries the stack it inherits).\n * Always present after `normalizeResolved`; adapters without font roles may leave it out.\n */\n fonts?: ResolvedFonts;\n};\n\n/**\n * Font roles: `interface` (the site's UI), `post` (main content text), `heading` (names and\n * headings), `numbers` (counts), `mono` (code), `chrome` (the app's own window: tabs, address bar,\n * panel) and `dialog` (plugin dialogs and toasts from the UI kit).\n */\nexport const FONT_ROLE_NAMES = ['interface', 'post', 'heading', 'numbers', 'mono', 'chrome', 'dialog'] as const;\nexport type FontRoleName = typeof FONT_ROLE_NAMES[number];\nexport type ResolvedFont = {\n /** CSS `font-family` stack. */\n stack: string;\n /** Weight the role asks for, or null to keep the text's own. */\n weight: number | null;\n /** Size relative to the site's (1 = unchanged). */\n size: number;\n /** Letter spacing in em, or null to keep the text's own. */\n tracking: number | null;\n /** Tabular (fixed-width) figures. */\n tabular: boolean;\n /** True when the role inherits its family from another role or the site. */\n inherited: boolean;\n};\nexport type ResolvedFonts = Record<FontRoleName, ResolvedFont>;\n/** Monospaced families, in order: what a `mono` role that inherits (or a source without fonts) gets. */\nexport const MONO_FAMILIES = ['Cascadia Mono', 'SF Mono', 'Consolas', 'Menlo', 'ui-monospace', 'monospace'];\nexport const MONO_FONT = MONO_FAMILIES.map(name => (/^(ui-monospace|monospace)$/.test(name) ? name : `\"${name}\"`)).join(', ');\n\n/** What a site adapter provides: the current resolved theme and change notifications. */\nexport type ThemeSource = {\n current(): ResolvedTheme | null;\n onChange(listener: () => void): () => void;\n};\n\nexport type ThemeApi = {\n /** The active theme, resolved. A frozen snapshot: call again after `onChange`. */\n current(): ResolvedTheme;\n /** Calls `listener(theme)` after the theme (or the site's light/dark mode) changes. Returns a dispose. */\n onChange(listener: (theme: ResolvedTheme) => void): () => void;\n};\n\n/** Where the theme file JSON Schema is published with the `waybetter` npm package (`\"$schema\"` in theme files). */\nexport const THEME_SCHEMA_URL = 'https://unpkg.com/waybetter/theme.schema.json';\n\nexport const FALLBACK_FONT = 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif';\n\nconst HEX = /^#[0-9a-f]{6}$/i;\n\n/** `#rrggbb` → [r, g, b]. */\nexport function hexToRgb(hex: string): [number, number, number] {\n const n = parseInt(hex.slice(1, 7), 16);\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\n\n/** WCAG relative luminance of `#rrggbb`. */\nexport function relativeLuminance(hex: string): number {\n const [r, g, b] = hexToRgb(hex).map(v => { const c = v / 255; return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4; }) as [number, number, number];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio of two `#rrggbb` colors (1–21). */\nexport function contrastRatio(a: string, b: string): number {\n const [x, y] = [relativeLuminance(a), relativeLuminance(b)].sort((m, n) => n - m) as [number, number];\n return (x + 0.05) / (y + 0.05);\n}\n\n/** White when it reaches 4.5:1 on `hex`, otherwise whichever of black and white reads better. */\nexport function readableOn(hex: string): string {\n const white = contrastRatio(hex, '#ffffff');\n return white >= 4.5 || white >= contrastRatio(hex, '#000000') ? '#ffffff' : '#000000';\n}\n\n/** `rgb(…)` / `rgba(…)` / `#rgb` / `#rrggbb` to `#rrggbb`; null when it is transparent or unparsable. */\nexport function toHex(value: string | null | undefined): string | null {\n const text = (value ?? '').trim();\n if (HEX.test(text)) return text.toLowerCase();\n const short = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(text);\n if (short) return `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}`.toLowerCase();\n const match = /^rgba?\\(\\s*(\\d+)[,\\s]+(\\d+)[,\\s]+(\\d+)(?:\\s*[,/]\\s*([\\d.]+%?))?\\s*\\)$/.exec(text);\n if (!match) return null;\n if (match[4] !== undefined && parseFloat(match[4]) === 0) return null;\n return `#${[match[1], match[2], match[3]].map(v => Math.min(255, Number(v)).toString(16).padStart(2, '0')).join('')}`;\n}\n\n/** `a` moved toward `b` by `t` (0–1), per channel. */\nexport function mix(a: string, b: string, t: number): string {\n const [x, y] = [hexToRgb(a), hexToRgb(b)];\n return `#${x.map((v, i) => Math.round(v + (y[i]! - v) * t).toString(16).padStart(2, '0')).join('')}`;\n}\n\n/**\n * A complete resolved theme from a few page colors (background, text, accent), for sites or moments\n * without a theme engine. Surfaces, border and muted text sit between background and text.\n */\nexport function themeFromColors(bg: string, text: string, accent: string, extra: Partial<Omit<ResolvedTheme, 'colors'>> = {}): ResolvedTheme {\n const dark = relativeLuminance(bg) < relativeLuminance(text);\n const colors: ResolvedColors = {\n bg, text, accent,\n surface: mix(bg, text, dark ? 0.05 : 0.03),\n elevated: mix(bg, text, dark ? 0.1 : 0.07),\n border: mix(bg, text, dark ? 0.2 : 0.15),\n muted: mix(bg, text, 0.62),\n onAccent: readableOn(accent),\n inverse: text,\n onInverse: bg,\n page: bg,\n };\n return { id: 'page', name: 'Page colors', mode: dark ? 'dark' : 'light', siteDefault: true, radius: 16, buttonRadius: 9999, font: FALLBACK_FONT, ...extra, colors };\n}\n\n/** The page's own colors (computed body background and text), used when no theme source exists. */\nexport function readPageTheme(win: Window, accent = '#1d9bf0'): ResolvedTheme {\n let bg: string | null = null;\n let text: string | null = null;\n let font = '';\n try {\n const body = win.document.body;\n if (body) {\n const style = win.getComputedStyle(body);\n bg = toHex(style.backgroundColor) ?? toHex(win.getComputedStyle(win.document.documentElement).backgroundColor);\n text = toHex(style.color);\n font = style.fontFamily;\n }\n } catch { /* No layout yet. */ }\n const dark = !!win.matchMedia?.('(prefers-color-scheme: dark)').matches;\n bg ??= dark ? '#000000' : '#ffffff';\n text ??= relativeLuminance(bg) < 0.2 ? '#e7e9ea' : '#0f1419';\n if (contrastRatio(bg, text) < 3) text = readableOn(bg);\n return themeFromColors(bg, text, accent, font ? { font } : {});\n}\n\n/** Fills in any role a source left out and freezes the result. */\nexport function normalizeResolved(theme: ResolvedTheme): ResolvedTheme {\n const bg = toHex(theme.colors?.bg) ?? '#ffffff';\n let text = toHex(theme.colors?.text) ?? readableOn(bg);\n if (contrastRatio(bg, text) < 3) text = readableOn(bg);\n const base = themeFromColors(bg, text, toHex(theme.colors?.accent) ?? '#1d9bf0');\n const colors = { ...base.colors };\n for (const key of Object.keys(colors) as Array<keyof ResolvedColors>) {\n const value = theme.colors?.[key];\n if (key !== 'bg' && key !== 'text' && typeof value === 'string' && HEX.test(value)) colors[key] = value.toLowerCase();\n }\n if (!HEX.test(theme.colors.onAccent ?? '')) colors.onAccent = readableOn(colors.accent);\n const radius = Number.isFinite(theme.radius) ? Math.max(0, Math.min(28, theme.radius)) : 16;\n const buttonRadius = Number.isFinite(theme.buttonRadius) ? Math.max(0, theme.buttonRadius) : 9999;\n const font = theme.font || FALLBACK_FONT;\n return Object.freeze({\n id: String(theme.id || 'theme'), name: String(theme.name || ''), mode: theme.mode === 'light' ? 'light' : 'dark',\n siteDefault: !!theme.siteDefault, radius, buttonRadius, font,\n colors: Object.freeze(colors),\n fonts: normalizeFonts(theme.fonts, font),\n });\n}\n\nconst cssSafe = (value: string) => value.replace(/[;{}<>\\\\]/g, '');\nconst finite = (value: unknown, min: number, max: number): number | null =>\n typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : null;\n\n/** Every role filled in: missing roles inherit the interface font (`mono` a monospaced stack). */\nexport function normalizeFonts(fonts: Partial<Record<FontRoleName, Partial<ResolvedFont>>> | undefined, font: string): ResolvedFonts {\n const out = {} as Record<FontRoleName, ResolvedFont>;\n const base = fonts?.interface?.stack ? cssSafe(fonts.interface.stack) : font;\n for (const role of FONT_ROLE_NAMES) {\n const given = fonts?.[role];\n const stack = given?.stack ? cssSafe(given.stack) : role === 'mono' ? MONO_FONT : base;\n out[role] = Object.freeze({\n stack,\n weight: finite(given?.weight, 100, 900),\n size: finite(given?.size, 0.5, 2) ?? 1,\n tracking: finite(given?.tracking, -0.1, 0.2),\n tabular: !!given?.tabular,\n inherited: given ? !!given.inherited : true,\n });\n }\n return Object.freeze(out);\n}\n\nconst VAR_NAMES: Record<keyof ResolvedColors, string> = {\n bg: '--wb-bg', surface: '--wb-surface', elevated: '--wb-elevated', border: '--wb-border', text: '--wb-text', muted: '--wb-muted',\n accent: '--wb-accent', onAccent: '--wb-on-accent', inverse: '--wb-inverse', onInverse: '--wb-on-inverse', page: '--wb-page',\n};\n\n/**\n * The theme as CSS custom properties: `--wb-bg`, `--wb-surface`, `--wb-elevated`, `--wb-border`,\n * `--wb-text`, `--wb-muted`, `--wb-accent`, `--wb-on-accent`, `--wb-inverse`, `--wb-on-inverse`,\n * `--wb-page`, `--wb-radius`, `--wb-button-radius`, `--wb-font`, `--wb-mode` (`light`/`dark`), and\n * one stack per font role: `--wb-font-interface`, `--wb-font-post`, `--wb-font-heading`,\n * `--wb-font-numbers`, `--wb-font-mono`, `--wb-font-chrome`, `--wb-font-dialog`.\n */\nexport function themeVars(theme: ResolvedTheme): Record<string, string> {\n const vars: Record<string, string> = {};\n for (const [key, name] of Object.entries(VAR_NAMES)) vars[name] = theme.colors[key as keyof ResolvedColors];\n vars['--wb-radius'] = `${theme.radius}px`;\n vars['--wb-button-radius'] = `${theme.buttonRadius}px`;\n vars['--wb-font'] = theme.font;\n const fonts = theme.fonts ?? normalizeFonts(undefined, theme.font);\n for (const role of FONT_ROLE_NAMES) vars[`--wb-font-${role}`] = fonts[role].stack;\n vars['--wb-mode'] = theme.mode;\n return vars;\n}\n\n/** `:root{--wb-…}` for a `<style>` element (values come from a resolved theme, never raw input). */\nexport function themeVarsCss(theme: ResolvedTheme, selector = ':root'): string {\n const safe = (value: string) => value.replace(/[;{}<>\\\\]/g, '');\n return `${selector}{${Object.entries(themeVars(theme)).map(([name, value]) => `${name}:${safe(value)}`).join(';')}}`;\n}\n\n/** A stable key: equal keys mean nothing visible changed. */\nexport const themeKey = (theme: ResolvedTheme) => JSON.stringify(theme);\n\n/**\n * `api.theme`. With a site `source` it reports the site's resolved theme (falling back to the page's\n * computed colors while the source has none); without one, only the page's colors.\n */\nexport function createThemeApi(win: Window, source?: ThemeSource, fallbackAccent?: string): ThemeApi {\n const report = (error: unknown) => console.error('[wayBetter] theme listener failed', error);\n const current = (): ResolvedTheme => {\n let resolved: ResolvedTheme | null = null;\n try { resolved = source?.current() ?? null; } catch (error) { report(error); }\n return normalizeResolved(resolved ?? readPageTheme(win, fallbackAccent));\n };\n return Object.freeze({\n current,\n onChange(listener: (theme: ResolvedTheme) => void) {\n if (!source) return () => {};\n let last = '';\n try { last = themeKey(current()); } catch { /* Reported by current(). */ }\n return source.onChange(() => {\n const next = current();\n const key = themeKey(next);\n if (key === last) return;\n last = key;\n try { listener(next); } catch (error) { report(error); }\n });\n },\n });\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const BAR_ATTR = "data-wb-bar";
|
|
2
|
+
export type BarMode = 'labels' | 'icons';
|
|
3
|
+
/** One item as the measurer sees it: its label text and font, and the fixed parts around the label. */
|
|
4
|
+
export type BarItem = {
|
|
5
|
+
label: string;
|
|
6
|
+
font: string;
|
|
7
|
+
fixed: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Pure: the mode for `items` in `available` px, with `gap` between items. Hysteresis: leaving icons
|
|
11
|
+
* mode needs 16px more room than staying in labels mode, so a bar never flaps at the threshold.
|
|
12
|
+
*/
|
|
13
|
+
export declare function barMode(items: Array<{
|
|
14
|
+
labelWidth: number;
|
|
15
|
+
fixed: number;
|
|
16
|
+
}>, available: number, gap: number, current: BarMode | null, hysteresis?: number): BarMode;
|
|
17
|
+
export type BarMeasurerOptions = {
|
|
18
|
+
/** The bar's item container (X: the nav). */
|
|
19
|
+
nav: string;
|
|
20
|
+
/** Items inside it, and inside an item its icon box and label box. */
|
|
21
|
+
item: string;
|
|
22
|
+
icon: string;
|
|
23
|
+
label: string;
|
|
24
|
+
/** Only while this matches (a bar layout is active). */
|
|
25
|
+
when?: string;
|
|
26
|
+
};
|
|
27
|
+
/** Measures on nav resize and font loads; writes the mode on <html>. Returns a stop function. */
|
|
28
|
+
export declare function installBarMeasurer(win: Window, options: BarMeasurerOptions): () => void;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Theme engine v3, step 9 (.omc/plans/theme-engine-v3.md 4.1): one bar component. Whether a bar's
|
|
2
|
+
// items carry their labels is measured, never guessed from a width breakpoint: the sum of the whole
|
|
3
|
+
// labels (text measured in the label's own font), icons, paddings and gaps against the room the items
|
|
4
|
+
// have. The mode lands on `html[data-wb-bar="labels|icons"]`; the bar's CSS keys on it. A label is
|
|
5
|
+
// therefore whole or absent, never cut ("H…", 1.0.2).
|
|
6
|
+
export const BAR_ATTR = 'data-wb-bar';
|
|
7
|
+
/**
|
|
8
|
+
* Pure: the mode for `items` in `available` px, with `gap` between items. Hysteresis: leaving icons
|
|
9
|
+
* mode needs 16px more room than staying in labels mode, so a bar never flaps at the threshold.
|
|
10
|
+
*/
|
|
11
|
+
export function barMode(items, available, gap, current, hysteresis = 16) {
|
|
12
|
+
if (!items.length || !items.some(item => item.labelWidth > 0))
|
|
13
|
+
return 'labels';
|
|
14
|
+
const need = items.reduce((sum, item) => sum + item.labelWidth + item.fixed, 0) + gap * Math.max(0, items.length - 1);
|
|
15
|
+
const room = current === 'icons' ? available - hysteresis : available;
|
|
16
|
+
return need <= room ? 'labels' : 'icons';
|
|
17
|
+
}
|
|
18
|
+
const px = (value) => { const n = parseFloat(value); return Number.isFinite(n) ? n : 0; };
|
|
19
|
+
/** Measures on nav resize and font loads; writes the mode on <html>. Returns a stop function. */
|
|
20
|
+
export function installBarMeasurer(win, options) {
|
|
21
|
+
const doc = win.document;
|
|
22
|
+
const canvas = doc.createElement('canvas');
|
|
23
|
+
const ctx = canvas.getContext('2d');
|
|
24
|
+
const textWidth = (text, font) => { if (!ctx)
|
|
25
|
+
return text.length * 8; ctx.font = font; return Math.ceil(ctx.measureText(text).width); };
|
|
26
|
+
let nav = null;
|
|
27
|
+
let frame = 0;
|
|
28
|
+
const measure = () => {
|
|
29
|
+
frame = 0;
|
|
30
|
+
const html = doc.documentElement;
|
|
31
|
+
if (options.when && !doc.querySelector(options.when)) {
|
|
32
|
+
html.removeAttribute(BAR_ATTR);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!nav?.isConnected)
|
|
36
|
+
return;
|
|
37
|
+
const items = [...nav.querySelectorAll(`:scope > ${options.item}`)].filter(el => el.offsetParent !== null || el.getClientRects().length);
|
|
38
|
+
const measured = items.map(item => {
|
|
39
|
+
const label = item.querySelector(options.label);
|
|
40
|
+
const icon = item.querySelector(options.icon);
|
|
41
|
+
const pill = item.firstElementChild ?? item;
|
|
42
|
+
const ps = win.getComputedStyle(pill);
|
|
43
|
+
const fixed = px(ps.paddingLeft) + px(ps.paddingRight) + px(ps.borderLeftWidth) + px(ps.borderRightWidth) + (icon ? Math.max(icon.getBoundingClientRect().width, 20) : 0);
|
|
44
|
+
if (!label || !label.textContent?.trim())
|
|
45
|
+
return { labelWidth: 0, fixed };
|
|
46
|
+
const ls = win.getComputedStyle(label);
|
|
47
|
+
// The label's own font, read even while the label is display:none (computed styles resolve it).
|
|
48
|
+
const font = `${ls.fontStyle} ${ls.fontWeight} ${ls.fontSize} ${ls.fontFamily}`;
|
|
49
|
+
return { labelWidth: textWidth(label.textContent.trim(), font) + px(ls.marginLeft) + px(ls.marginRight) + 2, fixed };
|
|
50
|
+
});
|
|
51
|
+
const ns = win.getComputedStyle(nav);
|
|
52
|
+
const available = nav.clientWidth - px(ns.paddingLeft) - px(ns.paddingRight);
|
|
53
|
+
const current = html.getAttribute(BAR_ATTR);
|
|
54
|
+
const mode = barMode(measured, available, px(ns.columnGap) || 0, current);
|
|
55
|
+
if (current !== mode)
|
|
56
|
+
html.setAttribute(BAR_ATTR, mode);
|
|
57
|
+
};
|
|
58
|
+
const schedule = () => { if (!frame)
|
|
59
|
+
frame = win.requestAnimationFrame(measure); };
|
|
60
|
+
const Resize = win.ResizeObserver;
|
|
61
|
+
const sized = Resize ? new Resize(schedule) : null;
|
|
62
|
+
const attach = () => {
|
|
63
|
+
const found = doc.querySelector(options.nav);
|
|
64
|
+
if (found !== nav) {
|
|
65
|
+
if (nav)
|
|
66
|
+
sized?.unobserve(nav);
|
|
67
|
+
nav = found;
|
|
68
|
+
if (nav)
|
|
69
|
+
sized?.observe(nav);
|
|
70
|
+
}
|
|
71
|
+
schedule();
|
|
72
|
+
};
|
|
73
|
+
attach();
|
|
74
|
+
// The nav mounts late and is replaced on some navigations; items and fonts change rarely.
|
|
75
|
+
const timer = win.setInterval(attach, 1500);
|
|
76
|
+
const fonts = doc.fonts;
|
|
77
|
+
fonts?.addEventListener?.('loadingdone', schedule);
|
|
78
|
+
win.addEventListener('resize', schedule);
|
|
79
|
+
return () => { win.clearInterval(timer); sized?.disconnect(); fonts?.removeEventListener?.('loadingdone', schedule); win.removeEventListener('resize', schedule); if (frame)
|
|
80
|
+
win.cancelAnimationFrame(frame); doc.documentElement.removeAttribute(BAR_ATTR); };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=themeBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeBar.js","sourceRoot":"","sources":["../../../runtime/src/themeBar.ts"],"names":[],"mappings":"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","sourcesContent":["// Theme engine v3, step 9 (.omc/plans/theme-engine-v3.md 4.1): one bar component. Whether a bar's\n// items carry their labels is measured, never guessed from a width breakpoint: the sum of the whole\n// labels (text measured in the label's own font), icons, paddings and gaps against the room the items\n// have. The mode lands on `html[data-wb-bar=\"labels|icons\"]`; the bar's CSS keys on it. A label is\n// therefore whole or absent, never cut (\"H…\", 1.0.2).\n\nexport const BAR_ATTR = 'data-wb-bar';\nexport type BarMode = 'labels' | 'icons';\n\n/** One item as the measurer sees it: its label text and font, and the fixed parts around the label. */\nexport type BarItem = { label: string; font: string; fixed: number };\n\n/**\n * Pure: the mode for `items` in `available` px, with `gap` between items. Hysteresis: leaving icons\n * mode needs 16px more room than staying in labels mode, so a bar never flaps at the threshold.\n */\nexport function barMode(items: Array<{ labelWidth: number; fixed: number }>, available: number, gap: number, current: BarMode | null, hysteresis = 16): BarMode {\n if (!items.length || !items.some(item => item.labelWidth > 0)) return 'labels';\n const need = items.reduce((sum, item) => sum + item.labelWidth + item.fixed, 0) + gap * Math.max(0, items.length - 1);\n const room = current === 'icons' ? available - hysteresis : available;\n return need <= room ? 'labels' : 'icons';\n}\n\nexport type BarMeasurerOptions = {\n /** The bar's item container (X: the nav). */\n nav: string;\n /** Items inside it, and inside an item its icon box and label box. */\n item: string; icon: string; label: string;\n /** Only while this matches (a bar layout is active). */\n when?: string;\n};\n\nconst px = (value: string) => { const n = parseFloat(value); return Number.isFinite(n) ? n : 0; };\n\n/** Measures on nav resize and font loads; writes the mode on <html>. Returns a stop function. */\nexport function installBarMeasurer(win: Window, options: BarMeasurerOptions): () => void {\n const doc = win.document;\n const canvas = doc.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const textWidth = (text: string, font: string) => { if (!ctx) return text.length * 8; ctx.font = font; return Math.ceil(ctx.measureText(text).width); };\n let nav: Element | null = null;\n let frame = 0;\n const measure = () => {\n frame = 0;\n const html = doc.documentElement;\n if (options.when && !doc.querySelector(options.when)) { html.removeAttribute(BAR_ATTR); return; }\n if (!nav?.isConnected) return;\n const items = [...nav.querySelectorAll(`:scope > ${options.item}`)].filter(el => (el as HTMLElement).offsetParent !== null || el.getClientRects().length);\n const measured = items.map(item => {\n const label = item.querySelector(options.label) as HTMLElement | null;\n const icon = item.querySelector(options.icon) as HTMLElement | null;\n const pill = (item.firstElementChild as HTMLElement | null) ?? (item as HTMLElement);\n const ps = win.getComputedStyle(pill);\n const fixed = px(ps.paddingLeft) + px(ps.paddingRight) + px(ps.borderLeftWidth) + px(ps.borderRightWidth) + (icon ? Math.max(icon.getBoundingClientRect().width, 20) : 0);\n if (!label || !label.textContent?.trim()) return { labelWidth: 0, fixed };\n const ls = win.getComputedStyle(label);\n // The label's own font, read even while the label is display:none (computed styles resolve it).\n const font = `${ls.fontStyle} ${ls.fontWeight} ${ls.fontSize} ${ls.fontFamily}`;\n return { labelWidth: textWidth(label.textContent.trim(), font) + px(ls.marginLeft) + px(ls.marginRight) + 2, fixed };\n });\n const ns = win.getComputedStyle(nav);\n const available = (nav as HTMLElement).clientWidth - px(ns.paddingLeft) - px(ns.paddingRight);\n const current = (html.getAttribute(BAR_ATTR) as BarMode | null);\n const mode = barMode(measured, available, px(ns.columnGap) || 0, current);\n if (current !== mode) html.setAttribute(BAR_ATTR, mode);\n };\n const schedule = () => { if (!frame) frame = win.requestAnimationFrame(measure); };\n const Resize = (win as Window & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver;\n const sized = Resize ? new Resize(schedule) : null;\n const attach = () => {\n const found = doc.querySelector(options.nav);\n if (found !== nav) { if (nav) sized?.unobserve(nav); nav = found; if (nav) sized?.observe(nav); }\n schedule();\n };\n attach();\n // The nav mounts late and is replaced on some navigations; items and fonts change rarely.\n const timer = win.setInterval(attach, 1500);\n const fonts = (doc as Document & { fonts?: FontFaceSet }).fonts;\n fonts?.addEventListener?.('loadingdone', schedule);\n win.addEventListener('resize', schedule);\n return () => { win.clearInterval(timer); sized?.disconnect(); fonts?.removeEventListener?.('loadingdone', schedule); win.removeEventListener('resize', schedule); if (frame) win.cancelAnimationFrame(frame); doc.documentElement.removeAttribute(BAR_ATTR); };\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type IconPack } from './themeIcons.ts';
|
|
2
|
+
import { type Palette, type ThemeOverrides, type ThemeSpec } from './themeSpec.ts';
|
|
3
|
+
/**
|
|
4
|
+
* What is persisted natively and handed to the page runtime. `icons` is the owner's icon pack (theme
|
|
5
|
+
* engine v2): absent follows the theme's suggestion, `site` keeps the site's own icons, else a pack id.
|
|
6
|
+
*/
|
|
7
|
+
export type ThemeChoice = {
|
|
8
|
+
preset: string;
|
|
9
|
+
accent: string | null;
|
|
10
|
+
overrides: ThemeOverrides;
|
|
11
|
+
themes: ThemeSpec[];
|
|
12
|
+
icons?: string;
|
|
13
|
+
packs?: IconPack[];
|
|
14
|
+
};
|
|
15
|
+
/** Icon packs the owner imported (standalone documents, kept with the choice so every site tab has them). */
|
|
16
|
+
export declare const MAX_USER_PACKS = 8;
|
|
17
|
+
/** Imported themes kept in a choice (the JSON Schema of the native side says the same). */
|
|
18
|
+
export declare const MAX_USER_THEMES = 24;
|
|
19
|
+
export type ThemeCatalog = {
|
|
20
|
+
/** The site's built-in themes; the first is its own look (and the default choice). */
|
|
21
|
+
builtins: readonly ThemeSpec[];
|
|
22
|
+
/** Nothing chosen: the first built-in theme, no customizations. */
|
|
23
|
+
defaultChoice: ThemeChoice;
|
|
24
|
+
/** Any JSON to a valid choice (imported themes sanitized, bounded and deduplicated; unknown ids fall back). */
|
|
25
|
+
normalize(input: unknown): ThemeChoice;
|
|
26
|
+
/** The theme document a choice refers to (built-in or imported), before overrides. */
|
|
27
|
+
base(choice: ThemeChoice): ThemeSpec;
|
|
28
|
+
/** The effective theme: the selected document with the panel's overrides and accent, legible. */
|
|
29
|
+
resolve(choice: ThemeChoice): ThemeSpec;
|
|
30
|
+
/** The effective palette: the theme's colors (or none for the site's own) with the accent override applied. */
|
|
31
|
+
palette(choice: ThemeChoice): Partial<Palette> | null;
|
|
32
|
+
/** Every theme that can be picked: the built-in ones, then the choice's imported ones. */
|
|
33
|
+
all(choice: ThemeChoice): ThemeSpec[];
|
|
34
|
+
};
|
|
35
|
+
export declare function createThemeCatalog(builtins: readonly ThemeSpec[]): ThemeCatalog;
|
|
36
|
+
/** The choice a tab's theme id stands for (themeScopes: account and pane themes): the global choice itself, or that theme without customizations. */
|
|
37
|
+
export declare function choiceForId(global: ThemeChoice, id: string): ThemeChoice;
|
|
38
|
+
/** The shape persisted natively: empty groups are dropped so "no override" stays unambiguous. */
|
|
39
|
+
export declare function serializeChoice(choice: ThemeChoice): {
|
|
40
|
+
packs?: IconPack[] | undefined;
|
|
41
|
+
icons?: string | undefined;
|
|
42
|
+
themes?: ThemeSpec[] | undefined;
|
|
43
|
+
overrides?: {
|
|
44
|
+
[k: string]: Record<string, string> | import("./themeSpec.ts").Background | Partial<import("./themeSpec.ts").ThemeColors> | (Partial<Omit<import("./themeSpec.ts").FontSpec, "roles">> & {
|
|
45
|
+
roles?: Partial<import("./themeSpec.ts").FontRoles>;
|
|
46
|
+
}) | Partial<import("./themeSpec.ts").ShapeSpec> | Partial<import("./themeSpec.ts").LayoutSpec> | Partial<import("./themeSpec.ts").EffectsSpec> | {
|
|
47
|
+
text: string;
|
|
48
|
+
};
|
|
49
|
+
} | undefined;
|
|
50
|
+
preset: string;
|
|
51
|
+
accent: string | null;
|
|
52
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// The owner's theme choice: which theme document is on (a built-in one or an imported one), the
|
|
2
|
+
// panel's customizations on top of it and an accent override. Native code persists it and hands it
|
|
3
|
+
// to every site tab; the shell's theme store and the panel edit it. A site brings its built-in
|
|
4
|
+
// themes (`createThemeCatalog(builtins)`; the first one is the site's own look, the default). Pure.
|
|
5
|
+
import { sanitizeIconPack } from "./themeIcons.js";
|
|
6
|
+
import { applyOverrides, isHex, sanitizeSpec } from "./themeSpec.js";
|
|
7
|
+
/** Icon packs the owner imported (standalone documents, kept with the choice so every site tab has them). */
|
|
8
|
+
export const MAX_USER_PACKS = 8;
|
|
9
|
+
/** Imported themes kept in a choice (the JSON Schema of the native side says the same). */
|
|
10
|
+
export const MAX_USER_THEMES = 24;
|
|
11
|
+
const OVERRIDE_GROUPS = ['colors', 'font', 'shape', 'layout', 'background', 'effects', 'css', 'variants'];
|
|
12
|
+
export function createThemeCatalog(builtins) {
|
|
13
|
+
if (!builtins.length)
|
|
14
|
+
throw new Error('wayBetter themes: a site needs at least one built-in theme (its own look)');
|
|
15
|
+
const first = builtins[0];
|
|
16
|
+
const defaultChoice = { preset: first.id, accent: null, overrides: {}, themes: [] };
|
|
17
|
+
const isBuiltin = (id) => builtins.some(item => item.id === id);
|
|
18
|
+
function normalize(input) {
|
|
19
|
+
const source = input && typeof input === 'object' ? input : {};
|
|
20
|
+
const themes = [];
|
|
21
|
+
if (Array.isArray(source.themes)) {
|
|
22
|
+
for (const raw of source.themes.slice(0, MAX_USER_THEMES)) {
|
|
23
|
+
const theme = sanitizeSpec(raw);
|
|
24
|
+
if (!isBuiltin(theme.id) && !themes.some(item => item.id === theme.id))
|
|
25
|
+
themes.push(theme);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const known = (id) => typeof id === 'string' && (isBuiltin(id) || themes.some(item => item.id === id));
|
|
29
|
+
const preset = known(source.preset) ? source.preset : first.id;
|
|
30
|
+
const accent = isHex(source.accent) ? source.accent.toLowerCase() : null;
|
|
31
|
+
const overrides = {};
|
|
32
|
+
const rawOverrides = source.overrides && typeof source.overrides === 'object' ? source.overrides : {};
|
|
33
|
+
for (const key of OVERRIDE_GROUPS) {
|
|
34
|
+
const value = rawOverrides[key];
|
|
35
|
+
if (value && typeof value === 'object' && !Array.isArray(value))
|
|
36
|
+
overrides[key] = value;
|
|
37
|
+
}
|
|
38
|
+
const icons = typeof source.icons === 'string' && /^[a-z0-9-]{1,40}$/.test(source.icons) && source.icons !== 'theme' ? source.icons : undefined;
|
|
39
|
+
const packs = [];
|
|
40
|
+
if (Array.isArray(source.packs)) {
|
|
41
|
+
for (const raw of source.packs.slice(0, MAX_USER_PACKS)) {
|
|
42
|
+
const pack = sanitizeIconPack(raw);
|
|
43
|
+
if (pack && !packs.some(item => item.id === pack.id))
|
|
44
|
+
packs.push(pack);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { preset, accent, overrides, themes, ...(icons ? { icons } : {}), ...(packs.length ? { packs } : {}) };
|
|
48
|
+
}
|
|
49
|
+
const base = (choice) => choice.themes.find(item => item.id === choice.preset) ?? builtins.find(item => item.id === choice.preset) ?? first;
|
|
50
|
+
const resolve = (choice) => applyOverrides(base(choice), choice.overrides, choice.accent);
|
|
51
|
+
function palette(choice) {
|
|
52
|
+
const theme = resolve(choice);
|
|
53
|
+
if (theme.colors)
|
|
54
|
+
return theme.colors;
|
|
55
|
+
return choice.accent ? { accent: choice.accent } : null;
|
|
56
|
+
}
|
|
57
|
+
const all = (choice) => [...builtins, ...choice.themes];
|
|
58
|
+
return { builtins, defaultChoice, normalize, base, resolve, palette, all };
|
|
59
|
+
}
|
|
60
|
+
/** The choice a tab's theme id stands for (themeScopes: account and pane themes): the global choice itself, or that theme without customizations. */
|
|
61
|
+
export function choiceForId(global, id) {
|
|
62
|
+
return id === global.preset ? global : { preset: id, accent: null, overrides: {}, themes: global.themes, ...(global.icons ? { icons: global.icons } : {}), ...(global.packs?.length ? { packs: global.packs } : {}) };
|
|
63
|
+
}
|
|
64
|
+
/** The shape persisted natively: empty groups are dropped so "no override" stays unambiguous. */
|
|
65
|
+
export function serializeChoice(choice) {
|
|
66
|
+
const overrides = Object.fromEntries(Object.entries(choice.overrides).filter(([, value]) => value && Object.keys(value).length));
|
|
67
|
+
return { preset: choice.preset, accent: choice.accent, ...(Object.keys(overrides).length ? { overrides } : {}), ...(choice.themes.length ? { themes: choice.themes } : {}), ...(choice.icons ? { icons: choice.icons } : {}), ...(choice.packs?.length ? { packs: choice.packs } : {}) };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=themeChoice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeChoice.js","sourceRoot":"","sources":["../../../runtime/src/themeChoice.ts"],"names":[],"mappings":"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","sourcesContent":["// The owner's theme choice: which theme document is on (a built-in one or an imported one), the\n// panel's customizations on top of it and an accent override. Native code persists it and hands it\n// to every site tab; the shell's theme store and the panel edit it. A site brings its built-in\n// themes (`createThemeCatalog(builtins)`; the first one is the site's own look, the default). Pure.\n\nimport { sanitizeIconPack, type IconPack } from './themeIcons.ts';\nimport { applyOverrides, isHex, sanitizeSpec, type Palette, type ThemeOverrides, type ThemeSpec } from './themeSpec.ts';\n\n/**\n * What is persisted natively and handed to the page runtime. `icons` is the owner's icon pack (theme\n * engine v2): absent follows the theme's suggestion, `site` keeps the site's own icons, else a pack id.\n */\nexport type ThemeChoice = { preset: string; accent: string | null; overrides: ThemeOverrides; themes: ThemeSpec[]; icons?: string; packs?: IconPack[] };\n\n/** Icon packs the owner imported (standalone documents, kept with the choice so every site tab has them). */\nexport const MAX_USER_PACKS = 8;\n\n/** Imported themes kept in a choice (the JSON Schema of the native side says the same). */\nexport const MAX_USER_THEMES = 24;\nconst OVERRIDE_GROUPS = ['colors', 'font', 'shape', 'layout', 'background', 'effects', 'css', 'variants'] as const;\n\nexport type ThemeCatalog = {\n /** The site's built-in themes; the first is its own look (and the default choice). */\n builtins: readonly ThemeSpec[];\n /** Nothing chosen: the first built-in theme, no customizations. */\n defaultChoice: ThemeChoice;\n /** Any JSON to a valid choice (imported themes sanitized, bounded and deduplicated; unknown ids fall back). */\n normalize(input: unknown): ThemeChoice;\n /** The theme document a choice refers to (built-in or imported), before overrides. */\n base(choice: ThemeChoice): ThemeSpec;\n /** The effective theme: the selected document with the panel's overrides and accent, legible. */\n resolve(choice: ThemeChoice): ThemeSpec;\n /** The effective palette: the theme's colors (or none for the site's own) with the accent override applied. */\n palette(choice: ThemeChoice): Partial<Palette> | null;\n /** Every theme that can be picked: the built-in ones, then the choice's imported ones. */\n all(choice: ThemeChoice): ThemeSpec[];\n};\n\nexport function createThemeCatalog(builtins: readonly ThemeSpec[]): ThemeCatalog {\n if (!builtins.length) throw new Error('wayBetter themes: a site needs at least one built-in theme (its own look)');\n const first = builtins[0]!;\n const defaultChoice: ThemeChoice = { preset: first.id, accent: null, overrides: {}, themes: [] };\n const isBuiltin = (id: unknown) => builtins.some(item => item.id === id);\n\n function normalize(input: unknown): ThemeChoice {\n const source = input && typeof input === 'object' ? input as Record<string, unknown> : {};\n const themes: ThemeSpec[] = [];\n if (Array.isArray(source.themes)) {\n for (const raw of source.themes.slice(0, MAX_USER_THEMES)) {\n const theme = sanitizeSpec(raw);\n if (!isBuiltin(theme.id) && !themes.some(item => item.id === theme.id)) themes.push(theme);\n }\n }\n const known = (id: unknown) => typeof id === 'string' && (isBuiltin(id) || themes.some(item => item.id === id));\n const preset = known(source.preset) ? source.preset as string : first.id;\n const accent = isHex(source.accent) ? source.accent.toLowerCase() : null;\n const overrides: ThemeOverrides = {};\n const rawOverrides = source.overrides && typeof source.overrides === 'object' ? source.overrides as Record<string, unknown> : {};\n for (const key of OVERRIDE_GROUPS) {\n const value = rawOverrides[key];\n if (value && typeof value === 'object' && !Array.isArray(value)) (overrides as Record<string, unknown>)[key] = value;\n }\n const icons = typeof source.icons === 'string' && /^[a-z0-9-]{1,40}$/.test(source.icons) && source.icons !== 'theme' ? source.icons : undefined;\n const packs: IconPack[] = [];\n if (Array.isArray(source.packs)) {\n for (const raw of source.packs.slice(0, MAX_USER_PACKS)) {\n const pack = sanitizeIconPack(raw);\n if (pack && !packs.some(item => item.id === pack.id)) packs.push(pack);\n }\n }\n return { preset, accent, overrides, themes, ...(icons ? { icons } : {}), ...(packs.length ? { packs } : {}) };\n }\n\n const base = (choice: ThemeChoice) => choice.themes.find(item => item.id === choice.preset) ?? builtins.find(item => item.id === choice.preset) ?? first;\n const resolve = (choice: ThemeChoice) => applyOverrides(base(choice), choice.overrides, choice.accent);\n function palette(choice: ThemeChoice): Partial<Palette> | null {\n const theme = resolve(choice);\n if (theme.colors) return theme.colors;\n return choice.accent ? { accent: choice.accent } : null;\n }\n const all = (choice: ThemeChoice) => [...builtins, ...choice.themes];\n return { builtins, defaultChoice, normalize, base, resolve, palette, all };\n}\n\n/** The choice a tab's theme id stands for (themeScopes: account and pane themes): the global choice itself, or that theme without customizations. */\nexport function choiceForId(global: ThemeChoice, id: string): ThemeChoice {\n return id === global.preset ? global : { preset: id, accent: null, overrides: {}, themes: global.themes, ...(global.icons ? { icons: global.icons } : {}), ...(global.packs?.length ? { packs: global.packs } : {}) };\n}\n\n/** The shape persisted natively: empty groups are dropped so \"no override\" stays unambiguous. */\nexport function serializeChoice(choice: ThemeChoice) {\n const overrides = Object.fromEntries(Object.entries(choice.overrides).filter(([, value]) => value && Object.keys(value).length));\n return { preset: choice.preset, accent: choice.accent, ...(Object.keys(overrides).length ? { overrides } : {}), ...(choice.themes.length ? { themes: choice.themes } : {}), ...(choice.icons ? { icons: choice.icons } : {}), ...(choice.packs?.length ? { packs: choice.packs } : {}) };\n}\n"]}
|