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,116 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Settings rows every app has: the home page, where links open and session restore (waybetter-host
|
|
3
|
+
// `settings_get/set`), keyword alerts (`alerts_get/set`) and the media panel's folders and
|
|
4
|
+
// clipboard history (`media_get/set`). Stored natively; applied live to every tab.
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
import { invoke } from "../frame/bridge.js";
|
|
7
|
+
import { useT } from "../frame/hooks.js";
|
|
8
|
+
import { CLIP_MAX_LIMIT, clampClipMax, folderLabel, withoutFolder } from "../frame/mediaModel.js";
|
|
9
|
+
import { site } from "../frame/site.js";
|
|
10
|
+
import { ChipList, Button, InputRow, Row, Section, Segmented, Stepper, Toggle } from "./controls.js";
|
|
11
|
+
import { panelSite } from "./site.js";
|
|
12
|
+
const getError = (error) => error instanceof Error ? error.message : String(error);
|
|
13
|
+
const FOLDER_ICON = _jsx("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": "true", children: _jsx("path", { d: "M2 4.5A1.5 1.5 0 0 1 3.5 3h3l1.5 1.5h4.5A1.5 1.5 0 0 1 14 6v5.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 11.5z", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }) });
|
|
14
|
+
const OPENS = { same: 'wb.settings.links.opens.same', pane: 'wb.settings.links.opens.pane', tab: 'wb.settings.links.opens.tab', browser: 'wb.settings.links.opens.browser' };
|
|
15
|
+
/** Home page, where links open, session restore. Stored natively; read by the tab webviews. */
|
|
16
|
+
export function BrowserSettings({ notify }) {
|
|
17
|
+
const t = useT();
|
|
18
|
+
const [settings, setSettings] = useState(null);
|
|
19
|
+
const [home, setHome] = useState('');
|
|
20
|
+
useEffect(() => { invoke('settings_get').then(value => { setSettings(value); setHome(value.homeUrl); }).catch(err => notify(getError(err), true)); }, []);
|
|
21
|
+
async function save(next, message) {
|
|
22
|
+
const previous = settings;
|
|
23
|
+
setSettings(next);
|
|
24
|
+
try {
|
|
25
|
+
const saved = await invoke('settings_set', { settings: next });
|
|
26
|
+
setSettings(saved);
|
|
27
|
+
setHome(saved.homeUrl);
|
|
28
|
+
notify(message);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
setSettings(previous);
|
|
32
|
+
notify(getError(err), true);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (!settings)
|
|
36
|
+
return null;
|
|
37
|
+
const siteHome = site().homeUrl;
|
|
38
|
+
return _jsxs(Section, { title: t('wb.settings.links.title'), hint: t('wb.settings.links.hint'), children: [_jsx(Row, { label: t('wb.settings.home.label'), hint: t('wb.settings.home.hint'), keywords: "new tab start url", wide: true, children: _jsxs("div", { className: "bt-inline", children: [_jsx(InputRow, { label: t('wb.settings.home.url'), mono: true, value: home, onChange: setHome, button: t('wb.common.save'), disabled: home === settings.homeUrl, onSubmit: () => void save({ ...settings, homeUrl: home }, t('wb.settings.home.saved')) }), settings.homeUrl !== siteHome && _jsx(Button, { tone: "ghost", size: "sm", onClick: () => void save({ ...settings, homeUrl: siteHome }, t('wb.settings.home.resetDone')), children: t('wb.settings.home.reset') })] }) }), _jsx(Row, { label: t('wb.settings.links.external'), hint: t('wb.settings.links.externalHint'), keywords: "external open browser pane split side", children: _jsx(Segmented, { label: t('wb.settings.links.external'), value: settings.externalLinks, onChange: mode => void save({ ...settings, externalLinks: mode }, t('wb.settings.links.externalSaved', { how: t(OPENS[mode]) })), options: [{ value: 'pane', label: t('wb.settings.links.beside') }, { value: 'tab', label: t('wb.settings.links.newTab') }, { value: 'browser', label: t('wb.settings.links.browser') }] }) }), _jsx(Row, { label: t('wb.settings.links.site'), hint: t('wb.settings.links.siteHint'), keywords: `${panelSite().settings?.linkKeywords ?? site().name} post profile open pane split side tab`, children: _jsx(Segmented, { label: t('wb.settings.links.site'), value: settings.siteLinks, onChange: mode => void save({ ...settings, siteLinks: mode }, t('wb.settings.links.siteSaved', { how: t(OPENS[mode]) })), options: [{ value: 'same', label: t('wb.settings.links.sameTab') }, { value: 'pane', label: t('wb.settings.links.beside') }, { value: 'tab', label: t('wb.settings.links.newTab') }, { value: 'browser', label: t('wb.settings.links.browser') }] }) }), _jsx(Row, { label: t('wb.settings.restore.label'), hint: t('wb.settings.restore.hint'), keywords: "session", children: _jsx(Toggle, { label: t('wb.settings.restore.label'), checked: settings.restoreTabs, onChange: next => void save({ ...settings, restoreTabs: next }, next ? t('wb.settings.restore.on') : t('wb.settings.restore.off')) }) })] });
|
|
39
|
+
}
|
|
40
|
+
export const parseKeywords = (text) => text.split(/[,\n]/).map(word => word.trim()).filter(Boolean);
|
|
41
|
+
const MAX_KEYWORDS = 32;
|
|
42
|
+
/** Keyword alerts: local OS notifications for keywords in items the site already loaded. Native only. */
|
|
43
|
+
export function AlertSettings({ notify }) {
|
|
44
|
+
const t = useT();
|
|
45
|
+
const [alerts, setAlerts] = useState(null);
|
|
46
|
+
const [text, setText] = useState('');
|
|
47
|
+
useEffect(() => { invoke('alerts_get').then(setAlerts).catch(err => notify(getError(err), true)); }, []);
|
|
48
|
+
async function save(next, message) {
|
|
49
|
+
const previous = alerts;
|
|
50
|
+
setAlerts(next);
|
|
51
|
+
try {
|
|
52
|
+
setAlerts(await invoke('alerts_set', { settings: next }));
|
|
53
|
+
notify(message);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
setAlerts(previous);
|
|
57
|
+
notify(getError(err), true);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function add() {
|
|
61
|
+
if (!alerts)
|
|
62
|
+
return;
|
|
63
|
+
const known = new Set(alerts.keywords.map(word => word.toLowerCase()));
|
|
64
|
+
const fresh = parseKeywords(text).filter(word => !known.has(word.toLowerCase()));
|
|
65
|
+
if (!fresh.length) {
|
|
66
|
+
setText('');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const keywords = [...alerts.keywords, ...fresh].slice(0, MAX_KEYWORDS);
|
|
70
|
+
setText('');
|
|
71
|
+
void save({ ...alerts, keywords }, fresh.length === 1 ? t('wb.settings.alerts.watching', { word: fresh[0] }) : t('wb.settings.alerts.watchingMore', { count: fresh.length }));
|
|
72
|
+
}
|
|
73
|
+
if (!alerts)
|
|
74
|
+
return null;
|
|
75
|
+
return _jsxs(Section, { title: t('wb.settings.alerts.title'), hint: t('wb.settings.alerts.hint'), children: [_jsx(Row, { label: t('wb.settings.alerts.label'), hint: alerts.enabled ? t('wb.settings.alerts.onCount', { count: alerts.keywords.length }) : t('wb.common.off'), keywords: "keyword notification notify", children: _jsx(Toggle, { label: t('wb.settings.alerts.title'), checked: alerts.enabled, onChange: next => void save({ ...alerts, enabled: next }, next ? t('wb.settings.alerts.turnedOn') : t('wb.settings.alerts.turnedOff')) }) }), _jsx(Row, { label: t('wb.settings.alerts.keywords'), hint: t('wb.settings.alerts.keywordsHint', { max: MAX_KEYWORDS }), keywords: "alert words watch", wide: true, children: _jsxs("div", { className: "bt-stack", children: [_jsx(ChipList, { items: alerts.keywords, empty: t('wb.settings.alerts.none'), onRemove: word => void save({ ...alerts, keywords: alerts.keywords.filter(item => item !== word) }, t('wb.settings.alerts.stopped', { word })) }), _jsx(InputRow, { label: t('wb.settings.alerts.add'), placeholder: "tauri, waybetter", value: text, onChange: setText, button: t('wb.common.add'), disabled: !parseKeywords(text).length || alerts.keywords.length >= MAX_KEYWORDS, onSubmit: add })] }) })] });
|
|
76
|
+
}
|
|
77
|
+
/** Gallery folders and the opt-in clipboard history for the Media panel (Ctrl+Shift+G). Native only. */
|
|
78
|
+
export function MediaSettingsRows({ notify }) {
|
|
79
|
+
const t = useT();
|
|
80
|
+
const [settings, setSettings] = useState(null);
|
|
81
|
+
useEffect(() => { invoke('media_get').then(setSettings).catch(err => notify(getError(err), true)); }, []);
|
|
82
|
+
async function save(next, message) {
|
|
83
|
+
const previous = settings;
|
|
84
|
+
setSettings(next);
|
|
85
|
+
try {
|
|
86
|
+
setSettings(await invoke('media_set', { settings: next }));
|
|
87
|
+
if (message)
|
|
88
|
+
notify(message);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
setSettings(previous);
|
|
92
|
+
notify(getError(err), true);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function addFolder() {
|
|
96
|
+
try {
|
|
97
|
+
setSettings(await invoke('gallery_add_folder'));
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
notify(getError(err), true);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function clearHistory() {
|
|
104
|
+
try {
|
|
105
|
+
await invoke('clip_remove', { id: null });
|
|
106
|
+
notify(t('wb.settings.media.cleared'));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
notify(getError(err), true);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!settings)
|
|
113
|
+
return null;
|
|
114
|
+
return _jsxs(Section, { title: t('wb.settings.media.title'), hint: t('wb.settings.media.hint'), children: [_jsx(Row, { label: t('wb.settings.media.folders'), hint: t('wb.settings.media.foldersHint'), keywords: "images pictures screenshots", wide: true, children: _jsxs("div", { className: "bt-stack", children: [_jsx(ChipList, { items: settings.folders, icon: FOLDER_ICON, render: folderLabel, empty: t('wb.settings.media.noFolders'), onRemove: folder => void save(withoutFolder(settings, folder), t('wb.settings.media.folderRemoved')) }), _jsx("div", { children: _jsx(Button, { size: "sm", onClick: () => void addFolder(), icon: _jsx("span", { "aria-hidden": "true", children: "\uFF0B" }), children: t('wb.settings.media.addFolder') }) })] }) }), _jsx(Row, { label: t('wb.settings.media.clipboard'), hint: t('wb.settings.media.clipboardHint'), keywords: "copy paste", children: _jsx(Toggle, { label: t('wb.settings.media.clipboard'), checked: settings.clipboardEnabled, onChange: next => void save({ ...settings, clipboardEnabled: next }, next ? t('wb.settings.media.clipboardOn') : t('wb.settings.media.clipboardOff')) }) }), settings.clipboardEnabled && _jsx(Row, { label: t('wb.settings.media.keep'), hint: t('wb.settings.media.keepHint'), keywords: "clipboard size", children: _jsxs("div", { className: "bt-inline", children: [_jsx(Stepper, { label: t('wb.settings.media.keep'), value: settings.clipboardMax, min: 1, max: CLIP_MAX_LIMIT, onChange: value => void save({ ...settings, clipboardMax: clampClipMax(value) }) }), _jsx(Button, { size: "sm", tone: "ghost", onClick: () => void clearHistory(), children: t('wb.common.clear') })] }) })] });
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=Settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Settings.js","sourceRoot":"","sources":["../../../../../../shell/src/panel/Settings.tsx"],"names":[],"mappings":";AAAA,mGAAmG;AACnG,2FAA2F;AAC3F,mFAAmF;AACnF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAsB,MAAM,wBAAwB,CAAC;AACtH,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAgB,CAAC;AACtG,OAAO,EAAE,SAAS,EAAe,MAAM,WAAW,CAAC;AAYnD,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5F,MAAM,WAAW,GAAG,cAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,iBAAa,MAAM,YAAC,eAAM,CAAC,EAAC,gHAAgH,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,KAAK,GAAE,GAAM,CAAC;AAG5Q,MAAM,KAAK,GAA0B,EAAE,IAAI,EAAE,8BAA8B,EAAE,IAAI,EAAE,8BAA8B,EAAE,GAAG,EAAE,6BAA6B,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC;AAEpM,+FAA+F;AAC/F,MAAM,UAAU,eAAe,CAAC,EAAE,MAAM,EAAsB;IAC5D,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAuB,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAErC,SAAS,CAAC,GAAG,EAAE,GAAG,MAAM,CAAgB,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEzK,KAAK,UAAU,IAAI,CAAC,IAAmB,EAAE,OAAe;QACtD,MAAM,QAAQ,GAAG,QAAQ,CAAC;QAC1B,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC;YAAC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAgB,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QACnJ,OAAO,GAAG,EAAE,CAAC;YAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAAC,CAAC;IACrE,CAAC;IAED,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAI,EAAE,CAAC,OAAO,CAAC;IAChC,OAAO,MAAC,OAAO,IAAC,KAAK,EAAE,CAAC,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,wBAAwB,CAAC,aACpF,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,uBAAuB,CAAC,EAAE,QAAQ,EAAC,mBAAmB,EAAC,IAAI,kBAC1G,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,QAAQ,IAAC,KAAK,EAAE,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,QAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,IAAI,KAAK,QAAQ,CAAC,OAAO,EAC/I,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,wBAAwB,CAAC,CAAC,GAAG,EAC1F,QAAQ,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAC,MAAM,IAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,4BAA4B,CAAC,CAAC,YAAG,CAAC,CAAC,wBAAwB,CAAC,GAAU,IAClM,GACF,EACN,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,4BAA4B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,gCAAgC,CAAC,EAAE,QAAQ,EAAC,uCAAuC,YACtI,KAAC,SAAS,IAAC,KAAK,EAAE,CAAC,CAAC,4BAA4B,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,aAAa,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,iCAAiC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,IAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EACzN,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,2BAA2B,CAAC,EAAE,CAAC,GAAG,GACxL,EACN,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,4BAA4B,CAAC,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,CAAC,QAAQ,EAAE,YAAY,IAAI,IAAI,EAAE,CAAC,IAAI,wCAAwC,YACpL,KAAC,SAAS,IAAC,KAAK,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,6BAA6B,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,IAAa,CAAC,CAAC,EAAE,CAAC,CAAC,EACzM,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,2BAA2B,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,2BAA2B,CAAC,EAAE,CAAC,GAAG,GAClP,EACN,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,2BAA2B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,QAAQ,EAAC,SAAS,YACjG,KAAC,MAAM,IAAC,KAAK,EAAE,CAAC,CAAC,2BAA2B,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,GAAG,GAC/M,IACE,CAAC;AACb,CAAC;AAGD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC5G,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,yGAAyG;AACzG,MAAM,UAAU,aAAa,CAAC,EAAE,MAAM,EAAsB;IAC1D,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAErC,SAAS,CAAC,GAAG,EAAE,GAAG,MAAM,CAAS,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEjH,KAAK,UAAU,IAAI,CAAC,IAAY,EAAE,OAAe;QAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC;QACxB,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,IAAI,CAAC;YAAC,SAAS,CAAC,MAAM,MAAM,CAAS,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAC3F,OAAO,GAAG,EAAE,CAAC;YAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAAC,CAAC;IACnE,CAAC;IACD,SAAS,GAAG;QACV,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACvE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACjF,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;QACvE,OAAO,CAAC,EAAE,CAAC,CAAC;QACZ,KAAK,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,6BAA6B,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iCAAiC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACjL,CAAC;IAED,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,MAAC,OAAO,IAAC,KAAK,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,yBAAyB,CAAC,aACtF,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAC,6BAA6B,YAC/L,KAAC,MAAM,IAAC,KAAK,EAAE,CAAC,CAAC,0BAA0B,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,GAAG,GAC5M,EACN,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,6BAA6B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,iCAAiC,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAC,mBAAmB,EAAC,IAAI,kBAChJ,eAAK,SAAS,EAAC,UAAU,aACvB,KAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,yBAAyB,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,4BAA4B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EACxN,KAAC,QAAQ,IAAC,KAAK,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAC,kBAAkB,EAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,IAAI,YAAY,EAAE,QAAQ,EAAE,GAAG,GAAG,IACtO,GACF,IACE,CAAC;AACb,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,iBAAiB,CAAC,EAAE,MAAM,EAAsB;IAC9D,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAuB,IAAI,CAAC,CAAC;IACrE,SAAS,CAAC,GAAG,EAAE,GAAG,MAAM,CAAgB,WAAW,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEzH,KAAK,UAAU,IAAI,CAAC,IAAmB,EAAE,OAAgB;QACvD,MAAM,QAAQ,GAAG,QAAQ,CAAC;QAC1B,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC;YAAC,WAAW,CAAC,MAAM,MAAM,CAAgB,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAAC,IAAI,OAAO;gBAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAChH,OAAO,GAAG,EAAE,CAAC;YAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAAC,CAAC;IACrE,CAAC;IACD,KAAK,UAAU,SAAS;QACtB,IAAI,CAAC;YAAC,WAAW,CAAC,MAAM,MAAM,CAAgB,oBAAoB,CAAC,CAAC,CAAC;QAAC,CAAC;QACvE,OAAO,GAAG,EAAE,CAAC;YAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAAC,CAAC;IAC9C,CAAC;IACD,KAAK,UAAU,YAAY;QACzB,IAAI,CAAC;YAAC,MAAM,MAAM,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAAC,MAAM,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC;QAAC,CAAC;QAC1F,OAAO,GAAG,EAAE,CAAC;YAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO,MAAC,OAAO,IAAC,KAAK,EAAE,CAAC,CAAC,yBAAyB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,wBAAwB,CAAC,aACpF,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,2BAA2B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,+BAA+B,CAAC,EAAE,QAAQ,EAAC,6BAA6B,EAAC,IAAI,kBAC/H,eAAK,SAAS,EAAC,UAAU,aACvB,KAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,6BAA6B,CAAC,EAChH,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,iCAAiC,CAAC,CAAC,GAAG,EACzG,wBAAK,KAAC,MAAM,IAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,SAAS,EAAE,EAAE,IAAI,EAAE,8BAAkB,MAAM,uBAAS,YAAG,CAAC,CAAC,6BAA6B,CAAC,GAAU,GAAM,IAC9I,GACF,EACN,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,6BAA6B,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,iCAAiC,CAAC,EAAE,QAAQ,EAAC,YAAY,YAC7G,KAAC,MAAM,IAAC,KAAK,EAAE,CAAC,CAAC,6BAA6B,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,gBAAgB,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,gBAAgB,EAAE,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,GAAG,GACzO,EACL,QAAQ,CAAC,gBAAgB,IAAI,KAAC,GAAG,IAAC,KAAK,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,4BAA4B,CAAC,EAAE,QAAQ,EAAC,gBAAgB,YACrI,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,OAAO,IAAC,KAAK,EAAE,CAAC,CAAC,wBAAwB,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAC3L,KAAC,MAAM,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,YAAY,EAAE,YAAG,CAAC,CAAC,iBAAiB,CAAC,GAAU,IAC9F,GACF,IACE,CAAC;AACb,CAAC","sourcesContent":["// Settings rows every app has: the home page, where links open and session restore (waybetter-host\n// `settings_get/set`), keyword alerts (`alerts_get/set`) and the media panel's folders and\n// clipboard history (`media_get/set`). Stored natively; applied live to every tab.\nimport { useEffect, useState } from 'react';\nimport { invoke } from '../frame/bridge.ts';\nimport { useT } from '../frame/hooks.ts';\nimport { CLIP_MAX_LIMIT, clampClipMax, folderLabel, withoutFolder, type MediaSettings } from '../frame/mediaModel.ts';\nimport { site } from '../frame/site.ts';\nimport { ChipList, Button, InputRow, Row, Section, Segmented, Stepper, Toggle } from './controls.tsx';\nimport { panelSite, type Notify } from './site.ts';\n\n/** The shell's settings (waybetter-host `ShellSettings`). */\nexport type ShellSettings = {\n homeUrl: string;\n /** Links to other sites: beside the tab (split), a new tab, or the system browser. */\n externalLinks: 'pane' | 'tab' | 'browser';\n /** Site pages clicked in the page: the site's in-place navigation, beside, a new tab, or the system browser. */\n siteLinks: 'same' | 'pane' | 'tab' | 'browser';\n restoreTabs: boolean;\n};\n\nconst getError = (error: unknown) => error instanceof Error ? error.message : String(error);\nconst FOLDER_ICON = <svg width=\"13\" height=\"13\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M2 4.5A1.5 1.5 0 0 1 3.5 3h3l1.5 1.5h4.5A1.5 1.5 0 0 1 14 6v5.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 11.5z\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.3\"/></svg>;\n\ntype Opens = 'same' | 'pane' | 'tab' | 'browser';\nconst OPENS: Record<Opens, string> = { same: 'wb.settings.links.opens.same', pane: 'wb.settings.links.opens.pane', tab: 'wb.settings.links.opens.tab', browser: 'wb.settings.links.opens.browser' };\n\n/** Home page, where links open, session restore. Stored natively; read by the tab webviews. */\nexport function BrowserSettings({ notify }: { notify: Notify }) {\n const t = useT();\n const [settings, setSettings] = useState<ShellSettings | null>(null);\n const [home, setHome] = useState('');\n\n useEffect(() => { invoke<ShellSettings>('settings_get').then(value => { setSettings(value); setHome(value.homeUrl); }).catch(err => notify(getError(err), true)); }, []);\n\n async function save(next: ShellSettings, message: string) {\n const previous = settings;\n setSettings(next);\n try { const saved = await invoke<ShellSettings>('settings_set', { settings: next }); setSettings(saved); setHome(saved.homeUrl); notify(message); }\n catch (err) { setSettings(previous); notify(getError(err), true); }\n }\n\n if (!settings) return null;\n const siteHome = site().homeUrl;\n return <Section title={t('wb.settings.links.title')} hint={t('wb.settings.links.hint')}>\n <Row label={t('wb.settings.home.label')} hint={t('wb.settings.home.hint')} keywords=\"new tab start url\" wide>\n <div className=\"bt-inline\">\n <InputRow label={t('wb.settings.home.url')} mono value={home} onChange={setHome} button={t('wb.common.save')} disabled={home === settings.homeUrl}\n onSubmit={() => void save({ ...settings, homeUrl: home }, t('wb.settings.home.saved'))}/>\n {settings.homeUrl !== siteHome && <Button tone=\"ghost\" size=\"sm\" onClick={() => void save({ ...settings, homeUrl: siteHome }, t('wb.settings.home.resetDone'))}>{t('wb.settings.home.reset')}</Button>}\n </div>\n </Row>\n <Row label={t('wb.settings.links.external')} hint={t('wb.settings.links.externalHint')} keywords=\"external open browser pane split side\">\n <Segmented label={t('wb.settings.links.external')} value={settings.externalLinks} onChange={mode => void save({ ...settings, externalLinks: mode }, t('wb.settings.links.externalSaved', { how: t(OPENS[mode as Opens]) }))}\n options={[{ value: 'pane', label: t('wb.settings.links.beside') }, { value: 'tab', label: t('wb.settings.links.newTab') }, { value: 'browser', label: t('wb.settings.links.browser') }]}/>\n </Row>\n <Row label={t('wb.settings.links.site')} hint={t('wb.settings.links.siteHint')} keywords={`${panelSite().settings?.linkKeywords ?? site().name} post profile open pane split side tab`}>\n <Segmented label={t('wb.settings.links.site')} value={settings.siteLinks} onChange={mode => void save({ ...settings, siteLinks: mode }, t('wb.settings.links.siteSaved', { how: t(OPENS[mode as Opens]) }))}\n options={[{ value: 'same', label: t('wb.settings.links.sameTab') }, { value: 'pane', label: t('wb.settings.links.beside') }, { value: 'tab', label: t('wb.settings.links.newTab') }, { value: 'browser', label: t('wb.settings.links.browser') }]}/>\n </Row>\n <Row label={t('wb.settings.restore.label')} hint={t('wb.settings.restore.hint')} keywords=\"session\">\n <Toggle label={t('wb.settings.restore.label')} checked={settings.restoreTabs} onChange={next => void save({ ...settings, restoreTabs: next }, next ? t('wb.settings.restore.on') : t('wb.settings.restore.off'))}/>\n </Row>\n </Section>;\n}\n\ntype Alerts = { enabled: boolean; keywords: string[] };\nexport const parseKeywords = (text: string) => text.split(/[,\\n]/).map(word => word.trim()).filter(Boolean);\nconst MAX_KEYWORDS = 32;\n\n/** Keyword alerts: local OS notifications for keywords in items the site already loaded. Native only. */\nexport function AlertSettings({ notify }: { notify: Notify }) {\n const t = useT();\n const [alerts, setAlerts] = useState<Alerts | null>(null);\n const [text, setText] = useState('');\n\n useEffect(() => { invoke<Alerts>('alerts_get').then(setAlerts).catch(err => notify(getError(err), true)); }, []);\n\n async function save(next: Alerts, message: string) {\n const previous = alerts;\n setAlerts(next);\n try { setAlerts(await invoke<Alerts>('alerts_set', { settings: next })); notify(message); }\n catch (err) { setAlerts(previous); notify(getError(err), true); }\n }\n function add() {\n if (!alerts) return;\n const known = new Set(alerts.keywords.map(word => word.toLowerCase()));\n const fresh = parseKeywords(text).filter(word => !known.has(word.toLowerCase()));\n if (!fresh.length) { setText(''); return; }\n const keywords = [...alerts.keywords, ...fresh].slice(0, MAX_KEYWORDS);\n setText('');\n void save({ ...alerts, keywords }, fresh.length === 1 ? t('wb.settings.alerts.watching', { word: fresh[0]! }) : t('wb.settings.alerts.watchingMore', { count: fresh.length }));\n }\n\n if (!alerts) return null;\n return <Section title={t('wb.settings.alerts.title')} hint={t('wb.settings.alerts.hint')}>\n <Row label={t('wb.settings.alerts.label')} hint={alerts.enabled ? t('wb.settings.alerts.onCount', { count: alerts.keywords.length }) : t('wb.common.off')} keywords=\"keyword notification notify\">\n <Toggle label={t('wb.settings.alerts.title')} checked={alerts.enabled} onChange={next => void save({ ...alerts, enabled: next }, next ? t('wb.settings.alerts.turnedOn') : t('wb.settings.alerts.turnedOff'))}/>\n </Row>\n <Row label={t('wb.settings.alerts.keywords')} hint={t('wb.settings.alerts.keywordsHint', { max: MAX_KEYWORDS })} keywords=\"alert words watch\" wide>\n <div className=\"bt-stack\">\n <ChipList items={alerts.keywords} empty={t('wb.settings.alerts.none')} onRemove={word => void save({ ...alerts, keywords: alerts.keywords.filter(item => item !== word) }, t('wb.settings.alerts.stopped', { word }))}/>\n <InputRow label={t('wb.settings.alerts.add')} placeholder=\"tauri, waybetter\" value={text} onChange={setText} button={t('wb.common.add')} disabled={!parseKeywords(text).length || alerts.keywords.length >= MAX_KEYWORDS} onSubmit={add}/>\n </div>\n </Row>\n </Section>;\n}\n\n/** Gallery folders and the opt-in clipboard history for the Media panel (Ctrl+Shift+G). Native only. */\nexport function MediaSettingsRows({ notify }: { notify: Notify }) {\n const t = useT();\n const [settings, setSettings] = useState<MediaSettings | null>(null);\n useEffect(() => { invoke<MediaSettings>('media_get').then(setSettings).catch(err => notify(getError(err), true)); }, []);\n\n async function save(next: MediaSettings, message?: string) {\n const previous = settings;\n setSettings(next);\n try { setSettings(await invoke<MediaSettings>('media_set', { settings: next })); if (message) notify(message); }\n catch (err) { setSettings(previous); notify(getError(err), true); }\n }\n async function addFolder() {\n try { setSettings(await invoke<MediaSettings>('gallery_add_folder')); }\n catch (err) { notify(getError(err), true); }\n }\n async function clearHistory() {\n try { await invoke('clip_remove', { id: null }); notify(t('wb.settings.media.cleared')); }\n catch (err) { notify(getError(err), true); }\n }\n\n if (!settings) return null;\n return <Section title={t('wb.settings.media.title')} hint={t('wb.settings.media.hint')}>\n <Row label={t('wb.settings.media.folders')} hint={t('wb.settings.media.foldersHint')} keywords=\"images pictures screenshots\" wide>\n <div className=\"bt-stack\">\n <ChipList items={settings.folders} icon={FOLDER_ICON} render={folderLabel} empty={t('wb.settings.media.noFolders')}\n onRemove={folder => void save(withoutFolder(settings, folder), t('wb.settings.media.folderRemoved'))}/>\n <div><Button size=\"sm\" onClick={() => void addFolder()} icon={<span aria-hidden=\"true\">+</span>}>{t('wb.settings.media.addFolder')}</Button></div>\n </div>\n </Row>\n <Row label={t('wb.settings.media.clipboard')} hint={t('wb.settings.media.clipboardHint')} keywords=\"copy paste\">\n <Toggle label={t('wb.settings.media.clipboard')} checked={settings.clipboardEnabled} onChange={next => void save({ ...settings, clipboardEnabled: next }, next ? t('wb.settings.media.clipboardOn') : t('wb.settings.media.clipboardOff'))}/>\n </Row>\n {settings.clipboardEnabled && <Row label={t('wb.settings.media.keep')} hint={t('wb.settings.media.keepHint')} keywords=\"clipboard size\">\n <div className=\"bt-inline\">\n <Stepper label={t('wb.settings.media.keep')} value={settings.clipboardMax} min={1} max={CLIP_MAX_LIMIT} onChange={value => void save({ ...settings, clipboardMax: clampClipMax(value) })}/>\n <Button size=\"sm\" tone=\"ghost\" onClick={() => void clearHistory()}>{t('wb.common.clear')}</Button>\n </div>\n </Row>}\n </Section>;\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Notify } from './site.ts';
|
|
2
|
+
/** The event the shell raises when the host hands it a `waybetter://setup` link opened from outside. */
|
|
3
|
+
export declare const SETUP_LINK_EVENT = "wb-setup-link";
|
|
4
|
+
/** Keeps a link opened from outside until the section is on screen to preview it. */
|
|
5
|
+
export declare function receiveSetupLink(text: string): void;
|
|
6
|
+
export declare function ShareSetup({ notify }: {
|
|
7
|
+
notify: Notify;
|
|
8
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Settings → Backup & sync → "Share my setup" (setupModel.ts): the look, the mode and which
|
|
3
|
+
// features are on, as one `waybetter://setup` link or one small file, and the way back in: paste a
|
|
4
|
+
// link, open a file, or open a link from outside (the host forwards its text, `setup-link`). Nothing
|
|
5
|
+
// is applied before the owner has seen "What will change" and pressed Apply. The section shows only
|
|
6
|
+
// when the app names itself (`PanelSite.setup`).
|
|
7
|
+
import { useEffect, useRef, useState } from 'react';
|
|
8
|
+
import { modeById, normalizeFlags, sparseFlags } from "../../runtime/featureTable.js";
|
|
9
|
+
import { invoke } from "../frame/bridge.js";
|
|
10
|
+
import { useT } from "../frame/hooks.js";
|
|
11
|
+
import { tx } from "../frame/i18n.js";
|
|
12
|
+
import { Button, InputRow, Row, Section } from "./controls.js";
|
|
13
|
+
import { flagName, rememberModeState } from "./Modes.js";
|
|
14
|
+
import { featureTable, flagsChanged } from "./PluginFeatures.js";
|
|
15
|
+
import { SETUP_FILE_SUFFIX, applySetup, makeSetup, readSetup, setupFile, setupLink, setupPlan } from "./setupModel.js";
|
|
16
|
+
import { panelSite } from "./site.js";
|
|
17
|
+
/** The event the shell raises when the host hands it a `waybetter://setup` link opened from outside. */
|
|
18
|
+
export const SETUP_LINK_EVENT = 'wb-setup-link';
|
|
19
|
+
let pendingLink = null;
|
|
20
|
+
/** Keeps a link opened from outside until the section is on screen to preview it. */
|
|
21
|
+
export function receiveSetupLink(text) {
|
|
22
|
+
pendingLink = text;
|
|
23
|
+
window.dispatchEvent(new CustomEvent(SETUP_LINK_EVENT));
|
|
24
|
+
}
|
|
25
|
+
async function currentState() {
|
|
26
|
+
const site = panelSite();
|
|
27
|
+
const table = featureTable();
|
|
28
|
+
await site.themes?.store.loadThemes();
|
|
29
|
+
const choice = site.themes?.store.themeState().choice;
|
|
30
|
+
const flags = normalizeFlags(table, await invoke('features_get'));
|
|
31
|
+
const mode = table.modes.length ? (await invoke('mode_get')).mode : null;
|
|
32
|
+
return { ...(choice ? { theme: { preset: choice.preset, accent: choice.accent, ...(choice.icons ? { icons: choice.icons } : {}) } } : {}), mode, flags };
|
|
33
|
+
}
|
|
34
|
+
function download(name, text) {
|
|
35
|
+
const url = URL.createObjectURL(new Blob([text], { type: 'application/json' }));
|
|
36
|
+
const anchor = document.createElement('a');
|
|
37
|
+
anchor.href = url;
|
|
38
|
+
anchor.download = name;
|
|
39
|
+
anchor.click();
|
|
40
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
41
|
+
}
|
|
42
|
+
export function ShareSetup({ notify }) {
|
|
43
|
+
const site = panelSite();
|
|
44
|
+
return site.setup && featureTable() ? _jsx(ShareSetupOf, { app: site.setup.app, notify: notify }) : null;
|
|
45
|
+
}
|
|
46
|
+
function ShareSetupOf({ app, notify }) {
|
|
47
|
+
const t = useT();
|
|
48
|
+
const site = panelSite();
|
|
49
|
+
const table = featureTable();
|
|
50
|
+
const [text, setText] = useState('');
|
|
51
|
+
const [preview, setPreview] = useState(null);
|
|
52
|
+
const [busy, setBusy] = useState(false);
|
|
53
|
+
const file = useRef(null);
|
|
54
|
+
const store = site.themes?.store;
|
|
55
|
+
const error = (read) => read.error === 'app'
|
|
56
|
+
? (read.app ? t('wb.setup.err.app', { app: read.app }) : t('wb.setup.err.appUnknown'))
|
|
57
|
+
: t(`wb.setup.err.${read.error}`);
|
|
58
|
+
async function open(input) {
|
|
59
|
+
const read = readSetup(input, app, table);
|
|
60
|
+
if (!read.ok) {
|
|
61
|
+
notify(error(read), true);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const now = await currentState();
|
|
66
|
+
const themes = new Set(store?.allThemes().map(theme => theme.id) ?? []);
|
|
67
|
+
const packs = new Set(site.themes?.iconPacks?.map(pack => pack.id) ?? []);
|
|
68
|
+
setPreview({ setup: read.setup, plan: setupPlan(table, now, read.setup, themes, packs), dropped: read.dropped, now });
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
notify(err instanceof Error ? err.message : String(err), true);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// A link opened from outside (the host's `setup-link` event, forwarded by the shell).
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const take = () => { const link = pendingLink; pendingLink = null; if (link)
|
|
77
|
+
void open(link); };
|
|
78
|
+
take();
|
|
79
|
+
window.addEventListener(SETUP_LINK_EVENT, take);
|
|
80
|
+
return () => window.removeEventListener(SETUP_LINK_EVENT, take);
|
|
81
|
+
}, []);
|
|
82
|
+
async function share(kind) {
|
|
83
|
+
try {
|
|
84
|
+
const setup = makeSetup(app, table, await currentState());
|
|
85
|
+
if (kind === 'file') {
|
|
86
|
+
download(`setup${SETUP_FILE_SUFFIX}`, setupFile(setup));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
await navigator.clipboard.writeText(setupLink(setup)).then(() => notify(t('wb.setup.linkCopied')), () => notify(t('wb.common.clipboardUnavailable'), true));
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
notify(err instanceof Error ? err.message : String(err), true);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function apply() {
|
|
96
|
+
if (!preview)
|
|
97
|
+
return;
|
|
98
|
+
setBusy(true);
|
|
99
|
+
try {
|
|
100
|
+
const { plan, now } = preview;
|
|
101
|
+
const next = applySetup(table, now, plan);
|
|
102
|
+
if (plan.on.length || plan.off.length) {
|
|
103
|
+
await invoke('features_set', { features: sparseFlags(table, next.flags) });
|
|
104
|
+
flagsChanged();
|
|
105
|
+
}
|
|
106
|
+
if (store && next.theme && (plan.theme || plan.accent || plan.icons)) {
|
|
107
|
+
const choice = store.themeState().choice;
|
|
108
|
+
const { icons: _icons, ...rest } = choice;
|
|
109
|
+
await store.setChoice({ ...rest, preset: next.theme.preset, accent: next.theme.accent, ...(plan.theme ? { overrides: {} } : {}), ...(next.theme.icons ? { icons: next.theme.icons } : {}) });
|
|
110
|
+
}
|
|
111
|
+
if (plan.mode)
|
|
112
|
+
rememberModeState(await invoke('mode_set', { mode: next.mode, onboarded: true, preset: modeById(table, next.mode)?.flags ?? {} }));
|
|
113
|
+
setPreview(null);
|
|
114
|
+
setText('');
|
|
115
|
+
notify(t('wb.setup.applied'));
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
notify(err instanceof Error ? err.message : String(err), true);
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
setBusy(false);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const themeName = (id) => store?.themeName(id) ?? id;
|
|
125
|
+
const iconName = (id) => id === undefined ? t('wb.appearance.icons.theme') : id === 'site' ? t('wb.appearance.icons.site') : site.themes?.iconPacks?.find(pack => pack.id === id)?.name ?? id;
|
|
126
|
+
const modeName = (id) => id ? tx(modeById(table, id)?.name ?? id, undefined, id) : t('wb.setup.modeNone');
|
|
127
|
+
const names = (ids) => ids.map(id => flagName(table, id)).join(', ');
|
|
128
|
+
return _jsxs(Section, { title: t('wb.setup.title'), hint: t('wb.setup.hint'), id: "setup", children: [_jsx(Row, { label: t('wb.setup.shareLabel'), hint: t('wb.setup.private'), keywords: "share setup link export copy send friend", children: _jsxs("div", { className: "bt-inline", children: [_jsx(Button, { tone: "primary", onClick: () => void share('link'), children: t('wb.setup.copyLink') }), _jsx(Button, { onClick: () => void share('file'), children: t('wb.setup.saveFile') })] }) }), _jsx(Row, { label: t('wb.setup.useLabel'), hint: t('wb.setup.useHint'), keywords: "import setup link apply", wide: true, children: _jsxs("div", { className: "bt-inline", children: [_jsx(InputRow, { value: text, onChange: setText, onSubmit: () => void open(text), placeholder: "waybetter://setup?\u2026", label: t('wb.setup.paste'), button: t('wb.setup.preview'), disabled: !text.trim(), mono: true }), _jsx(Button, { onClick: () => file.current?.click(), children: t('wb.setup.openFile') }), _jsx("input", { ref: file, type: "file", hidden: true, accept: "application/json,.json", onChange: event => { void event.target.files?.[0]?.text().then(open); event.target.value = ''; } })] }) }), preview && _jsx(Row, { label: t('wb.setup.changes'), wide: true, children: _jsxs("div", { className: "wbf-ask", role: "alertdialog", "aria-label": t('wb.setup.changes'), children: [preview.plan.same && _jsx("small", { children: t('wb.setup.same') }), preview.plan.theme && _jsx("small", { children: t('wb.setup.theme', { from: themeName(preview.plan.theme.from), to: themeName(preview.plan.theme.to) }) }), preview.plan.accent && _jsx("small", { children: t('wb.setup.accent', { to: preview.plan.accent.to ?? t('wb.setup.accentTheme') }) }), preview.plan.icons && _jsx("small", { children: t('wb.setup.icons', { to: iconName(preview.plan.icons.to) }) }), preview.plan.mode && _jsx("small", { children: t('wb.setup.mode', { from: modeName(preview.plan.mode.from), to: modeName(preview.plan.mode.to) }) }), preview.plan.on.length > 0 && _jsx("small", { children: t('wb.modes.turnsOn', { list: names(preview.plan.on) }) }), preview.plan.off.length > 0 && _jsx("small", { children: t('wb.modes.turnsOff', { list: names(preview.plan.off) }) }), preview.plan.missingTheme && _jsx("small", { children: t('wb.setup.missingTheme', { id: preview.plan.missingTheme }) }), preview.plan.missingIcons && _jsx("small", { children: t('wb.setup.missingIcons', { id: preview.plan.missingIcons }) }), preview.dropped.length > 0 && _jsx("small", { children: t('wb.setup.dropped', { list: preview.dropped.join(', ') }) }), !preview.plan.same && _jsx(Button, { size: "sm", tone: "primary", disabled: busy, onClick: () => void apply(), children: t('wb.setup.apply') }), _jsx(Button, { size: "sm", tone: "ghost", onClick: () => setPreview(null), children: t('wb.common.cancel') })] }) })] });
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=ShareSetup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShareSetup.js","sourceRoot":"","sources":["../../../../../../shell/src/panel/ShareSetup.tsx"],"names":[],"mappings":"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","sourcesContent":["// Settings → Backup & sync → \"Share my setup\" (setupModel.ts): the look, the mode and which\n// features are on, as one `waybetter://setup` link or one small file, and the way back in: paste a\n// link, open a file, or open a link from outside (the host forwards its text, `setup-link`). Nothing\n// is applied before the owner has seen \"What will change\" and pressed Apply. The section shows only\n// when the app names itself (`PanelSite.setup`).\nimport { useEffect, useRef, useState } from 'react';\nimport { modeById, normalizeFlags, sparseFlags, type Flags } from '../../../runtime/src/featureTable.ts';\nimport type { ThemeChoice } from '../../../runtime/src/themeChoice.ts';\nimport { invoke } from '../frame/bridge.ts';\nimport { useT } from '../frame/hooks.ts';\nimport { tx } from '../frame/i18n.ts';\nimport { Button, InputRow, Row, Section } from './controls.tsx';\nimport { flagName, rememberModeState, type ModeState } from './Modes.tsx';\nimport { featureTable, flagsChanged } from './PluginFeatures.tsx';\nimport { SETUP_FILE_SUFFIX, applySetup, makeSetup, readSetup, setupFile, setupLink, setupPlan, type ReadSetup, type SetupPlan, type SetupState, type SharedSetup } from './setupModel.ts';\nimport { panelSite, type Notify } from './site.ts';\n\n/** The event the shell raises when the host hands it a `waybetter://setup` link opened from outside. */\nexport const SETUP_LINK_EVENT = 'wb-setup-link';\nlet pendingLink: string | null = null;\n\n/** Keeps a link opened from outside until the section is on screen to preview it. */\nexport function receiveSetupLink(text: string) {\n pendingLink = text;\n window.dispatchEvent(new CustomEvent(SETUP_LINK_EVENT));\n}\n\nasync function currentState(): Promise<SetupState> {\n const site = panelSite();\n const table = featureTable()!;\n await site.themes?.store.loadThemes();\n const choice = site.themes?.store.themeState().choice as ThemeChoice | undefined;\n const flags = normalizeFlags(table, await invoke<unknown>('features_get'));\n const mode = table.modes.length ? (await invoke<ModeState>('mode_get')).mode : null;\n return { ...(choice ? { theme: { preset: choice.preset, accent: choice.accent, ...(choice.icons ? { icons: choice.icons } : {}) } } : {}), mode, flags };\n}\n\nfunction download(name: string, text: string) {\n const url = URL.createObjectURL(new Blob([text], { type: 'application/json' }));\n const anchor = document.createElement('a');\n anchor.href = url; anchor.download = name; anchor.click();\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\ntype Preview = { setup: SharedSetup; plan: SetupPlan; dropped: string[]; now: SetupState };\n\nexport function ShareSetup({ notify }: { notify: Notify }) {\n const site = panelSite();\n return site.setup && featureTable() ? <ShareSetupOf app={site.setup.app} notify={notify}/> : null;\n}\n\nfunction ShareSetupOf({ app, notify }: { app: string; notify: Notify }) {\n const t = useT();\n const site = panelSite();\n const table = featureTable()!;\n const [text, setText] = useState('');\n const [preview, setPreview] = useState<Preview | null>(null);\n const [busy, setBusy] = useState(false);\n const file = useRef<HTMLInputElement>(null);\n const store = site.themes?.store;\n\n const error = (read: Extract<ReadSetup, { ok: false }>) => read.error === 'app'\n ? (read.app ? t('wb.setup.err.app', { app: read.app }) : t('wb.setup.err.appUnknown'))\n : t(`wb.setup.err.${read.error}`);\n\n async function open(input: string) {\n const read = readSetup(input, app, table);\n if (!read.ok) { notify(error(read), true); return; }\n try {\n const now = await currentState();\n const themes = new Set(store?.allThemes().map(theme => theme.id) ?? []);\n const packs = new Set(site.themes?.iconPacks?.map(pack => pack.id) ?? []);\n setPreview({ setup: read.setup, plan: setupPlan(table, now, read.setup, themes, packs), dropped: read.dropped, now });\n } catch (err) { notify(err instanceof Error ? err.message : String(err), true); }\n }\n\n // A link opened from outside (the host's `setup-link` event, forwarded by the shell).\n useEffect(() => {\n const take = () => { const link = pendingLink; pendingLink = null; if (link) void open(link); };\n take();\n window.addEventListener(SETUP_LINK_EVENT, take);\n return () => window.removeEventListener(SETUP_LINK_EVENT, take);\n }, []);\n\n async function share(kind: 'link' | 'file') {\n try {\n const setup = makeSetup(app, table, await currentState());\n if (kind === 'file') { download(`setup${SETUP_FILE_SUFFIX}`, setupFile(setup)); return; }\n await navigator.clipboard.writeText(setupLink(setup)).then(() => notify(t('wb.setup.linkCopied')), () => notify(t('wb.common.clipboardUnavailable'), true));\n } catch (err) { notify(err instanceof Error ? err.message : String(err), true); }\n }\n\n async function apply() {\n if (!preview) return;\n setBusy(true);\n try {\n const { plan, now } = preview;\n const next = applySetup(table, now, plan);\n if (plan.on.length || plan.off.length) {\n await invoke('features_set', { features: sparseFlags(table, next.flags as Flags) });\n flagsChanged();\n }\n if (store && next.theme && (plan.theme || plan.accent || plan.icons)) {\n const choice = store.themeState().choice as ThemeChoice;\n const { icons: _icons, ...rest } = choice;\n await store.setChoice({ ...rest, preset: next.theme.preset, accent: next.theme.accent, ...(plan.theme ? { overrides: {} } : {}), ...(next.theme.icons ? { icons: next.theme.icons } : {}) });\n }\n if (plan.mode) rememberModeState(await invoke<ModeState>('mode_set', { mode: next.mode, onboarded: true, preset: modeById(table, next.mode)?.flags ?? {} }));\n setPreview(null);\n setText('');\n notify(t('wb.setup.applied'));\n } catch (err) { notify(err instanceof Error ? err.message : String(err), true); }\n finally { setBusy(false); }\n }\n\n const themeName = (id: string) => store?.themeName(id) ?? id;\n const iconName = (id: string | undefined) => id === undefined ? t('wb.appearance.icons.theme') : id === 'site' ? t('wb.appearance.icons.site') : site.themes?.iconPacks?.find(pack => pack.id === id)?.name ?? id;\n const modeName = (id: string | null) => id ? tx(modeById(table, id)?.name ?? id, undefined, id) : t('wb.setup.modeNone');\n const names = (ids: readonly string[]) => ids.map(id => flagName(table, id)).join(', ');\n\n return <Section title={t('wb.setup.title')} hint={t('wb.setup.hint')} id=\"setup\">\n <Row label={t('wb.setup.shareLabel')} hint={t('wb.setup.private')} keywords=\"share setup link export copy send friend\">\n <div className=\"bt-inline\">\n <Button tone=\"primary\" onClick={() => void share('link')}>{t('wb.setup.copyLink')}</Button>\n <Button onClick={() => void share('file')}>{t('wb.setup.saveFile')}</Button>\n </div>\n </Row>\n <Row label={t('wb.setup.useLabel')} hint={t('wb.setup.useHint')} keywords=\"import setup link apply\" wide>\n <div className=\"bt-inline\">\n <InputRow value={text} onChange={setText} onSubmit={() => void open(text)} placeholder=\"waybetter://setup?…\" label={t('wb.setup.paste')} button={t('wb.setup.preview')} disabled={!text.trim()} mono/>\n <Button onClick={() => file.current?.click()}>{t('wb.setup.openFile')}</Button>\n <input ref={file} type=\"file\" hidden accept=\"application/json,.json\" onChange={event => { void event.target.files?.[0]?.text().then(open); event.target.value = ''; }}/>\n </div>\n </Row>\n {preview && <Row label={t('wb.setup.changes')} wide>\n <div className=\"wbf-ask\" role=\"alertdialog\" aria-label={t('wb.setup.changes')}>\n {preview.plan.same && <small>{t('wb.setup.same')}</small>}\n {preview.plan.theme && <small>{t('wb.setup.theme', { from: themeName(preview.plan.theme.from), to: themeName(preview.plan.theme.to) })}</small>}\n {preview.plan.accent && <small>{t('wb.setup.accent', { to: preview.plan.accent.to ?? t('wb.setup.accentTheme') })}</small>}\n {preview.plan.icons && <small>{t('wb.setup.icons', { to: iconName(preview.plan.icons.to) })}</small>}\n {preview.plan.mode && <small>{t('wb.setup.mode', { from: modeName(preview.plan.mode.from), to: modeName(preview.plan.mode.to) })}</small>}\n {preview.plan.on.length > 0 && <small>{t('wb.modes.turnsOn', { list: names(preview.plan.on) })}</small>}\n {preview.plan.off.length > 0 && <small>{t('wb.modes.turnsOff', { list: names(preview.plan.off) })}</small>}\n {preview.plan.missingTheme && <small>{t('wb.setup.missingTheme', { id: preview.plan.missingTheme })}</small>}\n {preview.plan.missingIcons && <small>{t('wb.setup.missingIcons', { id: preview.plan.missingIcons })}</small>}\n {preview.dropped.length > 0 && <small>{t('wb.setup.dropped', { list: preview.dropped.join(', ') })}</small>}\n {!preview.plan.same && <Button size=\"sm\" tone=\"primary\" disabled={busy} onClick={() => void apply()}>{t('wb.setup.apply')}</Button>}\n <Button size=\"sm\" tone=\"ghost\" onClick={() => setPreview(null)}>{t('wb.common.cancel')}</Button>\n </div>\n </Row>}\n </Section>;\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// The wayBetter Store section: the Store view (StoreView.tsx) with the panel's own controls, the
|
|
3
|
+
// app's built-in and own themes, and removal of installed items. Native code verifies the catalog's
|
|
4
|
+
// Ed25519 signature, keeps the app's items, then checks each download's sha256. Installing always
|
|
5
|
+
// goes through the shell's install screen (InstallDialog), never straight from here.
|
|
6
|
+
import { invoke } from "../frame/bridge.js";
|
|
7
|
+
import { useT } from "../frame/hooks.js";
|
|
8
|
+
import { relativeTime } from "../frame/i18n.js";
|
|
9
|
+
import { site } from "../frame/site.js";
|
|
10
|
+
import { StoreView } from "../StoreView.js";
|
|
11
|
+
import '../store.css';
|
|
12
|
+
import { Badge, Button, EmptyState, SearchField, Segmented, useSettingsQuery } from "./controls.js";
|
|
13
|
+
import { panelSite, panelThemes } from "./site.js";
|
|
14
|
+
import { useThemeState } from "./themes.js";
|
|
15
|
+
import './plugins.css';
|
|
16
|
+
const CONTROLS = { Button, SearchField, Segmented, EmptyState, Badge };
|
|
17
|
+
const RELOAD_ON = ['bt-plugins-changed', 'bt-store-changed', 'bt-strings-changed'];
|
|
18
|
+
export function StoreSection({ notify }) {
|
|
19
|
+
const t = useT();
|
|
20
|
+
const { store, catalog } = panelThemes();
|
|
21
|
+
const themes = useThemeState();
|
|
22
|
+
const settingsQuery = useSettingsQuery();
|
|
23
|
+
if (settingsQuery)
|
|
24
|
+
return null;
|
|
25
|
+
const userThemes = themes.choice.themes.map(theme => theme.id);
|
|
26
|
+
async function remove(item, view) {
|
|
27
|
+
if (item.kind === 'strings') {
|
|
28
|
+
await invoke('strings_remove', { id: item.id });
|
|
29
|
+
window.dispatchEvent(new CustomEvent('bt-strings-changed'));
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (item.kind === 'plugin' && item.file) {
|
|
33
|
+
await invoke('plugin_remove', { file: item.file });
|
|
34
|
+
window.dispatchEvent(new CustomEvent('bt-plugins-changed'));
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
const record = Object.entries(view.records.items).find(([key, value]) => key.startsWith('theme:') && value.storeId === item.id && userThemes.includes(key.slice(6)));
|
|
38
|
+
const id = record?.[0].slice(6);
|
|
39
|
+
if (!id)
|
|
40
|
+
return false;
|
|
41
|
+
const choice = themes.choice;
|
|
42
|
+
await store.setChoice({ ...choice, themes: choice.themes.filter(theme => theme.id !== id), ...(choice.preset === id ? { preset: catalog.defaultChoice.preset, overrides: {}, accent: null } : {}) });
|
|
43
|
+
await invoke('install_forget', { kind: 'theme', name: id });
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return _jsx(StoreView, { t: t, site: site().name, load: refresh => invoke('store_catalog', { refresh }), controller: panelSite().store.controller, builtinThemes: catalog.builtins.map(theme => theme.id), userThemes: userThemes, onRemove: remove, onUse: item => store.setChoice({ ...themes.choice, preset: item.id, overrides: {}, accent: null }), notify: notify, relativeTime: relativeTime, reloadOn: RELOAD_ON, controls: CONTROLS });
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=StoreSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoreSection.js","sourceRoot":"","sources":["../../../../../../shell/src/panel/StoreSection.tsx"],"names":[],"mappings":"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","sourcesContent":["// The wayBetter Store section: the Store view (StoreView.tsx) with the panel's own controls, the\n// app's built-in and own themes, and removal of installed items. Native code verifies the catalog's\n// Ed25519 signature, keeps the app's items, then checks each download's sha256. Installing always\n// goes through the shell's install screen (InstallDialog), never straight from here.\nimport { invoke } from '../frame/bridge.ts';\nimport { useT } from '../frame/hooks.ts';\nimport { relativeTime } from '../frame/i18n.ts';\nimport { site } from '../frame/site.ts';\nimport type { StoreCatalogView, StoreItem } from '../storeModel.ts';\nimport { StoreView, type StoreControls } from '../StoreView.tsx';\nimport '../store.css';\nimport { Badge, Button, EmptyState, SearchField, Segmented, useSettingsQuery } from './controls.tsx';\nimport { panelSite, panelThemes, type Notify } from './site.ts';\nimport { useThemeState } from './themes.ts';\nimport './plugins.css';\n\nconst CONTROLS: Partial<StoreControls> = { Button, SearchField, Segmented, EmptyState, Badge };\nconst RELOAD_ON = ['bt-plugins-changed', 'bt-store-changed', 'bt-strings-changed'];\n\nexport function StoreSection({ notify }: { notify: Notify }) {\n const t = useT();\n const { store, catalog } = panelThemes();\n const themes = useThemeState();\n const settingsQuery = useSettingsQuery();\n if (settingsQuery) return null;\n const userThemes = themes.choice.themes.map(theme => theme.id);\n\n async function remove(item: StoreItem, view: StoreCatalogView): Promise<boolean> {\n if (item.kind === 'strings') {\n await invoke('strings_remove', { id: item.id });\n window.dispatchEvent(new CustomEvent('bt-strings-changed'));\n return true;\n }\n if (item.kind === 'plugin' && item.file) {\n await invoke('plugin_remove', { file: item.file });\n window.dispatchEvent(new CustomEvent('bt-plugins-changed'));\n return true;\n }\n const record = Object.entries(view.records.items).find(([key, value]) => key.startsWith('theme:') && value.storeId === item.id && userThemes.includes(key.slice(6)));\n const id = record?.[0].slice(6);\n if (!id) return false;\n const choice = themes.choice;\n await store.setChoice({ ...choice, themes: choice.themes.filter(theme => theme.id !== id), ...(choice.preset === id ? { preset: catalog.defaultChoice.preset, overrides: {}, accent: null } : {}) });\n await invoke('install_forget', { kind: 'theme', name: id });\n return true;\n }\n\n return <StoreView\n t={t}\n site={site().name}\n load={refresh => invoke<StoreCatalogView>('store_catalog', { refresh })}\n controller={panelSite().store!.controller}\n builtinThemes={catalog.builtins.map(theme => theme.id)}\n userThemes={userThemes}\n onRemove={remove}\n onUse={item => store.setChoice({ ...themes.choice, preset: item.id, overrides: {}, accent: null })}\n notify={notify}\n relativeTime={relativeTime}\n reloadOn={RELOAD_ON}\n controls={CONTROLS}\n />;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PageInventory } from '../../runtime/stringPacks.ts';
|
|
2
|
+
import { type InstalledStrings } from '../stringPacksModel.ts';
|
|
3
|
+
import type { PanelSection } from './site.ts';
|
|
4
|
+
import './strings.css';
|
|
5
|
+
/** Hands the site tabs the packs that are on (in `packs` order). */
|
|
6
|
+
export declare function applyStringPacks(inventory: PageInventory, packs?: InstalledStrings[]): Promise<void>;
|
|
7
|
+
export declare function createStringPacksSection(options: {
|
|
8
|
+
inventory: PageInventory;
|
|
9
|
+
id?: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
}): PanelSection;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// The Languages section: the installed string packs (waybetter/docs/STRING-PACKS.md), which are on
|
|
3
|
+
// and in which order, with each pack's coverage against the site's inventory. Every change hands the
|
|
4
|
+
// site tabs the new delivery (`strings_apply`: the packs that are on, in order, with the inventory
|
|
5
|
+
// slice their keys need); the tabs reload to show it. Packs are data only: nothing here runs code.
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
import { invoke } from "../frame/bridge.js";
|
|
8
|
+
import { useT } from "../frame/hooks.js";
|
|
9
|
+
import { t as translate } from "../frame/i18n.js";
|
|
10
|
+
import { site } from "../frame/site.js";
|
|
11
|
+
import { buildDelivery, coveragePercent, packSummary } from "../stringPacksModel.js";
|
|
12
|
+
import { Badge, Button, EmptyState, Section, Toggle, useSettingsQuery } from "./controls.js";
|
|
13
|
+
import './strings.css';
|
|
14
|
+
const MAX_ON = 8;
|
|
15
|
+
const TRANSLATE_KEY = 'wb.strings.translate';
|
|
16
|
+
const translating = () => { try {
|
|
17
|
+
return localStorage.getItem(TRANSLATE_KEY) === '1';
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
} };
|
|
22
|
+
const ICON = 'M2.5 3.5h7M6 2v1.5M8 3.5c-.6 2.9-2.6 5-5.4 6.1M4.6 6.2c.8 1.2 2 2.2 3.4 2.9M9.5 14l3-7 3 7M10.6 11.8h3.8';
|
|
23
|
+
/** Hands the site tabs the packs that are on (in `packs` order). */
|
|
24
|
+
export async function applyStringPacks(inventory, packs) {
|
|
25
|
+
const list = packs ?? (await invoke('strings_list')).packs;
|
|
26
|
+
const texts = [];
|
|
27
|
+
for (const pack of list)
|
|
28
|
+
if (pack.enabled)
|
|
29
|
+
texts.push(await invoke('strings_read', { id: pack.id }));
|
|
30
|
+
await invoke('strings_apply', { delivery: buildDelivery(texts, inventory, { translate: translating() }) });
|
|
31
|
+
}
|
|
32
|
+
export function createStringPacksSection(options) {
|
|
33
|
+
const { inventory } = options;
|
|
34
|
+
function StringPacks({ notify, openSection }) {
|
|
35
|
+
const t = useT();
|
|
36
|
+
const query = useSettingsQuery();
|
|
37
|
+
const name = site().name;
|
|
38
|
+
const [rows, setRows] = useState(null);
|
|
39
|
+
const [busy, setBusy] = useState(false);
|
|
40
|
+
const [translate, setTranslate] = useState(translating);
|
|
41
|
+
const load = async () => {
|
|
42
|
+
const { packs } = await invoke('strings_list');
|
|
43
|
+
const next = [];
|
|
44
|
+
for (const pack of packs) {
|
|
45
|
+
const text = await invoke('strings_read', { id: pack.id }).catch(() => '');
|
|
46
|
+
next.push({ ...pack, summary: text ? packSummary(text, inventory) : null });
|
|
47
|
+
}
|
|
48
|
+
setRows(next);
|
|
49
|
+
};
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
void load().catch(error => notify(String(error), true));
|
|
52
|
+
const reload = () => void load().catch(() => { });
|
|
53
|
+
window.addEventListener('bt-strings-changed', reload);
|
|
54
|
+
return () => window.removeEventListener('bt-strings-changed', reload);
|
|
55
|
+
}, []);
|
|
56
|
+
/** Saves order and on/off, then hands the tabs the new delivery. */
|
|
57
|
+
const save = async (next) => {
|
|
58
|
+
setBusy(true);
|
|
59
|
+
try {
|
|
60
|
+
const { packs } = await invoke('strings_set', { order: next.map(row => row.id), enabled: next.filter(row => row.enabled).map(row => row.id) });
|
|
61
|
+
await applyStringPacks(inventory, packs);
|
|
62
|
+
setRows(packs.map(pack => ({ ...pack, summary: next.find(row => row.id === pack.id)?.summary ?? null })));
|
|
63
|
+
notify(t('wb.strings.applied', { site: name }));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
notify(String(error), true);
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
setBusy(false);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
if (query)
|
|
73
|
+
return null;
|
|
74
|
+
if (rows === null)
|
|
75
|
+
return null;
|
|
76
|
+
const on = rows.filter(row => row.enabled).length;
|
|
77
|
+
const move = (index, by) => {
|
|
78
|
+
const next = [...rows];
|
|
79
|
+
const [row] = next.splice(index, 1);
|
|
80
|
+
next.splice(index + by, 0, row);
|
|
81
|
+
void save(next);
|
|
82
|
+
};
|
|
83
|
+
return _jsxs(Section, { title: t('wb.strings.title'), hint: t('wb.strings.intro', { site: name }), children: [rows.length === 0
|
|
84
|
+
? _jsx(EmptyState, { title: t('wb.strings.empty'), action: _jsx(Button, { onClick: () => openSection('store'), children: t('wb.strings.openStore') }), children: t('wb.strings.emptyHint') })
|
|
85
|
+
: _jsx("ul", { className: "btp-strings", "aria-label": t('wb.strings.title'), children: rows.map((row, index) => _jsxs("li", { className: `btp-strings-row${row.enabled ? ' is-on' : ''}`, children: [_jsx(Toggle, { label: t('wb.strings.on', { name: row.name }), checked: row.enabled, disabled: busy || (!row.enabled && on >= MAX_ON), onChange: checked => void save(rows.map(item => (item.id === row.id ? { ...item, enabled: checked } : item))) }), _jsxs("div", { className: "btp-strings-text", children: [_jsxs("span", { className: "btp-strings-head", children: [_jsx("strong", { children: row.name }), _jsx(Badge, { children: row.language }), _jsx(Badge, { tone: "ok", children: t('wb.strings.dataOnly') })] }), _jsx("small", { children: [row.authors.length ? t('wb.strings.by', { authors: row.authors.join(', ') }) : '', row.version && `v${row.version}`,
|
|
86
|
+
row.summary && !row.summary.otherCatalog ? t('wb.install.strings.coverageValue', { translated: String(row.summary.translated), translatable: String(row.summary.translatable), site: name, percent: coveragePercent(row.summary) }) : t('wb.install.strings.count', { count: row.strings })]
|
|
87
|
+
.filter(Boolean).join(' · ') })] }), _jsx(Button, { size: "sm", label: t('wb.strings.up'), disabled: busy || index === 0, onClick: () => move(index, -1), children: "\u2191" }), _jsx(Button, { size: "sm", label: t('wb.strings.down'), disabled: busy || index === rows.length - 1, onClick: () => move(index, 1), children: "\u2193" }), _jsx(Button, { size: "sm", tone: "danger", disabled: busy, onClick: () => {
|
|
88
|
+
setBusy(true);
|
|
89
|
+
void invoke('strings_remove', { id: row.id })
|
|
90
|
+
.then(async ({ packs }) => { await applyStringPacks(inventory, packs); notify(t('wb.strings.removed', { name: row.name })); await load(); })
|
|
91
|
+
.catch(error => notify(String(error), true))
|
|
92
|
+
.finally(() => setBusy(false));
|
|
93
|
+
}, children: t('wb.strings.remove') })] }, row.id)) }), on >= MAX_ON && _jsx("p", { className: "btp-hint", children: t('wb.strings.limit') }), _jsxs("div", { className: "btp-strings-row", children: [_jsx(Toggle, { label: t('wb.strings.translate'), checked: translate, disabled: busy, onChange: checked => {
|
|
94
|
+
try {
|
|
95
|
+
localStorage.setItem(TRANSLATE_KEY, checked ? '1' : '0');
|
|
96
|
+
}
|
|
97
|
+
catch { /* it still applies once */ }
|
|
98
|
+
setTranslate(checked);
|
|
99
|
+
setBusy(true);
|
|
100
|
+
void applyStringPacks(inventory).then(() => notify(t('wb.strings.applied', { site: name }))).catch(error => notify(String(error), true)).finally(() => setBusy(false));
|
|
101
|
+
} }), _jsxs("div", { className: "btp-strings-text", children: [_jsx("strong", { children: t('wb.strings.translate') }), _jsx("small", { children: t('wb.strings.translateHint', { site: name }) })] })] })] });
|
|
102
|
+
}
|
|
103
|
+
// The nav label: the plain translator (the panel calls `label()` outside React too).
|
|
104
|
+
return { id: options.id ?? 'languages', label: () => translate('wb.strings.section'), icon: options.icon ?? ICON, component: StringPacks };
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=StringPacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StringPacks.js","sourceRoot":"","sources":["../../../../../../shell/src/panel/StringPacks.tsx"],"names":[],"mappings":"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","sourcesContent":["// The Languages section: the installed string packs (waybetter/docs/STRING-PACKS.md), which are on\n// and in which order, with each pack's coverage against the site's inventory. Every change hands the\n// site tabs the new delivery (`strings_apply`: the packs that are on, in order, with the inventory\n// slice their keys need); the tabs reload to show it. Packs are data only: nothing here runs code.\nimport { useEffect, useState } from 'react';\nimport type { PageInventory } from '../../../runtime/src/stringPacks.ts';\nimport { invoke } from '../frame/bridge.ts';\nimport { useT } from '../frame/hooks.ts';\nimport { t as translate } from '../frame/i18n.ts';\nimport { site } from '../frame/site.ts';\nimport { buildDelivery, coveragePercent, packSummary, type InstalledStrings, type StringsSummary } from '../stringPacksModel.ts';\nimport { Badge, Button, EmptyState, Section, Toggle, useSettingsQuery } from './controls.tsx';\nimport type { PanelSection, SectionProps } from './site.ts';\nimport './strings.css';\n\nconst MAX_ON = 8;\nconst TRANSLATE_KEY = 'wb.strings.translate';\nconst translating = () => { try { return localStorage.getItem(TRANSLATE_KEY) === '1'; } catch { return false; } };\nconst ICON = 'M2.5 3.5h7M6 2v1.5M8 3.5c-.6 2.9-2.6 5-5.4 6.1M4.6 6.2c.8 1.2 2 2.2 3.4 2.9M9.5 14l3-7 3 7M10.6 11.8h3.8';\n\ntype Row = InstalledStrings & { summary: StringsSummary | null };\n\n/** Hands the site tabs the packs that are on (in `packs` order). */\nexport async function applyStringPacks(inventory: PageInventory, packs?: InstalledStrings[]): Promise<void> {\n const list = packs ?? (await invoke<{ packs: InstalledStrings[] }>('strings_list')).packs;\n const texts: string[] = [];\n for (const pack of list) if (pack.enabled) texts.push(await invoke<string>('strings_read', { id: pack.id }));\n await invoke('strings_apply', { delivery: buildDelivery(texts, inventory, { translate: translating() }) });\n}\n\nexport function createStringPacksSection(options: { inventory: PageInventory; id?: string; icon?: string }): PanelSection {\n const { inventory } = options;\n\n function StringPacks({ notify, openSection }: SectionProps) {\n const t = useT();\n const query = useSettingsQuery();\n const name = site().name;\n const [rows, setRows] = useState<Row[] | null>(null);\n const [busy, setBusy] = useState(false);\n const [translate, setTranslate] = useState(translating);\n\n const load = async () => {\n const { packs } = await invoke<{ packs: InstalledStrings[] }>('strings_list');\n const next: Row[] = [];\n for (const pack of packs) {\n const text = await invoke<string>('strings_read', { id: pack.id }).catch(() => '');\n next.push({ ...pack, summary: text ? packSummary(text, inventory) : null });\n }\n setRows(next);\n };\n useEffect(() => {\n void load().catch(error => notify(String(error), true));\n const reload = () => void load().catch(() => {});\n window.addEventListener('bt-strings-changed', reload);\n return () => window.removeEventListener('bt-strings-changed', reload);\n }, []);\n\n /** Saves order and on/off, then hands the tabs the new delivery. */\n const save = async (next: Row[]) => {\n setBusy(true);\n try {\n const { packs } = await invoke<{ packs: InstalledStrings[] }>('strings_set', { order: next.map(row => row.id), enabled: next.filter(row => row.enabled).map(row => row.id) });\n await applyStringPacks(inventory, packs);\n setRows(packs.map(pack => ({ ...pack, summary: next.find(row => row.id === pack.id)?.summary ?? null })));\n notify(t('wb.strings.applied', { site: name }));\n } catch (error) {\n notify(String(error), true);\n } finally {\n setBusy(false);\n }\n };\n\n if (query) return null;\n if (rows === null) return null;\n const on = rows.filter(row => row.enabled).length;\n const move = (index: number, by: number) => {\n const next = [...rows];\n const [row] = next.splice(index, 1);\n next.splice(index + by, 0, row!);\n void save(next);\n };\n\n return <Section title={t('wb.strings.title')} hint={t('wb.strings.intro', { site: name })}>\n {rows.length === 0\n ? <EmptyState title={t('wb.strings.empty')} action={<Button onClick={() => openSection('store')}>{t('wb.strings.openStore')}</Button>}>{t('wb.strings.emptyHint')}</EmptyState>\n : <ul className=\"btp-strings\" aria-label={t('wb.strings.title')}>\n {rows.map((row, index) => <li key={row.id} className={`btp-strings-row${row.enabled ? ' is-on' : ''}`}>\n <Toggle label={t('wb.strings.on', { name: row.name })} checked={row.enabled} disabled={busy || (!row.enabled && on >= MAX_ON)}\n onChange={checked => void save(rows.map(item => (item.id === row.id ? { ...item, enabled: checked } : item)))}/>\n <div className=\"btp-strings-text\">\n <span className=\"btp-strings-head\"><strong>{row.name}</strong><Badge>{row.language}</Badge><Badge tone=\"ok\">{t('wb.strings.dataOnly')}</Badge></span>\n <small>\n {[row.authors.length ? t('wb.strings.by', { authors: row.authors.join(', ') }) : '', row.version && `v${row.version}`,\n row.summary && !row.summary.otherCatalog ? t('wb.install.strings.coverageValue', { translated: String(row.summary.translated), translatable: String(row.summary.translatable), site: name, percent: coveragePercent(row.summary) }) : t('wb.install.strings.count', { count: row.strings })]\n .filter(Boolean).join(' · ')}\n </small>\n </div>\n <Button size=\"sm\" label={t('wb.strings.up')} disabled={busy || index === 0} onClick={() => move(index, -1)}>↑</Button>\n <Button size=\"sm\" label={t('wb.strings.down')} disabled={busy || index === rows.length - 1} onClick={() => move(index, 1)}>↓</Button>\n <Button size=\"sm\" tone=\"danger\" disabled={busy} onClick={() => {\n setBusy(true);\n void invoke<{ packs: InstalledStrings[] }>('strings_remove', { id: row.id })\n .then(async ({ packs }) => { await applyStringPacks(inventory, packs); notify(t('wb.strings.removed', { name: row.name })); await load(); })\n .catch(error => notify(String(error), true))\n .finally(() => setBusy(false));\n }}>{t('wb.strings.remove')}</Button>\n </li>)}\n </ul>}\n {on >= MAX_ON && <p className=\"btp-hint\">{t('wb.strings.limit')}</p>}\n <div className=\"btp-strings-row\">\n <Toggle label={t('wb.strings.translate')} checked={translate} disabled={busy} onChange={checked => {\n try { localStorage.setItem(TRANSLATE_KEY, checked ? '1' : '0'); } catch { /* it still applies once */ }\n setTranslate(checked);\n setBusy(true);\n void applyStringPacks(inventory).then(() => notify(t('wb.strings.applied', { site: name }))).catch(error => notify(String(error), true)).finally(() => setBusy(false));\n }}/>\n <div className=\"btp-strings-text\"><strong>{t('wb.strings.translate')}</strong><small>{t('wb.strings.translateHint', { site: name })}</small></div>\n </div>\n </Section>;\n }\n\n // The nav label: the plain translator (the panel calls `label()` outside React too).\n return { id: options.id ?? 'languages', label: () => translate('wb.strings.section'), icon: options.icon ?? ICON, component: StringPacks };\n}\n"]}
|