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
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
// Theme documents: a wayBetter theme is a JSON document (like a BetterDiscord theme, but data instead
|
|
2
|
+
// of a stylesheet) describing colors, type, shape, density, layout, background and motion; the format
|
|
3
|
+
// is `theme.schema.json` in the `waybetter` package. This module is the document side of the theme
|
|
4
|
+
// engine: types, the schema's defaults, sanitizing, import, panel overrides, legibility, font roles
|
|
5
|
+
// and stacks, and page backgrounds. Pure, so it runs in the page runtime, the settings previews and
|
|
6
|
+
// the tests. What a theme does to one site's page (its selectors, its palette) is the site's
|
|
7
|
+
// (themeRuntime.ts takes a site adapter).
|
|
8
|
+
//
|
|
9
|
+
// Safety: a theme is sanitized field by field before it becomes CSS. Colors are six-digit hex,
|
|
10
|
+
// numbers are clamped, enums are fixed, font names match a strict pattern, background images are
|
|
11
|
+
// `data:image/*;base64` only, and CSS is built from those values (never concatenated from raw
|
|
12
|
+
// strings). The single escape hatch is `css`, which is the same trust level as the owner's own
|
|
13
|
+
// Custom CSS and is shown as such when a theme is imported.
|
|
14
|
+
//
|
|
15
|
+
// Fonts: sites usually block remote and `data:` fonts (CSP `font-src`), so a theme may name installed
|
|
16
|
+
// system fonts, "Geist" / "Geist Mono" (bundled and registered from bytes: `FontFace(ArrayBuffer)` is
|
|
17
|
+
// not a fetch), or Google Fonts families the owner downloaded (`source: "google"`, fonts.ts).
|
|
18
|
+
import { FONT_ROLE_NAMES, MONO_FAMILIES, contrastRatio, hexToRgb, mix, relativeLuminance } from "./themeApi.js";
|
|
19
|
+
import { deriveColors } from "./themeTokens.js";
|
|
20
|
+
import { ACTION_NAMES, sanitizeComponents } from "./themeComponents.js";
|
|
21
|
+
import { sanitizeDecor } from "./themeDecor.js";
|
|
22
|
+
import { sanitizeThemeIcons } from "./themeIcons.js";
|
|
23
|
+
import { sanitizeVariants } from "./themeVariants.js";
|
|
24
|
+
export { FONT_ROLE_NAMES } from "./themeApi.js";
|
|
25
|
+
// ---------------------------------------------------------------------------------------------
|
|
26
|
+
// The schema's defaults: a theme that sets nothing changes nothing (the site keeps its own geometry;
|
|
27
|
+
// a site's structure CSS only emits rules where a theme differs from these).
|
|
28
|
+
export const BASE_SHAPE = { radius: 16, button: 9999, avatar: 'circle' };
|
|
29
|
+
export const INHERIT_ROLES = { interface: 'inherit', post: 'inherit', heading: 'inherit', numbers: 'inherit', mono: 'inherit', chrome: 'inherit', dialog: 'inherit' };
|
|
30
|
+
/** A font spec from the interface/post stacks alone (every other role inherits). */
|
|
31
|
+
export const withRoles = (font) => ({
|
|
32
|
+
...font,
|
|
33
|
+
roles: { ...INHERIT_ROLES, ...(font.family.length ? { interface: { family: font.family } } : {}), ...(font.tweet.length ? { post: { family: font.tweet } } : {}) },
|
|
34
|
+
});
|
|
35
|
+
export const BASE_FONT = withRoles({ family: [], tweet: [], scale: 1, weight: 400, bold: 700, tracking: 0, leading: 0 });
|
|
36
|
+
export const BASE_LAYOUT = { column: 600, sidebar: 350, density: 'default', cards: false, frame: false };
|
|
37
|
+
export const BASE_EFFECTS = { motion: 0, hover: 'none', shadow: 'none', crt: false };
|
|
38
|
+
/** The system UI stack themes put after their own sans-serif families. */
|
|
39
|
+
export const SYSTEM_SANS = ['Segoe UI', '-apple-system', 'BlinkMacSystemFont', 'system-ui', 'sans-serif'];
|
|
40
|
+
// ---------------------------------------------------------------------------------------------
|
|
41
|
+
// Colors and contrast (luminance and ratios: themeApi.ts)
|
|
42
|
+
export const isHex = (value) => typeof value === 'string' && /^#[0-9a-f]{6}$/i.test(value);
|
|
43
|
+
/** `#rrggbb` + alpha as `rgba()`. */
|
|
44
|
+
export const rgba = (hex, alpha) => `rgba(${hexToRgb(hex).join(', ')}, ${+alpha.toFixed(3)})`;
|
|
45
|
+
/** Moves `color` toward black or white until it reaches `ratio` against every surface. */
|
|
46
|
+
export function reach(color, surfaces, ratio) {
|
|
47
|
+
const worst = (c) => Math.min(...surfaces.map(s => contrastRatio(c, s)));
|
|
48
|
+
if (worst(color) >= ratio)
|
|
49
|
+
return color;
|
|
50
|
+
const lightSurfaces = surfaces.reduce((sum, s) => sum + relativeLuminance(s), 0) / surfaces.length > 0.18;
|
|
51
|
+
const pole = lightSurfaces ? '#000000' : '#ffffff';
|
|
52
|
+
for (let t = 0.04; t <= 1; t += 0.04) {
|
|
53
|
+
const next = mix(color, pole, t);
|
|
54
|
+
if (worst(next) >= ratio)
|
|
55
|
+
return next;
|
|
56
|
+
}
|
|
57
|
+
return pole;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Guarantees legibility for any palette (built-in, imported or overridden): text, secondary text
|
|
61
|
+
* and links reach WCAG AA (4.5:1) on every surface, including the page canvas, and the site's filled
|
|
62
|
+
* button keeps a readable label and stands out from the page (3:1).
|
|
63
|
+
*/
|
|
64
|
+
export function ensureContrast(colors) {
|
|
65
|
+
const surfaces = [colors.bg, colors.surface, colors.elevated, ...(colors.page ? [colors.page] : [])];
|
|
66
|
+
const out = { ...colors };
|
|
67
|
+
out.text = reach(colors.text, surfaces, 7);
|
|
68
|
+
out.muted = reach(colors.muted, surfaces, 4.5);
|
|
69
|
+
// Links live inside the columns, never on the page canvas.
|
|
70
|
+
out.accent = reach(colors.accent, [colors.bg, colors.surface, colors.elevated], 4.5);
|
|
71
|
+
const inverse = colors.inverse ?? out.text;
|
|
72
|
+
const onInverse = colors.onInverse ?? colors.bg;
|
|
73
|
+
out.inverse = reach(inverse, [colors.bg], 3);
|
|
74
|
+
out.onInverse = reach(onInverse, [out.inverse], 4.5);
|
|
75
|
+
// Action colors are icons and counts: non-text contrast on the column and on a raised row.
|
|
76
|
+
if (colors.actions)
|
|
77
|
+
out.actions = Object.fromEntries(Object.entries(colors.actions).map(([name, color]) => [name, reach(color, [colors.bg, colors.surface], 3)]));
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
// ---------------------------------------------------------------------------------------------
|
|
81
|
+
// Sanitizing
|
|
82
|
+
const GENERIC_FONTS = new Set(['system-ui', 'sans-serif', 'serif', 'monospace', 'cursive', 'ui-sans-serif', 'ui-serif', 'ui-monospace', 'ui-rounded', '-apple-system', 'BlinkMacSystemFont']);
|
|
83
|
+
const FONT_NAME = /^[A-Za-z0-9][A-Za-z0-9 .\-]{0,47}$/;
|
|
84
|
+
const DATA_IMAGE = /^data:image\/(png|jpeg|webp|gif|avif);base64,[A-Za-z0-9+/]+={0,2}$/;
|
|
85
|
+
export const MAX_IMAGE_CHARS = 1_100_000;
|
|
86
|
+
export const MAX_CSS_CHARS = 60_000;
|
|
87
|
+
const PATTERNS = ['dots', 'grid', 'diagonal', 'scanlines', 'paper', 'clouds'];
|
|
88
|
+
const obj = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
89
|
+
const num = (value, min, max, fallback) => typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;
|
|
90
|
+
const pick = (value, options, fallback) => options.includes(value) ? value : fallback;
|
|
91
|
+
const text = (value, max, fallback = '') => typeof value === 'string' ? value.replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, max) : fallback;
|
|
92
|
+
export function sanitizeFonts(value) {
|
|
93
|
+
if (!Array.isArray(value))
|
|
94
|
+
return [];
|
|
95
|
+
return value.filter((name) => typeof name === 'string' && (GENERIC_FONTS.has(name) || FONT_NAME.test(name))).slice(0, 8);
|
|
96
|
+
}
|
|
97
|
+
/** A theme needs its background, text and accent; any other role it leaves out is derived from the
|
|
98
|
+
* base structure (themeTokens.ts `deriveColors`), so every theme has the same proportions. */
|
|
99
|
+
function sanitizeColors(value) {
|
|
100
|
+
const source = obj(value);
|
|
101
|
+
if (!['bg', 'text', 'accent'].every(role => isHex(source[role])))
|
|
102
|
+
return null;
|
|
103
|
+
const named = Object.fromEntries(['bg', 'surface', 'elevated', 'border', 'text', 'muted', 'accent']
|
|
104
|
+
.filter(role => isHex(source[role])).map(role => [role, source[role].toLowerCase()]));
|
|
105
|
+
const colors = deriveColors(named);
|
|
106
|
+
for (const role of ['inverse', 'onInverse', 'page'])
|
|
107
|
+
if (isHex(source[role]))
|
|
108
|
+
colors[role] = source[role].toLowerCase();
|
|
109
|
+
const actions = obj(source.actions);
|
|
110
|
+
const named_actions = Object.fromEntries(ACTION_NAMES.filter(name => isHex(actions[name])).map(name => [name, actions[name].toLowerCase()]));
|
|
111
|
+
if (Object.keys(named_actions).length)
|
|
112
|
+
colors.actions = named_actions;
|
|
113
|
+
return colors;
|
|
114
|
+
}
|
|
115
|
+
export function sanitizeBackground(value) {
|
|
116
|
+
const source = obj(value);
|
|
117
|
+
if (source.kind === 'gradient' && isHex(source.from) && isHex(source.to)) {
|
|
118
|
+
return { kind: 'gradient', from: source.from.toLowerCase(), to: source.to.toLowerCase(), angle: Math.round(num(source.angle, 0, 360, 180)), ...(isHex(source.glow) ? { glow: source.glow.toLowerCase() } : {}) };
|
|
119
|
+
}
|
|
120
|
+
if (source.kind === 'pattern' && isHex(source.ink)) {
|
|
121
|
+
return { kind: 'pattern', pattern: pick(source.pattern, PATTERNS, 'dots'), ink: source.ink.toLowerCase(), opacity: num(source.opacity, 0, 0.6, 0.1), size: Math.round(num(source.size, 2, 400, 24)) };
|
|
122
|
+
}
|
|
123
|
+
if (source.kind === 'image' && typeof source.url === 'string' && source.url.length <= MAX_IMAGE_CHARS && DATA_IMAGE.test(source.url)) {
|
|
124
|
+
return { kind: 'image', url: source.url, fit: pick(source.fit, ['cover', 'tile'], 'cover'), dim: num(source.dim, 0.35, 0.95, 0.6) };
|
|
125
|
+
}
|
|
126
|
+
return { kind: 'none' };
|
|
127
|
+
}
|
|
128
|
+
/** Bounds of a role's own settings (the JSON Schema says the same; tested). */
|
|
129
|
+
export const ROLE_LIMITS = { weight: [100, 900], size: [0.75, 1.5], tracking: [-0.05, 0.1] };
|
|
130
|
+
/** One role from JSON: "inherit", or a stack with optional extras (clamped; unknown keys dropped). */
|
|
131
|
+
export function sanitizeFontRole(value, role) {
|
|
132
|
+
if (value === 'inherit' || value === null || value === undefined)
|
|
133
|
+
return 'inherit';
|
|
134
|
+
const source = obj(value);
|
|
135
|
+
if (!Object.keys(source).length)
|
|
136
|
+
return 'inherit';
|
|
137
|
+
const family = sanitizeFonts(source.family);
|
|
138
|
+
const out = { family };
|
|
139
|
+
if (source.source === 'google' && family.length && !GENERIC_FONTS.has(family[0]))
|
|
140
|
+
out.source = 'google';
|
|
141
|
+
if (role !== 'interface') {
|
|
142
|
+
if (typeof source.weight === 'number' && Number.isFinite(source.weight))
|
|
143
|
+
out.weight = Math.round(num(source.weight, ...ROLE_LIMITS.weight, 400));
|
|
144
|
+
if (typeof source.size === 'number' && Number.isFinite(source.size))
|
|
145
|
+
out.size = +num(source.size, ...ROLE_LIMITS.size, 1).toFixed(3);
|
|
146
|
+
if (typeof source.tracking === 'number' && Number.isFinite(source.tracking))
|
|
147
|
+
out.tracking = +num(source.tracking, ...ROLE_LIMITS.tracking, 0).toFixed(4);
|
|
148
|
+
if (source.tabular === true)
|
|
149
|
+
out.tabular = true;
|
|
150
|
+
}
|
|
151
|
+
// A role that changes nothing is the same as inheriting.
|
|
152
|
+
return Object.keys(out).length === 1 && !family.length ? 'inherit' : out;
|
|
153
|
+
}
|
|
154
|
+
const roleFamily = (role) => role === 'inherit' ? [] : role.family;
|
|
155
|
+
/** A role with a new family, keeping its other settings (and its Google source only for the same font). */
|
|
156
|
+
export function withFamily(role, family, source) {
|
|
157
|
+
const rest = role === 'inherit' ? {} : { ...role };
|
|
158
|
+
delete rest.source;
|
|
159
|
+
delete rest.family;
|
|
160
|
+
const next = { ...rest, family, ...(source && family.length ? { source } : {}) };
|
|
161
|
+
return Object.keys(next).length === 1 && !family.length ? 'inherit' : next;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The roles of a font group from JSON over `base`: `roles.<name>` wins; the legacy shorthand
|
|
165
|
+
* (`family` → interface, `tweet` → post) applies when that role is not named, so theme files and
|
|
166
|
+
* saved customizations from before font roles keep working.
|
|
167
|
+
*/
|
|
168
|
+
function sanitizeRoles(source, base) {
|
|
169
|
+
const named = obj(source.roles);
|
|
170
|
+
const roles = { ...base.roles };
|
|
171
|
+
for (const role of FONT_ROLE_NAMES)
|
|
172
|
+
if (role in named)
|
|
173
|
+
roles[role] = sanitizeFontRole(named[role], role);
|
|
174
|
+
if (!('interface' in named) && 'family' in source)
|
|
175
|
+
roles.interface = withFamily(base.roles.interface, sanitizeFonts(source.family), sameFirst(source.family, base.roles.interface));
|
|
176
|
+
if (!('post' in named) && 'tweet' in source)
|
|
177
|
+
roles.post = withFamily(base.roles.post, sanitizeFonts(source.tweet), sameFirst(source.tweet, base.roles.post));
|
|
178
|
+
return roles;
|
|
179
|
+
}
|
|
180
|
+
/** Keeps a role's Google source when the shorthand still names the same first family. */
|
|
181
|
+
function sameFirst(stack, role) {
|
|
182
|
+
return role !== 'inherit' && role.source === 'google' && Array.isArray(stack) && stack[0] === role.family[0] ? 'google' : undefined;
|
|
183
|
+
}
|
|
184
|
+
function sanitizeFont(value, base) {
|
|
185
|
+
const source = obj(value);
|
|
186
|
+
const roles = sanitizeRoles(source, base);
|
|
187
|
+
return {
|
|
188
|
+
family: roleFamily(roles.interface),
|
|
189
|
+
tweet: roleFamily(roles.post),
|
|
190
|
+
roles,
|
|
191
|
+
scale: num(source.scale, 0.85, 1.2, base.scale),
|
|
192
|
+
weight: Math.round(num(source.weight, 300, 500, base.weight)),
|
|
193
|
+
bold: Math.round(num(source.bold, 500, 800, base.bold)),
|
|
194
|
+
tracking: num(source.tracking, -0.03, 0.06, base.tracking),
|
|
195
|
+
leading: source.leading === 0 ? 0 : num(source.leading, 1.2, 1.8, base.leading),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const sanitizeShape = (value, base) => {
|
|
199
|
+
const source = obj(value);
|
|
200
|
+
return { radius: Math.round(num(source.radius, 0, 28, base.radius)), button: Math.round(num(source.button, 0, 9999, base.button)), avatar: pick(source.avatar, ['circle', 'rounded', 'square'], base.avatar) };
|
|
201
|
+
};
|
|
202
|
+
const sanitizeLayout = (value, base) => {
|
|
203
|
+
const source = obj(value);
|
|
204
|
+
return {
|
|
205
|
+
column: Math.round(num(source.column, 520, 820, base.column)), sidebar: Math.round(num(source.sidebar, 290, 400, base.sidebar)),
|
|
206
|
+
density: pick(source.density, ['compact', 'default', 'comfortable'], base.density),
|
|
207
|
+
cards: typeof source.cards === 'boolean' ? source.cards : base.cards, frame: typeof source.frame === 'boolean' ? source.frame : base.frame,
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
const sanitizeEffects = (value, base) => {
|
|
211
|
+
const source = obj(value);
|
|
212
|
+
return {
|
|
213
|
+
motion: Math.round(num(source.motion, 0, 400, base.motion)), hover: pick(source.hover, ['none', 'tint', 'lift', 'glow'], base.hover),
|
|
214
|
+
shadow: pick(source.shadow, ['none', 'soft', 'deep'], base.shadow), crt: typeof source.crt === 'boolean' ? source.crt : base.crt,
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
export const slug = (value) => value.toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'theme';
|
|
218
|
+
/** Any JSON to a complete, safe theme. Missing groups fall back to the schema's defaults. */
|
|
219
|
+
export function sanitizeSpec(input) {
|
|
220
|
+
const source = obj(input);
|
|
221
|
+
const name = text(source.name, 60) || 'Untitled theme';
|
|
222
|
+
const id = typeof source.id === 'string' && /^[a-z0-9-]{1,40}$/.test(source.id) ? source.id : slug(name);
|
|
223
|
+
return {
|
|
224
|
+
id, name, description: text(source.description, 200), author: text(source.author, 60), version: 1,
|
|
225
|
+
mode: pick(source.mode, ['light', 'dark', 'auto'], 'auto'),
|
|
226
|
+
colors: sanitizeColors(source.colors),
|
|
227
|
+
font: sanitizeFont(source.font, BASE_FONT),
|
|
228
|
+
shape: sanitizeShape(source.shape, BASE_SHAPE),
|
|
229
|
+
layout: sanitizeLayout(source.layout, BASE_LAYOUT),
|
|
230
|
+
background: sanitizeBackground(source.background),
|
|
231
|
+
effects: sanitizeEffects(source.effects, BASE_EFFECTS),
|
|
232
|
+
css: typeof source.css === 'string' ? source.css.slice(0, MAX_CSS_CHARS) : '',
|
|
233
|
+
...structureGroups(source),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** The optional structure groups of a theme, each present only when it says something valid. */
|
|
237
|
+
function structureGroups(source) {
|
|
238
|
+
const components = sanitizeComponents(source.components), variants = sanitizeVariants(source.variants);
|
|
239
|
+
const decor = sanitizeDecor(source.decor), icons = sanitizeThemeIcons(source.icons);
|
|
240
|
+
return { ...(components ? { components } : {}), ...(variants ? { variants } : {}), ...(decor ? { decor } : {}), ...(icons ? { icons } : {}) };
|
|
241
|
+
}
|
|
242
|
+
/** True when a theme carries any of the structure groups (components, variants, decoration, icons). */
|
|
243
|
+
export const hasStructure = (theme) => !!(theme.components || theme.variants || theme.decor || theme.icons);
|
|
244
|
+
/** Parses an imported file; errors are human-readable. */
|
|
245
|
+
export function parseThemeJson(json) {
|
|
246
|
+
let raw;
|
|
247
|
+
try {
|
|
248
|
+
raw = JSON.parse(json);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return { error: 'This file is not valid JSON.' };
|
|
252
|
+
}
|
|
253
|
+
const source = obj(raw);
|
|
254
|
+
if (typeof source.name !== 'string' || !source.name.trim())
|
|
255
|
+
return { error: 'A theme needs a "name".' };
|
|
256
|
+
const theme = sanitizeSpec(raw);
|
|
257
|
+
const warnings = [];
|
|
258
|
+
if (source.colors !== undefined && source.colors !== null && !theme.colors)
|
|
259
|
+
warnings.push('Colors were ignored: bg, text and accent must be #rrggbb (the other roles are derived when missing).');
|
|
260
|
+
if (theme.css)
|
|
261
|
+
warnings.push('Contains custom CSS, which runs inside the site like your own Custom CSS.');
|
|
262
|
+
if (source.background && theme.background.kind === 'none' && obj(source.background).kind !== 'none')
|
|
263
|
+
warnings.push('The background was ignored (images must be data:image/png, jpeg, webp, gif or avif, up to about 800 KB).');
|
|
264
|
+
if (theme.colors) {
|
|
265
|
+
const fixed = ensureContrast(theme.colors);
|
|
266
|
+
if (fixed.text !== theme.colors.text || fixed.muted !== theme.colors.muted || fixed.accent !== theme.colors.accent)
|
|
267
|
+
warnings.push('Some text colors were adjusted to stay readable.');
|
|
268
|
+
}
|
|
269
|
+
return { theme, warnings };
|
|
270
|
+
}
|
|
271
|
+
/** Applies panel overrides (and an accent) on top of a theme. Colors are then made legible. */
|
|
272
|
+
export function applyOverrides(theme, overrides = {}, accent = null) {
|
|
273
|
+
const o = obj(overrides);
|
|
274
|
+
let colors = theme.colors ? { ...theme.colors, ...Object.fromEntries(Object.entries(obj(o.colors)).filter(([, v]) => isHex(v))) } : null;
|
|
275
|
+
if (colors && accent)
|
|
276
|
+
colors = { ...colors, accent, ...(theme.colors?.inverse === theme.colors?.accent ? { inverse: accent } : {}) };
|
|
277
|
+
return {
|
|
278
|
+
...theme,
|
|
279
|
+
colors: colors ? ensureContrast(colors) : null,
|
|
280
|
+
// Missing keys fall back to the theme's own; the legacy `family`/`tweet` shorthand still overrides.
|
|
281
|
+
font: o.font ? sanitizeFont(o.font, theme.font) : theme.font,
|
|
282
|
+
shape: o.shape ? sanitizeShape({ ...theme.shape, ...o.shape }, theme.shape) : theme.shape,
|
|
283
|
+
layout: o.layout ? sanitizeLayout({ ...theme.layout, ...o.layout }, theme.layout) : theme.layout,
|
|
284
|
+
background: o.background ? sanitizeBackground(o.background) : theme.background,
|
|
285
|
+
effects: o.effects ? sanitizeEffects({ ...theme.effects, ...o.effects }, theme.effects) : theme.effects,
|
|
286
|
+
css: o.css && typeof o.css.text === 'string' ? o.css.text.slice(0, MAX_CSS_CHARS) : theme.css,
|
|
287
|
+
...variantOverrides(theme, o.variants),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/** A theme's variants with the owner's per-axis say on top (the raw merge goes through the sanitizer, so the site's own option removes the axis). */
|
|
291
|
+
function variantOverrides(theme, overrides) {
|
|
292
|
+
if (!overrides || typeof overrides !== 'object')
|
|
293
|
+
return {};
|
|
294
|
+
const variants = sanitizeVariants({ ...theme.variants, ...overrides });
|
|
295
|
+
return { variants: variants ?? undefined };
|
|
296
|
+
}
|
|
297
|
+
/** True when a theme changes nothing about the site (the site's own look without overrides). */
|
|
298
|
+
export function isNeutral(theme) {
|
|
299
|
+
return !theme.colors && JSON.stringify([theme.font, theme.shape, theme.layout, theme.effects]) === JSON.stringify([BASE_FONT, BASE_SHAPE, BASE_LAYOUT, BASE_EFFECTS])
|
|
300
|
+
&& theme.background.kind === 'none' && !theme.css && !hasStructure(theme);
|
|
301
|
+
}
|
|
302
|
+
// ---------------------------------------------------------------------------------------------
|
|
303
|
+
// Fonts
|
|
304
|
+
/** Bundled families and the names they are registered under (shell: @fontsource; site pages: FontFace, fonts.ts). */
|
|
305
|
+
export const BUNDLED_FONTS = { 'Geist': 'Geist Variable', 'Geist Mono': 'Geist Mono Variable' };
|
|
306
|
+
export function fontStack(names) {
|
|
307
|
+
const out = [];
|
|
308
|
+
for (const name of names) {
|
|
309
|
+
const real = BUNDLED_FONTS[name];
|
|
310
|
+
if (real)
|
|
311
|
+
out.push(`"${real}"`);
|
|
312
|
+
out.push(GENERIC_FONTS.has(name) ? name : `"${name}"`);
|
|
313
|
+
}
|
|
314
|
+
// Emoji and CJK keep working whatever the theme names.
|
|
315
|
+
return [...new Set(out), '"Segoe UI Emoji"', '"Apple Color Emoji"', 'sans-serif'].join(', ');
|
|
316
|
+
}
|
|
317
|
+
/** Roles that are drawn inside the site's page (chrome is wayBetter's own window). */
|
|
318
|
+
export const PAGE_ROLES = ['interface', 'post', 'heading', 'numbers', 'mono', 'dialog'];
|
|
319
|
+
const rolesUsing = (theme, roles) => roles.flatMap(role => roleFamily(theme.font.roles[role]));
|
|
320
|
+
/** Bundled families (Geist, Geist Mono) a theme names in the site's page. */
|
|
321
|
+
export const bundledFontsUsed = (theme, roles = PAGE_ROLES) => {
|
|
322
|
+
const names = new Set(rolesUsing(theme, roles));
|
|
323
|
+
return Object.keys(BUNDLED_FONTS).filter(name => names.has(name));
|
|
324
|
+
};
|
|
325
|
+
/** Google Fonts families a theme uses (`source: "google"`), for the given roles. */
|
|
326
|
+
export function googleFontsUsed(theme, roles = FONT_ROLE_NAMES) {
|
|
327
|
+
const out = new Set();
|
|
328
|
+
for (const role of roles) {
|
|
329
|
+
const spec = theme.font.roles[role];
|
|
330
|
+
if (spec !== 'inherit' && spec.source === 'google' && spec.family[0])
|
|
331
|
+
out.add(spec.family[0]);
|
|
332
|
+
}
|
|
333
|
+
return [...out];
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Every role resolved. `site` is the stack an inheriting interface uses: the site's own font in its
|
|
337
|
+
* page, wayBetter's own UI font in the shell. Every other role inherits the interface, except that an
|
|
338
|
+
* inheriting `mono` keeps a monospaced stack (`mono`: themeApi.ts `MONO_FAMILIES`, code rarely suits
|
|
339
|
+
* an interface font).
|
|
340
|
+
*/
|
|
341
|
+
export function resolveFontRoles(font, site, mono = fontStack(MONO_FAMILIES)) {
|
|
342
|
+
const base = font.roles.interface;
|
|
343
|
+
const baseFamily = roleFamily(base);
|
|
344
|
+
const root = {
|
|
345
|
+
stack: baseFamily.length ? fontStack(baseFamily) : site, family: baseFamily, inherited: !baseFamily.length,
|
|
346
|
+
google: base !== 'inherit' && base.source === 'google' ? baseFamily[0] ?? null : null,
|
|
347
|
+
weight: null, size: 1, tracking: null, tabular: false,
|
|
348
|
+
};
|
|
349
|
+
const out = { interface: root };
|
|
350
|
+
for (const role of FONT_ROLE_NAMES) {
|
|
351
|
+
if (role === 'interface')
|
|
352
|
+
continue;
|
|
353
|
+
const spec = font.roles[role];
|
|
354
|
+
const own = roleFamily(spec);
|
|
355
|
+
const from = own.length
|
|
356
|
+
? { stack: fontStack(own), family: own, inherited: false, google: spec !== 'inherit' && spec.source === 'google' ? own[0] : null }
|
|
357
|
+
: role === 'mono' ? { stack: mono, family: [], inherited: true, google: null }
|
|
358
|
+
: { stack: root.stack, family: root.family, inherited: true, google: root.google };
|
|
359
|
+
out[role] = {
|
|
360
|
+
...from,
|
|
361
|
+
weight: spec === 'inherit' ? null : spec.weight ?? null,
|
|
362
|
+
size: spec === 'inherit' ? 1 : spec.size ?? 1,
|
|
363
|
+
tracking: spec === 'inherit' ? null : spec.tracking ?? null,
|
|
364
|
+
tabular: spec !== 'inherit' && !!spec.tabular,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
return out;
|
|
368
|
+
}
|
|
369
|
+
/** Font choices offered by the panel; any installed family can also be named in theme JSON. */
|
|
370
|
+
export const FONT_CHOICES = [
|
|
371
|
+
{ id: 'geist', label: 'Geist', family: ['Geist', ...SYSTEM_SANS] },
|
|
372
|
+
{ id: 'geist-mono', label: 'Geist Mono', family: ['Geist Mono', 'Cascadia Mono', 'Consolas', 'Menlo', 'ui-monospace', 'monospace'] },
|
|
373
|
+
{ id: 'system', label: 'System UI', key: 'wb.fonts.choice.system', family: SYSTEM_SANS },
|
|
374
|
+
{ id: 'helvetica', label: 'Helvetica / Arial', family: ['Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'] },
|
|
375
|
+
{ id: 'humanist', label: 'Humanist (Segoe, Avenir)', key: 'wb.fonts.choice.humanist', family: ['Avenir Next', 'Segoe UI Variable Text', 'Segoe UI', 'sans-serif'] },
|
|
376
|
+
{ id: 'rounded', label: 'Rounded', key: 'wb.fonts.choice.rounded', family: ['ui-rounded', 'SF Pro Rounded', 'Nunito', 'Bahnschrift', 'Segoe UI', 'sans-serif'] },
|
|
377
|
+
{ id: 'serif', label: 'Serif (Charter, Georgia)', key: 'wb.fonts.choice.serif', family: ['Charter', 'Iowan Old Style', 'Georgia', 'Cambria', 'serif'] },
|
|
378
|
+
{ id: 'mono', label: 'System mono', key: 'wb.fonts.choice.mono', family: ['Cascadia Mono', 'SF Mono', 'Consolas', 'Menlo', 'ui-monospace', 'monospace'] },
|
|
379
|
+
];
|
|
380
|
+
/** The choice whose stack is exactly `family` in `choices`, or `custom`. */
|
|
381
|
+
export const fontChoiceOf = (family, choices = FONT_CHOICES) => choices.find(choice => choice.family.join('|') === family.join('|'))?.id ?? 'custom';
|
|
382
|
+
// ---------------------------------------------------------------------------------------------
|
|
383
|
+
// Backgrounds
|
|
384
|
+
const svgUrl = (svg) => `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
385
|
+
/**
|
|
386
|
+
* The site's page background. A site's `<html>` and `<body>` are often one viewport tall while the
|
|
387
|
+
* content overflows them (X's timeline), so a background painted on both had `<body>` paint its own
|
|
388
|
+
* copy over its box, which scrolls away: after a partial scroll the gradient/glow showed only below a hard seam. Now
|
|
389
|
+
* `<html>` holds the plain page color, `<body>` is transparent (beating, by specificity, the
|
|
390
|
+
* palette's `html,body{background-color}` and its `[style^="background-color: …"]` remap of the
|
|
391
|
+
* inline color X puts on `<body>`) and every layer sits on one viewport-sized fixed
|
|
392
|
+
* layer behind all of the page (`z-index:-1` in the root stacking context), with no `fixed` attachments.
|
|
393
|
+
*/
|
|
394
|
+
export function pageBackgroundCss(background, page) {
|
|
395
|
+
const out = [`html:root{background:${page} !important}`, `html:root body{background:transparent !important}`];
|
|
396
|
+
if (background.kind !== 'none') {
|
|
397
|
+
out.push(`html:root::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;contain:strict;background:${backgroundLayers(background, page, false)}}`);
|
|
398
|
+
}
|
|
399
|
+
return out;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* CSS `background` layers for the page canvas (behind the site's columns). `attach` keeps the `fixed`
|
|
403
|
+
* attachments for a box that scrolls (the Theme Builder preview); the site's page gets the layers without
|
|
404
|
+
* them, on one viewport-sized fixed layer (`pageBackgroundCss`).
|
|
405
|
+
*/
|
|
406
|
+
export function backgroundLayers(background, page, attach = true) {
|
|
407
|
+
const fixed = attach ? ' fixed' : '';
|
|
408
|
+
switch (background.kind) {
|
|
409
|
+
case 'gradient': {
|
|
410
|
+
const glow = background.glow ? `radial-gradient(1200px 600px at 50% -10%, ${rgba(background.glow, 0.55)}, transparent 70%), ` : '';
|
|
411
|
+
return `${glow}linear-gradient(${background.angle}deg, ${background.from}, ${background.to})${fixed}, ${page}`;
|
|
412
|
+
}
|
|
413
|
+
case 'pattern': {
|
|
414
|
+
const { ink, opacity: a, size: s } = background;
|
|
415
|
+
const c = rgba(ink, a);
|
|
416
|
+
switch (background.pattern) {
|
|
417
|
+
case 'dots': return `radial-gradient(${c} 1px, transparent 1.6px) 0 0 / ${s}px ${s}px, ${page}`;
|
|
418
|
+
case 'grid': return `linear-gradient(${c} 1px, transparent 1px) 0 0 / ${s}px ${s}px, linear-gradient(90deg, ${c} 1px, transparent 1px) 0 0 / ${s}px ${s}px, ${page}`;
|
|
419
|
+
case 'diagonal': return `repeating-linear-gradient(135deg, ${c} 0 1px, transparent 1px ${s}px), ${page}`;
|
|
420
|
+
case 'scanlines': return `repeating-linear-gradient(0deg, ${c} 0 1px, transparent 1px ${s}px), radial-gradient(ellipse at 50% 40%, ${rgba(ink, a * 1.6)}, transparent 70%)${fixed}, ${page}`;
|
|
421
|
+
case 'paper': return `${svgUrl(`<svg xmlns="http://www.w3.org/2000/svg" width="${s}" height="${s}"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency=".85" numOctaves="3" stitchTiles="stitch"/><feColorMatrix values="0 0 0 0 ${hexToRgb(ink).map(v => (v / 255).toFixed(3)).join(' 0 0 0 0 ')} 0 0 0 ${(a * 2.2).toFixed(3)} 0"/></filter><rect width="100%" height="100%" filter="url(#n)"/></svg>`)} 0 0 / ${s}px ${s}px, ${page}`;
|
|
422
|
+
case 'clouds': return [
|
|
423
|
+
`radial-gradient(${s}px ${Math.round(s * 0.42)}px at 12% 22%, ${c}, transparent 70%)${fixed}`,
|
|
424
|
+
`radial-gradient(${Math.round(s * 1.3)}px ${Math.round(s * 0.5)}px at 86% 12%, ${c}, transparent 70%)${fixed}`,
|
|
425
|
+
`radial-gradient(${Math.round(s * 1.1)}px ${Math.round(s * 0.4)}px at 78% 70%, ${c}, transparent 70%)${fixed}`,
|
|
426
|
+
`radial-gradient(${Math.round(s * 0.9)}px ${Math.round(s * 0.36)}px at 18% 82%, ${c}, transparent 70%)${fixed}`,
|
|
427
|
+
`linear-gradient(180deg, ${mix(page, '#ffffff', 0.25)}, ${page} 60%)${fixed}`, page,
|
|
428
|
+
].join(', ');
|
|
429
|
+
}
|
|
430
|
+
return page;
|
|
431
|
+
}
|
|
432
|
+
case 'image': {
|
|
433
|
+
const scrim = `linear-gradient(${rgba(page, background.dim)}, ${rgba(page, background.dim)})${fixed}`;
|
|
434
|
+
return background.fit === 'tile' ? `${scrim}, url("${background.url}") 0 0 / auto repeat, ${page}` : `${scrim}, url("${background.url}") center / cover no-repeat${fixed}, ${page}`;
|
|
435
|
+
}
|
|
436
|
+
default: return page;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=themeSpec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeSpec.js","sourceRoot":"","sources":["../../../runtime/src/themeSpec.ts"],"names":[],"mappings":"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","sourcesContent":["// Theme documents: a wayBetter theme is a JSON document (like a BetterDiscord theme, but data instead\n// of a stylesheet) describing colors, type, shape, density, layout, background and motion; the format\n// is `theme.schema.json` in the `waybetter` package. This module is the document side of the theme\n// engine: types, the schema's defaults, sanitizing, import, panel overrides, legibility, font roles\n// and stacks, and page backgrounds. Pure, so it runs in the page runtime, the settings previews and\n// the tests. What a theme does to one site's page (its selectors, its palette) is the site's\n// (themeRuntime.ts takes a site adapter).\n//\n// Safety: a theme is sanitized field by field before it becomes CSS. Colors are six-digit hex,\n// numbers are clamped, enums are fixed, font names match a strict pattern, background images are\n// `data:image/*;base64` only, and CSS is built from those values (never concatenated from raw\n// strings). The single escape hatch is `css`, which is the same trust level as the owner's own\n// Custom CSS and is shown as such when a theme is imported.\n//\n// Fonts: sites usually block remote and `data:` fonts (CSP `font-src`), so a theme may name installed\n// system fonts, \"Geist\" / \"Geist Mono\" (bundled and registered from bytes: `FontFace(ArrayBuffer)` is\n// not a fetch), or Google Fonts families the owner downloaded (`source: \"google\"`, fonts.ts).\n\nimport { FONT_ROLE_NAMES, MONO_FAMILIES, contrastRatio, hexToRgb, mix, relativeLuminance, type FontRoleName } from './themeApi.ts';\nimport { deriveColors } from './themeTokens.ts';\nimport { ACTION_NAMES, sanitizeComponents, type ActionColors, type ThemeComponents } from './themeComponents.ts';\nimport { sanitizeDecor, type ThemeDecor } from './themeDecor.ts';\nimport { sanitizeThemeIcons, type ThemeIcons } from './themeIcons.ts';\nimport { sanitizeVariants, type ThemeVariants } from './themeVariants.ts';\n\nexport { FONT_ROLE_NAMES } from './themeApi.ts';\nexport type { FontRoleName } from './themeApi.ts';\n\nexport type Role = 'bg' | 'surface' | 'elevated' | 'border' | 'text' | 'muted' | 'accent';\n/** The site's filled primary button (X: \"Post\"/\"Follow\"): an `inverse` background with an `onInverse` label. */\nexport type ButtonRole = 'inverse' | 'onInverse';\nexport type Palette = Record<Role, string> & Partial<Record<ButtonRole, string>>;\n/**\n * `page` is the canvas behind the site's columns (defaults to `bg`); the columns stay on `bg`.\n * `actions` are the colors of a set action (liked, reposted, bookmarked) and of the reply hover;\n * absent, the site's own action colors stay.\n */\nexport type ThemeColors = Palette & { page?: string; actions?: ActionColors };\n\nexport type Density = 'compact' | 'default' | 'comfortable';\nexport type AvatarShape = 'circle' | 'rounded' | 'square';\nexport type Pattern = 'dots' | 'grid' | 'diagonal' | 'scanlines' | 'paper' | 'clouds';\nexport type Background =\n | { kind: 'none' }\n | { kind: 'gradient'; from: string; to: string; angle: number; glow?: string }\n | { kind: 'pattern'; pattern: Pattern; ink: string; opacity: number; size: number }\n | { kind: 'image'; url: string; fit: 'cover' | 'tile'; dim: number };\n\n/**\n * One role's font (roles: themeApi.ts `FONT_ROLE_NAMES`). `family` is a font stack; empty keeps the\n * inherited family and only changes the rest. `source: \"google\"` says the first family comes from\n * Google Fonts (downloaded once, asked first when a theme is imported, loaded from bytes). `weight`\n * (100-900), `size` (0.75-1.5, relative to the site's size), `tracking` (em) and `tabular` figures\n * are optional; the interface role takes only `family`/`source` (its size, weights and tracking are\n * `scale`, `weight`, `bold` and `tracking` of the whole theme).\n */\nexport type FontRoleSpec = { family: string[]; source?: 'google'; weight?: number; size?: number; tracking?: number; tabular?: boolean };\nexport type FontRole = 'inherit' | FontRoleSpec;\nexport type FontRoles = Record<FontRoleName, FontRole>;\n\nexport type FontSpec = {\n /**\n * Interface font stack (replaces the site's own font everywhere). Empty keeps the site's.\n * Shorthand kept for older theme files and older apps: it always equals `roles.interface`'s family.\n */\n family: string[];\n /** Post text stack (e.g. a serif reading face); shorthand of `roles.post`'s family. */\n tweet: string[];\n /** Which font goes where. Every role inherits from `interface` unless it says otherwise. */\n roles: FontRoles;\n /** Multiplies the site's pixel font sizes (0.85-1.2). */\n scale: number;\n /** Regular weight (300-500) and bold weight (500-800) that replace the site's 400/700. */\n weight: number;\n bold: number;\n /** Letter spacing in em (-0.03-0.06). */\n tracking: number;\n /** Line height multiplier for post text (0 keeps the site's). */\n leading: number;\n};\nexport type ShapeSpec = { radius: number; button: number; avatar: AvatarShape };\nexport type LayoutSpec = { column: number; sidebar: number; density: Density; cards: boolean; frame: boolean };\nexport type EffectsSpec = { motion: number; hover: 'none' | 'tint' | 'lift' | 'glow'; shadow: 'none' | 'soft' | 'deep'; crt: boolean };\n\nexport type ThemeSpec = {\n id: string;\n name: string;\n description: string;\n author: string;\n version: 1;\n /** Which way the theme leans; used for scrollbars/form controls and previews. */\n mode: 'light' | 'dark' | 'auto';\n /** null keeps the site's own colors (X: Default / Dim / Lights out). */\n colors: ThemeColors | null;\n font: FontSpec;\n shape: ShapeSpec;\n layout: LayoutSpec;\n background: Background;\n effects: EffectsSpec;\n /** Free-form CSS appended last (owner-trusted, like Custom CSS). */\n css: string;\n // The groups below are optional and additive: a theme without them is drawn exactly as before,\n // and an app that does not know them drops them and draws the rest.\n /** How each piece of the site is built (themeComponents.ts). */\n components?: ThemeComponents;\n /** Structural choices: where the navigation sits, how a post is laid out (themeVariants.ts). */\n variants?: ThemeVariants;\n /** Lines, bars, focus ring, selection, scrollbars, motion (themeDecor.ts). */\n decor?: ThemeDecor;\n /** Which icon pack the theme suggests, with small adjustments (themeIcons.ts). */\n icons?: ThemeIcons;\n};\n\n/** Panel overrides: any group may be partially overridden on top of the selected theme. */\nexport type ThemeOverrides = {\n colors?: Partial<ThemeColors>;\n /** `roles` is merged role by role (a named role replaces that role only). */\n font?: Partial<Omit<FontSpec, 'roles'>> & { roles?: Partial<FontRoles> };\n shape?: Partial<ShapeSpec>;\n layout?: Partial<LayoutSpec>;\n background?: Background;\n effects?: Partial<EffectsSpec>;\n /** The theme's own CSS as edited in the panel (replaces the theme document's `css`). */\n css?: { text: string };\n /** The owner's say on a theme's layout suggestions, axis by axis (an axis set to the site's own option switches it off). */\n variants?: Record<string, string>;\n};\n\n// ---------------------------------------------------------------------------------------------\n// The schema's defaults: a theme that sets nothing changes nothing (the site keeps its own geometry;\n// a site's structure CSS only emits rules where a theme differs from these).\n\nexport const BASE_SHAPE: ShapeSpec = { radius: 16, button: 9999, avatar: 'circle' };\nexport const INHERIT_ROLES: FontRoles = { interface: 'inherit', post: 'inherit', heading: 'inherit', numbers: 'inherit', mono: 'inherit', chrome: 'inherit', dialog: 'inherit' };\n/** A font spec from the interface/post stacks alone (every other role inherits). */\nexport const withRoles = (font: Omit<FontSpec, 'roles'>): FontSpec => ({\n ...font,\n roles: { ...INHERIT_ROLES, ...(font.family.length ? { interface: { family: font.family } } : {}), ...(font.tweet.length ? { post: { family: font.tweet } } : {}) },\n});\nexport const BASE_FONT: FontSpec = withRoles({ family: [], tweet: [], scale: 1, weight: 400, bold: 700, tracking: 0, leading: 0 });\nexport const BASE_LAYOUT: LayoutSpec = { column: 600, sidebar: 350, density: 'default', cards: false, frame: false };\nexport const BASE_EFFECTS: EffectsSpec = { motion: 0, hover: 'none', shadow: 'none', crt: false };\n\n/** The system UI stack themes put after their own sans-serif families. */\nexport const SYSTEM_SANS = ['Segoe UI', '-apple-system', 'BlinkMacSystemFont', 'system-ui', 'sans-serif'];\n\n// ---------------------------------------------------------------------------------------------\n// Colors and contrast (luminance and ratios: themeApi.ts)\n\nexport const isHex = (value: unknown): value is string => typeof value === 'string' && /^#[0-9a-f]{6}$/i.test(value);\n/** `#rrggbb` + alpha as `rgba()`. */\nexport const rgba = (hex: string, alpha: number) => `rgba(${hexToRgb(hex).join(', ')}, ${+alpha.toFixed(3)})`;\n\n/** Moves `color` toward black or white until it reaches `ratio` against every surface. */\nexport function reach(color: string, surfaces: string[], ratio: number): string {\n const worst = (c: string) => Math.min(...surfaces.map(s => contrastRatio(c, s)));\n if (worst(color) >= ratio) return color;\n const lightSurfaces = surfaces.reduce((sum, s) => sum + relativeLuminance(s), 0) / surfaces.length > 0.18;\n const pole = lightSurfaces ? '#000000' : '#ffffff';\n for (let t = 0.04; t <= 1; t += 0.04) {\n const next = mix(color, pole, t);\n if (worst(next) >= ratio) return next;\n }\n return pole;\n}\n\n/**\n * Guarantees legibility for any palette (built-in, imported or overridden): text, secondary text\n * and links reach WCAG AA (4.5:1) on every surface, including the page canvas, and the site's filled\n * button keeps a readable label and stands out from the page (3:1).\n */\nexport function ensureContrast<T extends Palette & { page?: string; actions?: ActionColors }>(colors: T): T {\n const surfaces = [colors.bg, colors.surface, colors.elevated, ...(colors.page ? [colors.page] : [])];\n const out = { ...colors };\n out.text = reach(colors.text, surfaces, 7);\n out.muted = reach(colors.muted, surfaces, 4.5);\n // Links live inside the columns, never on the page canvas.\n out.accent = reach(colors.accent, [colors.bg, colors.surface, colors.elevated], 4.5);\n const inverse = colors.inverse ?? out.text;\n const onInverse = colors.onInverse ?? colors.bg;\n out.inverse = reach(inverse, [colors.bg], 3);\n out.onInverse = reach(onInverse, [out.inverse], 4.5);\n // Action colors are icons and counts: non-text contrast on the column and on a raised row.\n if (colors.actions) out.actions = Object.fromEntries(Object.entries(colors.actions).map(([name, color]) => [name, reach(color as string, [colors.bg, colors.surface], 3)]));\n return out;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Sanitizing\n\nconst GENERIC_FONTS = new Set(['system-ui', 'sans-serif', 'serif', 'monospace', 'cursive', 'ui-sans-serif', 'ui-serif', 'ui-monospace', 'ui-rounded', '-apple-system', 'BlinkMacSystemFont']);\nconst FONT_NAME = /^[A-Za-z0-9][A-Za-z0-9 .\\-]{0,47}$/;\nconst DATA_IMAGE = /^data:image\\/(png|jpeg|webp|gif|avif);base64,[A-Za-z0-9+/]+={0,2}$/;\nexport const MAX_IMAGE_CHARS = 1_100_000;\nexport const MAX_CSS_CHARS = 60_000;\nconst PATTERNS: Pattern[] = ['dots', 'grid', 'diagonal', 'scanlines', 'paper', 'clouds'];\n\nconst obj = (value: unknown): Record<string, unknown> => value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};\nconst num = (value: unknown, min: number, max: number, fallback: number) => typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;\nconst pick = <T extends string>(value: unknown, options: readonly T[], fallback: T): T => options.includes(value as T) ? value as T : fallback;\nconst text = (value: unknown, max: number, fallback = '') => typeof value === 'string' ? value.replace(/[\\u0000-\\u001f\\u007f]/g, '').trim().slice(0, max) : fallback;\n\nexport function sanitizeFonts(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((name): name is string => typeof name === 'string' && (GENERIC_FONTS.has(name) || FONT_NAME.test(name))).slice(0, 8);\n}\n\n/** A theme needs its background, text and accent; any other role it leaves out is derived from the\n * base structure (themeTokens.ts `deriveColors`), so every theme has the same proportions. */\nfunction sanitizeColors(value: unknown): ThemeColors | null {\n const source = obj(value);\n if (!(['bg', 'text', 'accent'] as const).every(role => isHex(source[role]))) return null;\n const named = Object.fromEntries((['bg', 'surface', 'elevated', 'border', 'text', 'muted', 'accent'] as Role[])\n .filter(role => isHex(source[role])).map(role => [role, (source[role] as string).toLowerCase()]));\n const colors = deriveColors(named as { bg: string; text: string; accent: string }) as ThemeColors;\n for (const role of ['inverse', 'onInverse', 'page'] as const) if (isHex(source[role])) colors[role] = (source[role] as string).toLowerCase();\n const actions = obj(source.actions);\n const named_actions = Object.fromEntries(ACTION_NAMES.filter(name => isHex(actions[name])).map(name => [name, (actions[name] as string).toLowerCase()]));\n if (Object.keys(named_actions).length) colors.actions = named_actions;\n return colors;\n}\n\nexport function sanitizeBackground(value: unknown): Background {\n const source = obj(value);\n if (source.kind === 'gradient' && isHex(source.from) && isHex(source.to)) {\n return { kind: 'gradient', from: source.from.toLowerCase(), to: source.to.toLowerCase(), angle: Math.round(num(source.angle, 0, 360, 180)), ...(isHex(source.glow) ? { glow: source.glow.toLowerCase() } : {}) };\n }\n if (source.kind === 'pattern' && isHex(source.ink)) {\n return { kind: 'pattern', pattern: pick(source.pattern, PATTERNS, 'dots'), ink: source.ink.toLowerCase(), opacity: num(source.opacity, 0, 0.6, 0.1), size: Math.round(num(source.size, 2, 400, 24)) };\n }\n if (source.kind === 'image' && typeof source.url === 'string' && source.url.length <= MAX_IMAGE_CHARS && DATA_IMAGE.test(source.url)) {\n return { kind: 'image', url: source.url, fit: pick(source.fit, ['cover', 'tile'] as const, 'cover'), dim: num(source.dim, 0.35, 0.95, 0.6) };\n }\n return { kind: 'none' };\n}\n\n/** Bounds of a role's own settings (the JSON Schema says the same; tested). */\nexport const ROLE_LIMITS = { weight: [100, 900], size: [0.75, 1.5], tracking: [-0.05, 0.1] } as const;\n\n/** One role from JSON: \"inherit\", or a stack with optional extras (clamped; unknown keys dropped). */\nexport function sanitizeFontRole(value: unknown, role: FontRoleName): FontRole {\n if (value === 'inherit' || value === null || value === undefined) return 'inherit';\n const source = obj(value);\n if (!Object.keys(source).length) return 'inherit';\n const family = sanitizeFonts(source.family);\n const out: FontRoleSpec = { family };\n if (source.source === 'google' && family.length && !GENERIC_FONTS.has(family[0]!)) out.source = 'google';\n if (role !== 'interface') {\n if (typeof source.weight === 'number' && Number.isFinite(source.weight)) out.weight = Math.round(num(source.weight, ...ROLE_LIMITS.weight, 400));\n if (typeof source.size === 'number' && Number.isFinite(source.size)) out.size = +num(source.size, ...ROLE_LIMITS.size, 1).toFixed(3);\n if (typeof source.tracking === 'number' && Number.isFinite(source.tracking)) out.tracking = +num(source.tracking, ...ROLE_LIMITS.tracking, 0).toFixed(4);\n if (source.tabular === true) out.tabular = true;\n }\n // A role that changes nothing is the same as inheriting.\n return Object.keys(out).length === 1 && !family.length ? 'inherit' : out;\n}\n\nconst roleFamily = (role: FontRole): string[] => role === 'inherit' ? [] : role.family;\n/** A role with a new family, keeping its other settings (and its Google source only for the same font). */\nexport function withFamily(role: FontRole, family: string[], source?: 'google'): FontRole {\n const rest = role === 'inherit' ? {} : { ...role };\n delete (rest as Partial<FontRoleSpec>).source;\n delete (rest as Partial<FontRoleSpec>).family;\n const next: FontRoleSpec = { ...rest, family, ...(source && family.length ? { source } : {}) };\n return Object.keys(next).length === 1 && !family.length ? 'inherit' : next;\n}\n\n/**\n * The roles of a font group from JSON over `base`: `roles.<name>` wins; the legacy shorthand\n * (`family` → interface, `tweet` → post) applies when that role is not named, so theme files and\n * saved customizations from before font roles keep working.\n */\nfunction sanitizeRoles(source: Record<string, unknown>, base: FontSpec): FontRoles {\n const named = obj(source.roles);\n const roles = { ...base.roles };\n for (const role of FONT_ROLE_NAMES) if (role in named) roles[role] = sanitizeFontRole(named[role], role);\n if (!('interface' in named) && 'family' in source) roles.interface = withFamily(base.roles.interface, sanitizeFonts(source.family), sameFirst(source.family, base.roles.interface));\n if (!('post' in named) && 'tweet' in source) roles.post = withFamily(base.roles.post, sanitizeFonts(source.tweet), sameFirst(source.tweet, base.roles.post));\n return roles;\n}\n/** Keeps a role's Google source when the shorthand still names the same first family. */\nfunction sameFirst(stack: unknown, role: FontRole): 'google' | undefined {\n return role !== 'inherit' && role.source === 'google' && Array.isArray(stack) && stack[0] === role.family[0] ? 'google' : undefined;\n}\n\nfunction sanitizeFont(value: unknown, base: FontSpec): FontSpec {\n const source = obj(value);\n const roles = sanitizeRoles(source, base);\n return {\n family: roleFamily(roles.interface),\n tweet: roleFamily(roles.post),\n roles,\n scale: num(source.scale, 0.85, 1.2, base.scale),\n weight: Math.round(num(source.weight, 300, 500, base.weight)),\n bold: Math.round(num(source.bold, 500, 800, base.bold)),\n tracking: num(source.tracking, -0.03, 0.06, base.tracking),\n leading: source.leading === 0 ? 0 : num(source.leading, 1.2, 1.8, base.leading),\n };\n}\nconst sanitizeShape = (value: unknown, base: ShapeSpec): ShapeSpec => {\n const source = obj(value);\n return { radius: Math.round(num(source.radius, 0, 28, base.radius)), button: Math.round(num(source.button, 0, 9999, base.button)), avatar: pick(source.avatar, ['circle', 'rounded', 'square'] as const, base.avatar) };\n};\nconst sanitizeLayout = (value: unknown, base: LayoutSpec): LayoutSpec => {\n const source = obj(value);\n return {\n column: Math.round(num(source.column, 520, 820, base.column)), sidebar: Math.round(num(source.sidebar, 290, 400, base.sidebar)),\n density: pick(source.density, ['compact', 'default', 'comfortable'] as const, base.density),\n cards: typeof source.cards === 'boolean' ? source.cards : base.cards, frame: typeof source.frame === 'boolean' ? source.frame : base.frame,\n };\n};\nconst sanitizeEffects = (value: unknown, base: EffectsSpec): EffectsSpec => {\n const source = obj(value);\n return {\n motion: Math.round(num(source.motion, 0, 400, base.motion)), hover: pick(source.hover, ['none', 'tint', 'lift', 'glow'] as const, base.hover),\n shadow: pick(source.shadow, ['none', 'soft', 'deep'] as const, base.shadow), crt: typeof source.crt === 'boolean' ? source.crt : base.crt,\n };\n};\n\nexport const slug = (value: string) => value.toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'theme';\n\n/** Any JSON to a complete, safe theme. Missing groups fall back to the schema's defaults. */\nexport function sanitizeSpec(input: unknown): ThemeSpec {\n const source = obj(input);\n const name = text(source.name, 60) || 'Untitled theme';\n const id = typeof source.id === 'string' && /^[a-z0-9-]{1,40}$/.test(source.id) ? source.id : slug(name);\n return {\n id, name, description: text(source.description, 200), author: text(source.author, 60), version: 1,\n mode: pick(source.mode, ['light', 'dark', 'auto'] as const, 'auto'),\n colors: sanitizeColors(source.colors),\n font: sanitizeFont(source.font, BASE_FONT),\n shape: sanitizeShape(source.shape, BASE_SHAPE),\n layout: sanitizeLayout(source.layout, BASE_LAYOUT),\n background: sanitizeBackground(source.background),\n effects: sanitizeEffects(source.effects, BASE_EFFECTS),\n css: typeof source.css === 'string' ? source.css.slice(0, MAX_CSS_CHARS) : '',\n ...structureGroups(source),\n };\n}\n\n/** The optional structure groups of a theme, each present only when it says something valid. */\nfunction structureGroups(source: Record<string, unknown>): Pick<ThemeSpec, 'components' | 'variants' | 'decor' | 'icons'> {\n const components = sanitizeComponents(source.components), variants = sanitizeVariants(source.variants);\n const decor = sanitizeDecor(source.decor), icons = sanitizeThemeIcons(source.icons);\n return { ...(components ? { components } : {}), ...(variants ? { variants } : {}), ...(decor ? { decor } : {}), ...(icons ? { icons } : {}) };\n}\n\n/** True when a theme carries any of the structure groups (components, variants, decoration, icons). */\nexport const hasStructure = (theme: ThemeSpec) => !!(theme.components || theme.variants || theme.decor || theme.icons);\n\n/** Parses an imported file; errors are human-readable. */\nexport function parseThemeJson(json: string): { theme: ThemeSpec; warnings: string[] } | { error: string } {\n let raw: unknown;\n try { raw = JSON.parse(json); } catch { return { error: 'This file is not valid JSON.' }; }\n const source = obj(raw);\n if (typeof source.name !== 'string' || !source.name.trim()) return { error: 'A theme needs a \"name\".' };\n const theme = sanitizeSpec(raw);\n const warnings: string[] = [];\n if (source.colors !== undefined && source.colors !== null && !theme.colors) warnings.push('Colors were ignored: bg, text and accent must be #rrggbb (the other roles are derived when missing).');\n if (theme.css) warnings.push('Contains custom CSS, which runs inside the site like your own Custom CSS.');\n if (source.background && theme.background.kind === 'none' && obj(source.background).kind !== 'none') warnings.push('The background was ignored (images must be data:image/png, jpeg, webp, gif or avif, up to about 800 KB).');\n if (theme.colors) {\n const fixed = ensureContrast(theme.colors);\n if (fixed.text !== theme.colors.text || fixed.muted !== theme.colors.muted || fixed.accent !== theme.colors.accent) warnings.push('Some text colors were adjusted to stay readable.');\n }\n return { theme, warnings };\n}\n\n/** Applies panel overrides (and an accent) on top of a theme. Colors are then made legible. */\nexport function applyOverrides(theme: ThemeSpec, overrides: ThemeOverrides = {}, accent: string | null = null): ThemeSpec {\n const o = obj(overrides) as ThemeOverrides;\n let colors = theme.colors ? { ...theme.colors, ...Object.fromEntries(Object.entries(obj(o.colors)).filter(([, v]) => isHex(v))) } as ThemeColors : null;\n if (colors && accent) colors = { ...colors, accent, ...(theme.colors?.inverse === theme.colors?.accent ? { inverse: accent } : {}) };\n return {\n ...theme,\n colors: colors ? ensureContrast(colors) : null,\n // Missing keys fall back to the theme's own; the legacy `family`/`tweet` shorthand still overrides.\n font: o.font ? sanitizeFont(o.font, theme.font) : theme.font,\n shape: o.shape ? sanitizeShape({ ...theme.shape, ...o.shape }, theme.shape) : theme.shape,\n layout: o.layout ? sanitizeLayout({ ...theme.layout, ...o.layout }, theme.layout) : theme.layout,\n background: o.background ? sanitizeBackground(o.background) : theme.background,\n effects: o.effects ? sanitizeEffects({ ...theme.effects, ...o.effects }, theme.effects) : theme.effects,\n css: o.css && typeof o.css.text === 'string' ? o.css.text.slice(0, MAX_CSS_CHARS) : theme.css,\n ...variantOverrides(theme, o.variants),\n };\n}\n\n/** A theme's variants with the owner's per-axis say on top (the raw merge goes through the sanitizer, so the site's own option removes the axis). */\nfunction variantOverrides(theme: ThemeSpec, overrides: unknown): Pick<ThemeSpec, 'variants'> {\n if (!overrides || typeof overrides !== 'object') return {};\n const variants = sanitizeVariants({ ...theme.variants, ...(overrides as Record<string, unknown>) });\n return { variants: variants ?? undefined };\n}\n\n/** True when a theme changes nothing about the site (the site's own look without overrides). */\nexport function isNeutral(theme: ThemeSpec): boolean {\n return !theme.colors && JSON.stringify([theme.font, theme.shape, theme.layout, theme.effects]) === JSON.stringify([BASE_FONT, BASE_SHAPE, BASE_LAYOUT, BASE_EFFECTS])\n && theme.background.kind === 'none' && !theme.css && !hasStructure(theme);\n}\n\n// ---------------------------------------------------------------------------------------------\n// Fonts\n\n/** Bundled families and the names they are registered under (shell: @fontsource; site pages: FontFace, fonts.ts). */\nexport const BUNDLED_FONTS: Record<string, string> = { 'Geist': 'Geist Variable', 'Geist Mono': 'Geist Mono Variable' };\n\nexport function fontStack(names: string[]): string {\n const out: string[] = [];\n for (const name of names) {\n const real = BUNDLED_FONTS[name];\n if (real) out.push(`\"${real}\"`);\n out.push(GENERIC_FONTS.has(name) ? name : `\"${name}\"`);\n }\n // Emoji and CJK keep working whatever the theme names.\n return [...new Set(out), '\"Segoe UI Emoji\"', '\"Apple Color Emoji\"', 'sans-serif'].join(', ');\n}\n/** Roles that are drawn inside the site's page (chrome is wayBetter's own window). */\nexport const PAGE_ROLES: FontRoleName[] = ['interface', 'post', 'heading', 'numbers', 'mono', 'dialog'];\nconst rolesUsing = (theme: ThemeSpec, roles: readonly FontRoleName[]) => roles.flatMap(role => roleFamily(theme.font.roles[role]));\n/** Bundled families (Geist, Geist Mono) a theme names in the site's page. */\nexport const bundledFontsUsed = (theme: ThemeSpec, roles: readonly FontRoleName[] = PAGE_ROLES) => {\n const names = new Set(rolesUsing(theme, roles));\n return Object.keys(BUNDLED_FONTS).filter(name => names.has(name));\n};\n/** Google Fonts families a theme uses (`source: \"google\"`), for the given roles. */\nexport function googleFontsUsed(theme: ThemeSpec, roles: readonly FontRoleName[] = FONT_ROLE_NAMES): string[] {\n const out = new Set<string>();\n for (const role of roles) {\n const spec = theme.font.roles[role];\n if (spec !== 'inherit' && spec.source === 'google' && spec.family[0]) out.add(spec.family[0]);\n }\n return [...out];\n}\n\n/** A role resolved to what is drawn: the stack actually used and its settings. */\nexport type ResolvedFontRole = {\n /** CSS `font-family`. */\n stack: string;\n /** The families of that stack (empty when it is the site's or app's own font). */\n family: string[];\n /** True when the family comes from another role (or the site/app default). */\n inherited: boolean;\n /** A Google Fonts family the stack starts with. */\n google: string | null;\n weight: number | null;\n size: number;\n tracking: number | null;\n tabular: boolean;\n};\nexport type ResolvedFontRoles = Record<FontRoleName, ResolvedFontRole>;\n\n/**\n * Every role resolved. `site` is the stack an inheriting interface uses: the site's own font in its\n * page, wayBetter's own UI font in the shell. Every other role inherits the interface, except that an\n * inheriting `mono` keeps a monospaced stack (`mono`: themeApi.ts `MONO_FAMILIES`, code rarely suits\n * an interface font).\n */\nexport function resolveFontRoles(font: FontSpec, site: string, mono = fontStack(MONO_FAMILIES)): ResolvedFontRoles {\n const base = font.roles.interface;\n const baseFamily = roleFamily(base);\n const root: ResolvedFontRole = {\n stack: baseFamily.length ? fontStack(baseFamily) : site, family: baseFamily, inherited: !baseFamily.length,\n google: base !== 'inherit' && base.source === 'google' ? baseFamily[0] ?? null : null,\n weight: null, size: 1, tracking: null, tabular: false,\n };\n const out = { interface: root } as ResolvedFontRoles;\n for (const role of FONT_ROLE_NAMES) {\n if (role === 'interface') continue;\n const spec = font.roles[role];\n const own = roleFamily(spec);\n const from = own.length\n ? { stack: fontStack(own), family: own, inherited: false, google: spec !== 'inherit' && spec.source === 'google' ? own[0]! : null }\n : role === 'mono' ? { stack: mono, family: [] as string[], inherited: true, google: null }\n : { stack: root.stack, family: root.family, inherited: true, google: root.google };\n out[role] = {\n ...from,\n weight: spec === 'inherit' ? null : spec.weight ?? null,\n size: spec === 'inherit' ? 1 : spec.size ?? 1,\n tracking: spec === 'inherit' ? null : spec.tracking ?? null,\n tabular: spec !== 'inherit' && !!spec.tabular,\n };\n }\n return out;\n}\n\n/**\n * A named font stack offered by the panel's pickers. `key` names its translated label\n * (`wb.fonts.choice.*`); `label` is the English one. A site adds its own font first (X: \"X's Chirp\",\n * an empty family that keeps the site's type).\n */\nexport type FontChoice = { id: string; label: string; key?: string; family: string[] };\n/** Font choices offered by the panel; any installed family can also be named in theme JSON. */\nexport const FONT_CHOICES: FontChoice[] = [\n { id: 'geist', label: 'Geist', family: ['Geist', ...SYSTEM_SANS] },\n { id: 'geist-mono', label: 'Geist Mono', family: ['Geist Mono', 'Cascadia Mono', 'Consolas', 'Menlo', 'ui-monospace', 'monospace'] },\n { id: 'system', label: 'System UI', key: 'wb.fonts.choice.system', family: SYSTEM_SANS },\n { id: 'helvetica', label: 'Helvetica / Arial', family: ['Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'] },\n { id: 'humanist', label: 'Humanist (Segoe, Avenir)', key: 'wb.fonts.choice.humanist', family: ['Avenir Next', 'Segoe UI Variable Text', 'Segoe UI', 'sans-serif'] },\n { id: 'rounded', label: 'Rounded', key: 'wb.fonts.choice.rounded', family: ['ui-rounded', 'SF Pro Rounded', 'Nunito', 'Bahnschrift', 'Segoe UI', 'sans-serif'] },\n { id: 'serif', label: 'Serif (Charter, Georgia)', key: 'wb.fonts.choice.serif', family: ['Charter', 'Iowan Old Style', 'Georgia', 'Cambria', 'serif'] },\n { id: 'mono', label: 'System mono', key: 'wb.fonts.choice.mono', family: ['Cascadia Mono', 'SF Mono', 'Consolas', 'Menlo', 'ui-monospace', 'monospace'] },\n];\n/** The choice whose stack is exactly `family` in `choices`, or `custom`. */\nexport const fontChoiceOf = (family: string[], choices: readonly FontChoice[] = FONT_CHOICES) => choices.find(choice => choice.family.join('|') === family.join('|'))?.id ?? 'custom';\n\n// ---------------------------------------------------------------------------------------------\n// Backgrounds\n\nconst svgUrl = (svg: string) => `url(\"data:image/svg+xml,${encodeURIComponent(svg)}\")`;\n\n/**\n * The site's page background. A site's `<html>` and `<body>` are often one viewport tall while the\n * content overflows them (X's timeline), so a background painted on both had `<body>` paint its own\n * copy over its box, which scrolls away: after a partial scroll the gradient/glow showed only below a hard seam. Now\n * `<html>` holds the plain page color, `<body>` is transparent (beating, by specificity, the\n * palette's `html,body{background-color}` and its `[style^=\"background-color: …\"]` remap of the\n * inline color X puts on `<body>`) and every layer sits on one viewport-sized fixed\n * layer behind all of the page (`z-index:-1` in the root stacking context), with no `fixed` attachments.\n */\nexport function pageBackgroundCss(background: Background, page: string): string[] {\n const out = [`html:root{background:${page} !important}`, `html:root body{background:transparent !important}`];\n if (background.kind !== 'none') {\n out.push(`html:root::before{content:\"\";position:fixed;inset:0;z-index:-1;pointer-events:none;contain:strict;background:${backgroundLayers(background, page, false)}}`);\n }\n return out;\n}\n\n/**\n * CSS `background` layers for the page canvas (behind the site's columns). `attach` keeps the `fixed`\n * attachments for a box that scrolls (the Theme Builder preview); the site's page gets the layers without\n * them, on one viewport-sized fixed layer (`pageBackgroundCss`).\n */\nexport function backgroundLayers(background: Background, page: string, attach = true): string {\n const fixed = attach ? ' fixed' : '';\n switch (background.kind) {\n case 'gradient': {\n const glow = background.glow ? `radial-gradient(1200px 600px at 50% -10%, ${rgba(background.glow, 0.55)}, transparent 70%), ` : '';\n return `${glow}linear-gradient(${background.angle}deg, ${background.from}, ${background.to})${fixed}, ${page}`;\n }\n case 'pattern': {\n const { ink, opacity: a, size: s } = background;\n const c = rgba(ink, a);\n switch (background.pattern) {\n case 'dots': return `radial-gradient(${c} 1px, transparent 1.6px) 0 0 / ${s}px ${s}px, ${page}`;\n case 'grid': return `linear-gradient(${c} 1px, transparent 1px) 0 0 / ${s}px ${s}px, linear-gradient(90deg, ${c} 1px, transparent 1px) 0 0 / ${s}px ${s}px, ${page}`;\n case 'diagonal': return `repeating-linear-gradient(135deg, ${c} 0 1px, transparent 1px ${s}px), ${page}`;\n case 'scanlines': return `repeating-linear-gradient(0deg, ${c} 0 1px, transparent 1px ${s}px), radial-gradient(ellipse at 50% 40%, ${rgba(ink, a * 1.6)}, transparent 70%)${fixed}, ${page}`;\n case 'paper': return `${svgUrl(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${s}\" height=\"${s}\"><filter id=\"n\"><feTurbulence type=\"fractalNoise\" baseFrequency=\".85\" numOctaves=\"3\" stitchTiles=\"stitch\"/><feColorMatrix values=\"0 0 0 0 ${hexToRgb(ink).map(v => (v / 255).toFixed(3)).join(' 0 0 0 0 ')} 0 0 0 ${(a * 2.2).toFixed(3)} 0\"/></filter><rect width=\"100%\" height=\"100%\" filter=\"url(#n)\"/></svg>`)} 0 0 / ${s}px ${s}px, ${page}`;\n case 'clouds': return [\n `radial-gradient(${s}px ${Math.round(s * 0.42)}px at 12% 22%, ${c}, transparent 70%)${fixed}`,\n `radial-gradient(${Math.round(s * 1.3)}px ${Math.round(s * 0.5)}px at 86% 12%, ${c}, transparent 70%)${fixed}`,\n `radial-gradient(${Math.round(s * 1.1)}px ${Math.round(s * 0.4)}px at 78% 70%, ${c}, transparent 70%)${fixed}`,\n `radial-gradient(${Math.round(s * 0.9)}px ${Math.round(s * 0.36)}px at 18% 82%, ${c}, transparent 70%)${fixed}`,\n `linear-gradient(180deg, ${mix(page, '#ffffff', 0.25)}, ${page} 60%)${fixed}`, page,\n ].join(', ');\n }\n return page;\n }\n case 'image': {\n const scrim = `linear-gradient(${rgba(page, background.dim)}, ${rgba(page, background.dim)})${fixed}`;\n return background.fit === 'tile' ? `${scrim}, url(\"${background.url}\") 0 0 / auto repeat, ${page}` : `${scrim}, url(\"${background.url}\") center / cover no-repeat${fixed}, ${page}`;\n }\n default: return page;\n }\n}\n"]}
|
|
@@ -17,6 +17,18 @@ export type ColorModel = {
|
|
|
17
17
|
exact: Map<string, Rgb>;
|
|
18
18
|
sourceAccent: Oklab;
|
|
19
19
|
accent: Oklab | null;
|
|
20
|
+
/**
|
|
21
|
+
* Text only: the stretch of the site's axis between its most raised surface and its secondary text.
|
|
22
|
+
* A foreground the site paints there (a dimmed icon, a gray it also uses for borders) would land
|
|
23
|
+
* between the theme's background and its secondary text, under every contrast a theme guarantees;
|
|
24
|
+
* it becomes the theme's secondary text instead. Below `from` a foreground is as dark (or light) as
|
|
25
|
+
* the site's surfaces: a label on a filled surface, which keeps its place.
|
|
26
|
+
*/
|
|
27
|
+
dim: {
|
|
28
|
+
from: number;
|
|
29
|
+
to: number;
|
|
30
|
+
color: Rgb;
|
|
31
|
+
} | null;
|
|
20
32
|
};
|
|
21
33
|
/**
|
|
22
34
|
* How a color is used decides which site role wins where the site reuses one value for two roles (Default's
|