waybetter 0.1.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -6
- package/bin/waybetter.mjs +43 -0
- package/build/runtime.mjs +105 -0
- package/cli/buildenv.mjs +130 -0
- package/cli/dev.mjs +97 -0
- package/cli/i18n.mjs +382 -0
- package/cli/manifests.mjs +383 -0
- package/cli/release.mjs +437 -0
- package/cli/releasecheck.mjs +15 -0
- package/cli/remote.mjs +260 -0
- package/cli/store.mjs +378 -0
- package/cli/strings.mjs +123 -0
- package/dist/runtime/column.d.ts +93 -0
- package/dist/runtime/column.js +451 -0
- package/dist/runtime/column.js.map +1 -0
- package/dist/runtime/commandReport.d.ts +19 -0
- package/dist/runtime/commandReport.js +47 -0
- package/dist/runtime/commandReport.js.map +1 -0
- package/dist/runtime/commands.d.ts +72 -0
- package/dist/runtime/commands.js +214 -0
- package/dist/runtime/commands.js.map +1 -0
- package/dist/runtime/devErrors.d.ts +16 -0
- package/dist/runtime/devErrors.js +30 -0
- package/dist/runtime/devErrors.js.map +1 -0
- package/dist/runtime/domHub.d.ts +21 -0
- package/dist/runtime/domHub.js +139 -4
- package/dist/runtime/domHub.js.map +1 -1
- package/dist/runtime/featureModules.d.ts +157 -0
- package/dist/runtime/featureModules.js +484 -0
- package/dist/runtime/featureModules.js.map +1 -0
- package/dist/runtime/featureTable.d.ts +144 -0
- package/dist/runtime/featureTable.js +223 -0
- package/dist/runtime/featureTable.js.map +1 -0
- package/dist/runtime/fonts.d.ts +22 -0
- package/dist/runtime/fonts.js +72 -0
- package/dist/runtime/fonts.js.map +1 -0
- package/dist/runtime/i18n.d.ts +138 -0
- package/dist/runtime/i18n.js +614 -0
- package/dist/runtime/i18n.js.map +1 -0
- package/dist/runtime/i18nCheck.d.ts +30 -0
- package/dist/runtime/i18nCheck.js +224 -0
- package/dist/runtime/i18nCheck.js.map +1 -0
- package/dist/runtime/pageLang.d.ts +8 -0
- package/dist/runtime/pageLang.js +64 -0
- package/dist/runtime/pageLang.js.map +1 -0
- package/dist/runtime/pageStrings.d.ts +35 -0
- package/dist/runtime/pageStrings.js +76 -0
- package/dist/runtime/pageStrings.js.map +1 -0
- package/dist/runtime/panelButton.d.ts +33 -0
- package/dist/runtime/panelButton.js +83 -0
- package/dist/runtime/panelButton.js.map +1 -0
- package/dist/runtime/permissions.d.ts +47 -0
- package/dist/runtime/permissions.js +141 -0
- package/dist/runtime/permissions.js.map +1 -0
- package/dist/runtime/pluginApi.d.ts +51 -2
- package/dist/runtime/pluginApi.js +137 -22
- package/dist/runtime/pluginApi.js.map +1 -1
- package/dist/runtime/pluginI18n.d.ts +29 -0
- package/dist/runtime/pluginI18n.js +119 -0
- package/dist/runtime/pluginI18n.js.map +1 -0
- package/dist/runtime/settingsForm.d.ts +66 -0
- package/dist/runtime/settingsForm.js +114 -0
- package/dist/runtime/settingsForm.js.map +1 -0
- package/dist/runtime/site.d.ts +5 -0
- package/dist/runtime/site.js +1 -1
- package/dist/runtime/site.js.map +1 -1
- package/dist/runtime/siteRuntime.d.ts +144 -0
- package/dist/runtime/siteRuntime.js +175 -0
- package/dist/runtime/siteRuntime.js.map +1 -0
- package/dist/runtime/stringPackHook.d.ts +84 -0
- package/dist/runtime/stringPackHook.js +220 -0
- package/dist/runtime/stringPackHook.js.map +1 -0
- package/dist/runtime/stringPackTranslate.d.ts +61 -0
- package/dist/runtime/stringPackTranslate.js +267 -0
- package/dist/runtime/stringPackTranslate.js.map +1 -0
- package/dist/runtime/stringPacks.d.ts +248 -0
- package/dist/runtime/stringPacks.js +610 -0
- package/dist/runtime/stringPacks.js.map +1 -0
- package/dist/runtime/styles.d.ts +15 -0
- package/dist/runtime/styles.js +81 -0
- package/dist/runtime/styles.js.map +1 -0
- package/dist/runtime/textNet.d.ts +15 -0
- package/dist/runtime/textNet.js +131 -0
- package/dist/runtime/textNet.js.map +1 -0
- package/dist/runtime/themeApi.d.ts +121 -0
- package/dist/runtime/themeApi.js +220 -0
- package/dist/runtime/themeApi.js.map +1 -0
- package/dist/runtime/themeBar.d.ts +28 -0
- package/dist/runtime/themeBar.js +82 -0
- package/dist/runtime/themeBar.js.map +1 -0
- package/dist/runtime/themeChoice.d.ts +52 -0
- package/dist/runtime/themeChoice.js +69 -0
- package/dist/runtime/themeChoice.js.map +1 -0
- package/dist/runtime/themeComponents.d.ts +435 -0
- package/dist/runtime/themeComponents.js +476 -0
- package/dist/runtime/themeComponents.js.map +1 -0
- package/dist/runtime/themeDecor.d.ts +73 -0
- package/dist/runtime/themeDecor.js +133 -0
- package/dist/runtime/themeDecor.js.map +1 -0
- package/dist/runtime/themeDerive.d.ts +69 -0
- package/dist/runtime/themeDerive.js +117 -0
- package/dist/runtime/themeDerive.js.map +1 -0
- package/dist/runtime/themeDoctor.d.ts +52 -0
- package/dist/runtime/themeDoctor.js +112 -0
- package/dist/runtime/themeDoctor.js.map +1 -0
- package/dist/runtime/themeGuards.d.ts +60 -0
- package/dist/runtime/themeGuards.js +235 -0
- package/dist/runtime/themeGuards.js.map +1 -0
- package/dist/runtime/themeIcons.d.ts +130 -0
- package/dist/runtime/themeIcons.js +271 -0
- package/dist/runtime/themeIcons.js.map +1 -0
- package/dist/runtime/themeInk.d.ts +37 -0
- package/dist/runtime/themeInk.js +111 -0
- package/dist/runtime/themeInk.js.map +1 -0
- package/dist/runtime/themeInvariants.d.ts +39 -0
- package/dist/runtime/themeInvariants.js +273 -0
- package/dist/runtime/themeInvariants.js.map +1 -0
- package/dist/runtime/themeRuntime.d.ts +114 -0
- package/dist/runtime/themeRuntime.js +570 -0
- package/dist/runtime/themeRuntime.js.map +1 -0
- package/dist/runtime/themeSchema.d.ts +10 -0
- package/dist/runtime/themeSchema.js +150 -0
- package/dist/runtime/themeSchema.js.map +1 -0
- package/dist/runtime/themeSpec.d.ts +263 -0
- package/dist/runtime/themeSpec.js +435 -0
- package/dist/runtime/themeSpec.js.map +1 -0
- package/dist/runtime/themeTokens.d.ts +12 -0
- package/dist/runtime/themeTokens.js +14 -6
- package/dist/runtime/themeTokens.js.map +1 -1
- package/dist/runtime/themeVariants.d.ts +30 -0
- package/dist/runtime/themeVariants.js +53 -0
- package/dist/runtime/themeVariants.js.map +1 -0
- package/dist/runtime/ui.d.ts +192 -0
- package/dist/runtime/ui.js +926 -0
- package/dist/runtime/ui.js.map +1 -0
- package/dist/shell/InstallDialog.d.ts +40 -0
- package/dist/shell/InstallDialog.js +176 -0
- package/dist/shell/InstallDialog.js.map +1 -0
- package/dist/shell/StoreView.d.ts +78 -0
- package/dist/shell/StoreView.js +108 -0
- package/dist/shell/StoreView.js.map +1 -0
- package/dist/shell/addressModel.js.map +1 -1
- package/dist/shell/chrome.js.map +1 -1
- package/dist/shell/deckModel.d.ts +117 -0
- package/dist/shell/deckModel.js +267 -0
- package/dist/shell/deckModel.js.map +1 -0
- package/dist/shell/frame/AccountSwitcher.d.ts +42 -0
- package/dist/shell/frame/AccountSwitcher.js +243 -0
- package/dist/shell/frame/AccountSwitcher.js.map +1 -0
- package/dist/shell/frame/AddColumnDialog.d.ts +8 -0
- package/dist/shell/frame/AddColumnDialog.js +81 -0
- package/dist/shell/frame/AddColumnDialog.js.map +1 -0
- package/dist/shell/frame/AppMenu.d.ts +44 -0
- package/dist/shell/frame/AppMenu.js +721 -0
- package/dist/shell/frame/AppMenu.js.map +1 -0
- package/dist/shell/frame/CommandPalette.d.ts +7 -0
- package/dist/shell/frame/CommandPalette.js +118 -0
- package/dist/shell/frame/CommandPalette.js.map +1 -0
- package/dist/shell/frame/DeckView.d.ts +13 -0
- package/dist/shell/frame/DeckView.js +287 -0
- package/dist/shell/frame/DeckView.js.map +1 -0
- package/dist/shell/frame/FindBar.d.ts +10 -0
- package/dist/shell/frame/FindBar.js +108 -0
- package/dist/shell/frame/FindBar.js.map +1 -0
- package/dist/shell/frame/MediaPanel.d.ts +8 -0
- package/dist/shell/frame/MediaPanel.js +76 -0
- package/dist/shell/frame/MediaPanel.js.map +1 -0
- package/dist/shell/frame/NewTabPage.d.ts +9 -0
- package/dist/shell/frame/NewTabPage.js +71 -0
- package/dist/shell/frame/NewTabPage.js.map +1 -0
- package/dist/shell/frame/PaneTheme.d.ts +16 -0
- package/dist/shell/frame/PaneTheme.js +67 -0
- package/dist/shell/frame/PaneTheme.js.map +1 -0
- package/dist/shell/frame/ReportDialog.d.ts +3 -0
- package/dist/shell/frame/ReportDialog.js +95 -0
- package/dist/shell/frame/ReportDialog.js.map +1 -0
- package/dist/shell/frame/Shell.d.ts +9 -0
- package/dist/shell/frame/Shell.js +1139 -0
- package/dist/shell/frame/Shell.js.map +1 -0
- package/dist/shell/frame/Titlebar.d.ts +17 -0
- package/dist/shell/frame/Titlebar.js +79 -0
- package/dist/shell/frame/Titlebar.js.map +1 -0
- package/dist/shell/frame/TitlebarExtras.d.ts +4 -0
- package/dist/shell/frame/TitlebarExtras.js +202 -0
- package/dist/shell/frame/TitlebarExtras.js.map +1 -0
- package/dist/shell/frame/WebPanel.d.ts +4 -0
- package/dist/shell/frame/WebPanel.js +185 -0
- package/dist/shell/frame/WebPanel.js.map +1 -0
- package/dist/shell/frame/accountModel.d.ts +49 -0
- package/dist/shell/frame/accountModel.js +67 -0
- package/dist/shell/frame/accountModel.js.map +1 -0
- package/dist/shell/frame/accountSwitcher.css +91 -0
- package/dist/shell/frame/appI18n.d.ts +44 -0
- package/dist/shell/frame/appI18n.js +64 -0
- package/dist/shell/frame/appI18n.js.map +1 -0
- package/dist/shell/frame/appMenu.css +127 -0
- package/dist/shell/frame/backupModel.d.ts +38 -0
- package/dist/shell/frame/backupModel.js +192 -0
- package/dist/shell/frame/backupModel.js.map +1 -0
- package/dist/shell/frame/boot.d.ts +8 -0
- package/dist/shell/frame/boot.js +37 -0
- package/dist/shell/frame/boot.js.map +1 -0
- package/dist/shell/frame/brand.d.ts +1 -0
- package/dist/shell/frame/brand.js +4 -0
- package/dist/shell/frame/brand.js.map +1 -0
- package/dist/shell/frame/bridge.d.ts +35 -0
- package/dist/shell/frame/bridge.js +70 -0
- package/dist/shell/frame/bridge.js.map +1 -0
- package/dist/shell/frame/chromeTheme.d.ts +47 -0
- package/dist/shell/frame/chromeTheme.js +118 -0
- package/dist/shell/frame/chromeTheme.js.map +1 -0
- package/dist/shell/frame/deck.css +209 -0
- package/dist/shell/frame/deckStore.d.ts +89 -0
- package/dist/shell/frame/deckStore.js +239 -0
- package/dist/shell/frame/deckStore.js.map +1 -0
- package/dist/shell/frame/devHost.d.ts +35 -0
- package/dist/shell/frame/devHost.js +80 -0
- package/dist/shell/frame/devHost.js.map +1 -0
- package/dist/shell/frame/devModel.d.ts +7 -0
- package/dist/shell/frame/devModel.js +17 -0
- package/dist/shell/frame/devModel.js.map +1 -0
- package/dist/shell/frame/diag.d.ts +7 -0
- package/dist/shell/frame/diag.js +39 -0
- package/dist/shell/frame/diag.js.map +1 -0
- package/dist/shell/frame/edges.css +159 -0
- package/dist/shell/frame/findBar.css +14 -0
- package/dist/shell/frame/frameMessages.d.ts +248 -0
- package/dist/shell/frame/frameMessages.js +252 -0
- package/dist/shell/frame/frameMessages.js.map +1 -0
- package/dist/shell/frame/hooks.d.ts +39 -0
- package/dist/shell/frame/hooks.js +97 -0
- package/dist/shell/frame/hooks.js.map +1 -0
- package/dist/shell/frame/i18n.d.ts +27 -0
- package/dist/shell/frame/i18n.js +66 -0
- package/dist/shell/frame/i18n.js.map +1 -0
- package/dist/shell/frame/iconPack.d.ts +11 -0
- package/dist/shell/frame/iconPack.js +45 -0
- package/dist/shell/frame/iconPack.js.map +1 -0
- package/dist/shell/frame/icons.d.ts +71 -0
- package/dist/shell/frame/icons.js +75 -0
- package/dist/shell/frame/icons.js.map +1 -0
- package/dist/shell/frame/index.d.ts +34 -0
- package/dist/shell/frame/index.js +43 -0
- package/dist/shell/frame/index.js.map +1 -0
- package/dist/shell/frame/language.d.ts +58 -0
- package/dist/shell/frame/language.js +165 -0
- package/dist/shell/frame/language.js.map +1 -0
- package/dist/shell/frame/mediaModel.d.ts +47 -0
- package/dist/shell/frame/mediaModel.js +67 -0
- package/dist/shell/frame/mediaModel.js.map +1 -0
- package/dist/shell/frame/menuMessages.en.json +96 -0
- package/dist/shell/frame/menuModel.d.ts +319 -0
- package/dist/shell/frame/menuModel.js +301 -0
- package/dist/shell/frame/menuModel.js.map +1 -0
- package/dist/shell/frame/nativeMenu.d.ts +5 -0
- package/dist/shell/frame/nativeMenu.js +178 -0
- package/dist/shell/frame/nativeMenu.js.map +1 -0
- package/dist/shell/frame/nativeMenuModel.d.ts +102 -0
- package/dist/shell/frame/nativeMenuModel.js +186 -0
- package/dist/shell/frame/nativeMenuModel.js.map +1 -0
- package/dist/shell/frame/paletteModel.d.ts +139 -0
- package/dist/shell/frame/paletteModel.js +114 -0
- package/dist/shell/frame/paletteModel.js.map +1 -0
- package/dist/shell/frame/paneTheme.css +9 -0
- package/dist/shell/frame/pluginCommandStore.d.ts +23 -0
- package/dist/shell/frame/pluginCommandStore.js +70 -0
- package/dist/shell/frame/pluginCommandStore.js.map +1 -0
- package/dist/shell/frame/reportModel.d.ts +52 -0
- package/dist/shell/frame/reportModel.js +105 -0
- package/dist/shell/frame/reportModel.js.map +1 -0
- package/dist/shell/frame/safeStore.d.ts +7 -0
- package/dist/shell/frame/safeStore.js +40 -0
- package/dist/shell/frame/safeStore.js.map +1 -0
- package/dist/shell/frame/searchInput.d.ts +2 -0
- package/dist/shell/frame/searchInput.js +11 -0
- package/dist/shell/frame/searchInput.js.map +1 -0
- package/dist/shell/frame/shell.css +634 -0
- package/dist/shell/frame/site.d.ts +206 -0
- package/dist/shell/frame/site.js +22 -0
- package/dist/shell/frame/site.js.map +1 -0
- package/dist/shell/frame/state.d.ts +101 -0
- package/dist/shell/frame/state.js +286 -0
- package/dist/shell/frame/state.js.map +1 -0
- package/dist/shell/frame/syncGate.d.ts +10 -0
- package/dist/shell/frame/syncGate.js +36 -0
- package/dist/shell/frame/syncGate.js.map +1 -0
- package/dist/shell/frame/syncHost.d.ts +35 -0
- package/dist/shell/frame/syncHost.js +85 -0
- package/dist/shell/frame/syncHost.js.map +1 -0
- package/dist/shell/frame/tabStill.d.ts +2 -0
- package/dist/shell/frame/tabStill.js +62 -0
- package/dist/shell/frame/tabStill.js.map +1 -0
- package/dist/shell/frame/themeScopes.d.ts +29 -0
- package/dist/shell/frame/themeScopes.js +61 -0
- package/dist/shell/frame/themeScopes.js.map +1 -0
- package/dist/shell/frame/themeStore.d.ts +99 -0
- package/dist/shell/frame/themeStore.js +267 -0
- package/dist/shell/frame/themeStore.js.map +1 -0
- package/dist/shell/frame/windowBinding.d.ts +2 -0
- package/dist/shell/frame/windowBinding.js +7 -0
- package/dist/shell/frame/windowBinding.js.map +1 -0
- package/dist/shell/frame/workspaceModel.d.ts +82 -0
- package/dist/shell/frame/workspaceModel.js +250 -0
- package/dist/shell/frame/workspaceModel.js.map +1 -0
- package/dist/shell/frame/workspaces.css +84 -0
- package/dist/shell/frame/workspaces.d.ts +22 -0
- package/dist/shell/frame/workspaces.js +125 -0
- package/dist/shell/frame/workspaces.js.map +1 -0
- package/dist/shell/frame/zoomStore.d.ts +10 -0
- package/dist/shell/frame/zoomStore.js +65 -0
- package/dist/shell/frame/zoomStore.js.map +1 -0
- package/dist/shell/installController.d.ts +137 -0
- package/dist/shell/installController.js +172 -0
- package/dist/shell/installController.js.map +1 -0
- package/dist/shell/installerMessages.d.ts +30 -0
- package/dist/shell/installerMessages.js +36 -0
- package/dist/shell/installerMessages.js.map +1 -0
- package/dist/shell/linkDrop.js.map +1 -1
- package/dist/shell/messages.d.ts +16 -0
- package/dist/shell/messages.js +27 -0
- package/dist/shell/messages.js.map +1 -0
- package/dist/shell/palette.js.map +1 -1
- package/dist/shell/paneModel.d.ts +22 -0
- package/dist/shell/paneModel.js +24 -1
- package/dist/shell/paneModel.js.map +1 -1
- package/dist/shell/panel/Appearance.d.ts +8 -0
- package/dist/shell/panel/Appearance.js +265 -0
- package/dist/shell/panel/Appearance.js.map +1 -0
- package/dist/shell/panel/BackupPanel.d.ts +5 -0
- package/dist/shell/panel/BackupPanel.js +198 -0
- package/dist/shell/panel/BackupPanel.js.map +1 -0
- package/dist/shell/panel/DeveloperPanel.d.ts +4 -0
- package/dist/shell/panel/DeveloperPanel.js +51 -0
- package/dist/shell/panel/DeveloperPanel.js.map +1 -0
- package/dist/shell/panel/FeatureToggles.d.ts +13 -0
- package/dist/shell/panel/FeatureToggles.js +42 -0
- package/dist/shell/panel/FeatureToggles.js.map +1 -0
- package/dist/shell/panel/Home.d.ts +3 -0
- package/dist/shell/panel/Home.js +23 -0
- package/dist/shell/panel/Home.js.map +1 -0
- package/dist/shell/panel/IconPackInstall.d.ts +15 -0
- package/dist/shell/panel/IconPackInstall.js +41 -0
- package/dist/shell/panel/IconPackInstall.js.map +1 -0
- package/dist/shell/panel/LanguageSettings.d.ts +1 -0
- package/dist/shell/panel/LanguageSettings.js +54 -0
- package/dist/shell/panel/LanguageSettings.js.map +1 -0
- package/dist/shell/panel/MakePanel.d.ts +3 -0
- package/dist/shell/panel/MakePanel.js +237 -0
- package/dist/shell/panel/MakePanel.js.map +1 -0
- package/dist/shell/panel/MarkdownDoc.d.ts +6 -0
- package/dist/shell/panel/MarkdownDoc.js +35 -0
- package/dist/shell/panel/MarkdownDoc.js.map +1 -0
- package/dist/shell/panel/Modes.d.ts +40 -0
- package/dist/shell/panel/Modes.js +270 -0
- package/dist/shell/panel/Modes.js.map +1 -0
- package/dist/shell/panel/NavIcon.d.ts +4 -0
- package/dist/shell/panel/NavIcon.js +6 -0
- package/dist/shell/panel/NavIcon.js.map +1 -0
- package/dist/shell/panel/Panel.d.ts +7 -0
- package/dist/shell/panel/Panel.js +139 -0
- package/dist/shell/panel/Panel.js.map +1 -0
- package/dist/shell/panel/PluginFeatures.d.ts +24 -0
- package/dist/shell/panel/PluginFeatures.js +165 -0
- package/dist/shell/panel/PluginFeatures.js.map +1 -0
- package/dist/shell/panel/Plugins.d.ts +33 -0
- package/dist/shell/panel/Plugins.js +112 -0
- package/dist/shell/panel/Plugins.js.map +1 -0
- package/dist/shell/panel/PowerSettings.d.ts +4 -0
- package/dist/shell/panel/PowerSettings.js +52 -0
- package/dist/shell/panel/PowerSettings.js.map +1 -0
- package/dist/shell/panel/Settings.d.ts +23 -0
- package/dist/shell/panel/Settings.js +117 -0
- package/dist/shell/panel/Settings.js.map +1 -0
- package/dist/shell/panel/ShareSetup.d.ts +8 -0
- package/dist/shell/panel/ShareSetup.js +130 -0
- package/dist/shell/panel/ShareSetup.js.map +1 -0
- package/dist/shell/panel/StoreSection.d.ts +7 -0
- package/dist/shell/panel/StoreSection.js +78 -0
- package/dist/shell/panel/StoreSection.js.map +1 -0
- package/dist/shell/panel/StringPacks.d.ts +20 -0
- package/dist/shell/panel/StringPacks.js +131 -0
- package/dist/shell/panel/StringPacks.js.map +1 -0
- package/dist/shell/panel/ThemeBuilder.d.ts +5 -0
- package/dist/shell/panel/ThemeBuilder.js +205 -0
- package/dist/shell/panel/ThemeBuilder.js.map +1 -0
- package/dist/shell/panel/backup.css +34 -0
- package/dist/shell/panel/builder.css +240 -0
- package/dist/shell/panel/builderModel.d.ts +55 -0
- package/dist/shell/panel/builderModel.js +95 -0
- package/dist/shell/panel/builderModel.js.map +1 -0
- package/dist/shell/panel/compare.d.ts +10 -0
- package/dist/shell/panel/compare.js +51 -0
- package/dist/shell/panel/compare.js.map +1 -0
- package/dist/shell/panel/controls.d.ts +135 -0
- package/dist/shell/panel/controls.js +213 -0
- package/dist/shell/panel/controls.js.map +1 -0
- package/dist/shell/panel/featureModel.d.ts +69 -0
- package/dist/shell/panel/featureModel.js +79 -0
- package/dist/shell/panel/featureModel.js.map +1 -0
- package/dist/shell/panel/fonts/FontBrowser.d.ts +10 -0
- package/dist/shell/panel/fonts/FontBrowser.js +189 -0
- package/dist/shell/panel/fonts/FontBrowser.js.map +1 -0
- package/dist/shell/panel/fonts/FontRoles.d.ts +20 -0
- package/dist/shell/panel/fonts/FontRoles.js +77 -0
- package/dist/shell/panel/fonts/FontRoles.js.map +1 -0
- package/dist/shell/panel/fonts/catalog.d.ts +37 -0
- package/dist/shell/panel/fonts/catalog.js +68 -0
- package/dist/shell/panel/fonts/catalog.js.map +1 -0
- package/dist/shell/panel/fonts/googleFontsCatalog.json +1 -0
- package/dist/shell/panel/index.d.ts +20 -0
- package/dist/shell/panel/index.js +34 -0
- package/dist/shell/panel/index.js.map +1 -0
- package/dist/shell/panel/languageModel.d.ts +5 -0
- package/dist/shell/panel/languageModel.js +7 -0
- package/dist/shell/panel/languageModel.js.map +1 -0
- package/dist/shell/panel/make.css +73 -0
- package/dist/shell/panel/makeModel.d.ts +32 -0
- package/dist/shell/panel/makeModel.js +84 -0
- package/dist/shell/panel/makeModel.js.map +1 -0
- package/dist/shell/panel/markdown.d.ts +46 -0
- package/dist/shell/panel/markdown.js +100 -0
- package/dist/shell/panel/markdown.js.map +1 -0
- package/dist/shell/panel/nav.d.ts +12 -0
- package/dist/shell/panel/nav.js +50 -0
- package/dist/shell/panel/nav.js.map +1 -0
- package/dist/shell/panel/panel.css +401 -0
- package/dist/shell/panel/panelMessages.d.ts +803 -0
- package/dist/shell/panel/panelMessages.js +779 -0
- package/dist/shell/panel/panelMessages.js.map +1 -0
- package/dist/shell/panel/plugins.css +59 -0
- package/dist/shell/panel/setupModel.d.ts +87 -0
- package/dist/shell/panel/setupModel.js +152 -0
- package/dist/shell/panel/setupModel.js.map +1 -0
- package/dist/shell/panel/site.d.ts +259 -0
- package/dist/shell/panel/site.js +25 -0
- package/dist/shell/panel/site.js.map +1 -0
- package/dist/shell/panel/strings.css +7 -0
- package/dist/shell/panel/themes.d.ts +10 -0
- package/dist/shell/panel/themes.js +8 -0
- package/dist/shell/panel/themes.js.map +1 -0
- package/dist/shell/profileModel.d.ts +3 -2
- package/dist/shell/profileModel.js +7 -5
- package/dist/shell/profileModel.js.map +1 -1
- package/dist/shell/store.css +250 -0
- package/dist/shell/storeMessages.d.ts +193 -0
- package/dist/shell/storeMessages.js +196 -0
- package/dist/shell/storeMessages.js.map +1 -0
- package/dist/shell/storeModel.d.ts +205 -0
- package/dist/shell/storeModel.js +146 -0
- package/dist/shell/storeModel.js.map +1 -0
- package/dist/shell/stringPacksModel.d.ts +44 -0
- package/dist/shell/stringPacksModel.js +99 -0
- package/dist/shell/stringPacksModel.js.map +1 -0
- package/dist/shell/updateModel.d.ts +2 -1
- package/dist/shell/updateModel.js +5 -4
- package/dist/shell/updateModel.js.map +1 -1
- package/dist/shell/windowControls.d.ts +4 -0
- package/dist/shell/windowControls.js +5 -0
- package/dist/shell/windowControls.js.map +1 -1
- package/i18n-config.schema.json +29 -0
- package/i18n-meta.schema.json +26 -0
- package/i18n.schema.json +42 -0
- package/icons.schema.json +655 -0
- package/languages.schema.json +23 -0
- package/locales/ar-x-fm.json +11 -0
- package/locales/ar.json +1388 -0
- package/locales/bg.json +1387 -0
- package/locales/bn.json +1387 -0
- package/locales/ca.json +1387 -0
- package/locales/cs.json +1387 -0
- package/locales/da.json +1387 -0
- package/locales/de.json +1387 -0
- package/locales/el.json +1387 -0
- package/locales/en-GB.json +27 -0
- package/locales/en.json +1386 -0
- package/locales/en.meta.json +51 -0
- package/locales/es.json +1387 -0
- package/locales/eu.json +1387 -0
- package/locales/fa.json +1388 -0
- package/locales/fi.json +1387 -0
- package/locales/fil.json +1387 -0
- package/locales/fr.json +1387 -0
- package/locales/ga.json +1387 -0
- package/locales/gl.json +1387 -0
- package/locales/gu.json +1387 -0
- package/locales/ha.json +1387 -0
- package/locales/he.json +1388 -0
- package/locales/hi.json +1387 -0
- package/locales/hr.json +1387 -0
- package/locales/hu.json +1387 -0
- package/locales/id.json +1387 -0
- package/locales/ig.json +1387 -0
- package/locales/it.json +1387 -0
- package/locales/ja.json +1387 -0
- package/locales/kn.json +1387 -0
- package/locales/ko.json +1387 -0
- package/locales/mr.json +1387 -0
- package/locales/ms.json +1387 -0
- package/locales/nb.json +1387 -0
- package/locales/nl.json +1387 -0
- package/locales/pl.json +1387 -0
- package/locales/pt.json +1387 -0
- package/locales/ro.json +1387 -0
- package/locales/ru.json +1387 -0
- package/locales/sk.json +1387 -0
- package/locales/sr.json +1387 -0
- package/locales/sv.json +1387 -0
- package/locales/ta.json +1387 -0
- package/locales/th.json +1387 -0
- package/locales/tr.json +1387 -0
- package/locales/uk.json +1387 -0
- package/locales/ur.json +1388 -0
- package/locales/vi.json +1387 -0
- package/locales/yo.json +1387 -0
- package/locales/zh-Hans.json +1387 -0
- package/locales/zh-Hant.json +1387 -0
- package/package.json +138 -5
- package/store-catalog.schema.json +78 -0
- package/theme.schema.json +4668 -0
- package/windows/installer.nsi +946 -0
- package/windows/languages/Basque.nsh +28 -0
- package/windows/languages/Catalan.nsh +28 -0
- package/windows/languages/Croatian.nsh +28 -0
- package/windows/languages/Czech.nsh +28 -0
- package/windows/languages/Danish.nsh +28 -0
- package/windows/languages/Finnish.nsh +28 -0
- package/windows/languages/Galician.nsh +28 -0
- package/windows/languages/Greek.nsh +28 -0
- package/windows/languages/Hungarian.nsh +28 -0
- package/windows/languages/Indonesian.nsh +28 -0
- package/windows/languages/Irish.nsh +28 -0
- package/windows/languages/Malay.nsh +28 -0
- package/windows/languages/Polish.nsh +28 -0
- package/windows/languages/Romanian.nsh +28 -0
- package/windows/languages/Serbian.nsh +28 -0
- package/windows/languages/Slovak.nsh +28 -0
- package/windows/languages/Thai.nsh +28 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// Pure model of the app menu (the titlebar's "Settings and more": AppMenu.tsx), unit-tested under
|
|
2
|
+
// node: which rows an app gets (its `ShellSite.menu` groups, the built-in rows and what gates
|
|
3
|
+
// them), how the keyboard moves through them, the language list, the zoom steps and how a shortcut
|
|
4
|
+
// is written. No React, no Tauri.
|
|
5
|
+
//
|
|
6
|
+
// The menu's new words live in menuMessages.en.json (`wb.menu.*`) and are read through the
|
|
7
|
+
// framework i18n with that English as the fallback (`menuText`), so they work before the catalogs
|
|
8
|
+
// have them. Words the panel already has (section names, the mode question) are reused.
|
|
9
|
+
import { formatMessage } from "../../runtime/i18n.js";
|
|
10
|
+
import EN from './menuMessages.en.json' with { type: 'json' };
|
|
11
|
+
import { tx } from "./i18n.js";
|
|
12
|
+
/** The menu's English (`wb.menu.*`), for the catalogs. */
|
|
13
|
+
export const MENU_MESSAGES = EN;
|
|
14
|
+
/** A menu message in the active language; English from menuMessages.en.json until a catalog has it. */
|
|
15
|
+
export function menuText(name, params) {
|
|
16
|
+
const key = `wb.menu.${name}`;
|
|
17
|
+
return tx(key, params, '') || formatMessage(MENU_MESSAGES[key] ?? key, params ?? {});
|
|
18
|
+
}
|
|
19
|
+
// ---------------------------------------------------------------------------------------------
|
|
20
|
+
// What the app configures (`ShellSite.menu`)
|
|
21
|
+
/** The rows the frame brings. Each shows only when the app serves what it needs (`builtinAvailable`). */
|
|
22
|
+
export const MENU_BUILTINS = [
|
|
23
|
+
'new-tab', 'split-right', 'split-down', 'new-column',
|
|
24
|
+
'language', 'theme', 'mode',
|
|
25
|
+
'zoom',
|
|
26
|
+
'plugins', 'store', 'strings', 'workspaces',
|
|
27
|
+
'find', 'shortcuts', 'palette',
|
|
28
|
+
'developer', 'updates', 'safe-mode', 'report', 'about', 'settings',
|
|
29
|
+
];
|
|
30
|
+
/** The trigger in use: `AppMenu`'s own `trigger` (`true` / `false` are the older `button` / `logo`), else the app's, else `pane`. */
|
|
31
|
+
export function resolveTrigger(trigger, configured) {
|
|
32
|
+
if (trigger === true)
|
|
33
|
+
return 'button';
|
|
34
|
+
if (trigger === false)
|
|
35
|
+
return 'logo';
|
|
36
|
+
return trigger ?? configured ?? 'pane';
|
|
37
|
+
}
|
|
38
|
+
/** The menu an app gets without saying anything: every built-in row it can serve. */
|
|
39
|
+
export const DEFAULT_MENU = [
|
|
40
|
+
['new-tab', 'split-right', 'split-down', 'new-column'],
|
|
41
|
+
['language', 'theme', 'mode'],
|
|
42
|
+
['zoom'],
|
|
43
|
+
['plugins', 'store', 'strings', 'workspaces'],
|
|
44
|
+
['find', 'shortcuts', 'palette'],
|
|
45
|
+
['developer', 'updates', 'safe-mode', 'report', 'about', 'settings'],
|
|
46
|
+
];
|
|
47
|
+
const entryId = (entry) => (typeof entry === 'string' ? entry : entry.id);
|
|
48
|
+
/** `groups` with the app's `items` put next to the row `anchor` (after it, or before). Without that row they form a last group. */
|
|
49
|
+
export function menuWith(groups, anchor, items, where = 'after') {
|
|
50
|
+
let placed = false;
|
|
51
|
+
const out = groups.map(group => {
|
|
52
|
+
const at = group.findIndex(entry => entryId(entry) === anchor);
|
|
53
|
+
if (at < 0 || placed)
|
|
54
|
+
return group;
|
|
55
|
+
placed = true;
|
|
56
|
+
const index = where === 'after' ? at + 1 : at;
|
|
57
|
+
return [...group.slice(0, index), ...items, ...group.slice(index)];
|
|
58
|
+
});
|
|
59
|
+
return placed ? out : [...out, items];
|
|
60
|
+
}
|
|
61
|
+
/** `groups` without the rows named. */
|
|
62
|
+
export function menuWithout(groups, ...ids) {
|
|
63
|
+
return groups.map(group => group.filter(entry => !ids.includes(entryId(entry))));
|
|
64
|
+
}
|
|
65
|
+
/** Whether the app can serve a built-in row: a row that would do nothing is never shown. */
|
|
66
|
+
export function builtinAvailable(id, env) {
|
|
67
|
+
switch (id) {
|
|
68
|
+
case 'new-column': return env.decks;
|
|
69
|
+
case 'language': return env.languages;
|
|
70
|
+
case 'theme': return env.themes;
|
|
71
|
+
case 'mode': return env.modes;
|
|
72
|
+
case 'zoom': return env.zoom;
|
|
73
|
+
case 'find': return env.find;
|
|
74
|
+
case 'safe-mode': return env.safeMode;
|
|
75
|
+
case 'store': return env.store;
|
|
76
|
+
case 'strings': return env.strings;
|
|
77
|
+
case 'workspaces': return env.workspaces;
|
|
78
|
+
case 'developer': return env.developer;
|
|
79
|
+
case 'updates': return env.updates;
|
|
80
|
+
default: return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const isBuiltin = (id) => MENU_BUILTINS.includes(id);
|
|
84
|
+
/**
|
|
85
|
+
* The entries the menu shows, group by group: built-in rows the app cannot serve and rows whose
|
|
86
|
+
* `when()` says no are left out, a row listed twice shows once, a group left empty goes (so no
|
|
87
|
+
* rule is ever doubled), and an unknown name is ignored.
|
|
88
|
+
*/
|
|
89
|
+
export function planMenu(groups, env) {
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
const out = [];
|
|
92
|
+
for (const group of groups) {
|
|
93
|
+
const kept = [];
|
|
94
|
+
for (const entry of group) {
|
|
95
|
+
const id = entryId(entry);
|
|
96
|
+
if (seen.has(id))
|
|
97
|
+
continue;
|
|
98
|
+
if (typeof entry === 'string') {
|
|
99
|
+
if (!isBuiltin(entry) || !builtinAvailable(entry, env))
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
else if (isBuiltin(id) || (entry.when && !entry.when())) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
seen.add(id);
|
|
106
|
+
kept.push(entry);
|
|
107
|
+
}
|
|
108
|
+
if (kept.length)
|
|
109
|
+
out.push(kept);
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/** Groups of rows as one list with a rule between two groups. */
|
|
114
|
+
export function joinGroups(groups) {
|
|
115
|
+
const out = [];
|
|
116
|
+
for (const group of groups) {
|
|
117
|
+
if (!group.length)
|
|
118
|
+
continue;
|
|
119
|
+
if (out.length)
|
|
120
|
+
out.push({ kind: 'separator' });
|
|
121
|
+
out.push(...group);
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
/** The app's own entries as rows (`submenu` rows are looked up again when they open). */
|
|
126
|
+
export function specRows(specs) {
|
|
127
|
+
const out = [];
|
|
128
|
+
for (const spec of specs) {
|
|
129
|
+
if (spec.when && !spec.when())
|
|
130
|
+
continue;
|
|
131
|
+
if (spec.separated && out.length)
|
|
132
|
+
out.push({ kind: 'separator' });
|
|
133
|
+
const checked = spec.checked?.();
|
|
134
|
+
out.push({
|
|
135
|
+
kind: 'item', id: spec.id, label: spec.label(), icon: spec.icon, shortcut: spec.shortcut, hint: spec.hint?.(),
|
|
136
|
+
check: checked === undefined ? undefined : 'checkbox', checked, submenu: !!spec.submenu, keepOpen: spec.keepOpen, run: spec.run,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
/** The app's entry with this id, anywhere in its groups or their submenus. */
|
|
142
|
+
export function findSpec(groups, id) {
|
|
143
|
+
const walk = (specs, depth) => {
|
|
144
|
+
for (const spec of specs) {
|
|
145
|
+
if (spec.id === id)
|
|
146
|
+
return spec;
|
|
147
|
+
const inner = depth < 3 && spec.submenu ? walk(spec.submenu(), depth + 1) : null;
|
|
148
|
+
if (inner)
|
|
149
|
+
return inner;
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
};
|
|
153
|
+
return walk(groups.flat().filter((entry) => typeof entry !== 'string'), 0);
|
|
154
|
+
}
|
|
155
|
+
// ---------------------------------------------------------------------------------------------
|
|
156
|
+
// Keyboard
|
|
157
|
+
/** Arrow keys, Home and End over `count` stops; wraps around. -1 (nothing focused) enters at an end. */
|
|
158
|
+
export function moveStop(index, key, count) {
|
|
159
|
+
if (count <= 0)
|
|
160
|
+
return -1;
|
|
161
|
+
if (key === 'Home')
|
|
162
|
+
return 0;
|
|
163
|
+
if (key === 'End')
|
|
164
|
+
return count - 1;
|
|
165
|
+
if (key === 'ArrowDown')
|
|
166
|
+
return index < 0 ? 0 : (index + 1) % count;
|
|
167
|
+
if (key === 'ArrowUp')
|
|
168
|
+
return index < 0 ? count - 1 : (index - 1 + count) % count;
|
|
169
|
+
return index;
|
|
170
|
+
}
|
|
171
|
+
const fold = (text) => text.normalize('NFKD').replace(/\p{M}/gu, '').toLocaleLowerCase();
|
|
172
|
+
/**
|
|
173
|
+
* Type-ahead: the next stop after `from` whose label starts with what was typed (accents and case
|
|
174
|
+
* ignored). One letter typed again cycles through the labels that start with it. -1: none.
|
|
175
|
+
*/
|
|
176
|
+
export function typeahead(labels, from, typed) {
|
|
177
|
+
const text = fold(typed);
|
|
178
|
+
if (!text || !labels.length)
|
|
179
|
+
return -1;
|
|
180
|
+
const chars = [...text];
|
|
181
|
+
const repeat = chars.length > 1 && chars.every(char => char === chars[0]);
|
|
182
|
+
const needle = repeat ? chars[0] : text;
|
|
183
|
+
// While a word is being typed the focused row may keep matching; a repeated letter moves on.
|
|
184
|
+
const start = chars.length > 1 && !repeat ? Math.max(0, from) : from + 1;
|
|
185
|
+
for (let step = 0; step < labels.length; step += 1) {
|
|
186
|
+
const index = (start + step + labels.length) % labels.length;
|
|
187
|
+
if (fold(labels[index]).startsWith(needle))
|
|
188
|
+
return index;
|
|
189
|
+
}
|
|
190
|
+
return -1;
|
|
191
|
+
}
|
|
192
|
+
/** The keys that open and close a submenu: they follow the reading direction. */
|
|
193
|
+
export function submenuKeys(rtl) {
|
|
194
|
+
return rtl ? { open: 'ArrowLeft', close: 'ArrowRight' } : { open: 'ArrowRight', close: 'ArrowLeft' };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The left edge of a submenu `width` wide beside its menu (`parent`), in a window `viewport` wide.
|
|
198
|
+
* The menu hangs at one edge of the window (under its button at the end, under the logo at the
|
|
199
|
+
* start): the submenu opens toward the middle, else on the other side, else over the menu, and
|
|
200
|
+
* always stays `edge` pixels inside the window.
|
|
201
|
+
*/
|
|
202
|
+
export function submenuLeft(parent, width, viewport, edge = 8) {
|
|
203
|
+
const overlap = 3;
|
|
204
|
+
const before = parent.left - width + overlap;
|
|
205
|
+
const after = parent.right - overlap;
|
|
206
|
+
const fits = (x) => x >= edge && x + width <= viewport - edge;
|
|
207
|
+
const first = (parent.left + parent.right) / 2 > viewport / 2 ? before : after;
|
|
208
|
+
const second = first === before ? after : before;
|
|
209
|
+
if (fits(first))
|
|
210
|
+
return first;
|
|
211
|
+
if (fits(second))
|
|
212
|
+
return second;
|
|
213
|
+
return Math.max(edge, Math.min(first, viewport - width - edge));
|
|
214
|
+
}
|
|
215
|
+
/** A shortcut as the platform writes it: `Ctrl+Shift+T` on Windows and Linux, `⇧⌘T` on macOS. */
|
|
216
|
+
export function shortcutText(shortcut, mac) {
|
|
217
|
+
const parts = shortcut.split('+').map(part => part.trim()).filter(Boolean);
|
|
218
|
+
if (!mac)
|
|
219
|
+
return parts.join('+');
|
|
220
|
+
const symbols = { ctrl: '⌘', cmd: '⌘', shift: '⇧', alt: '⌥' };
|
|
221
|
+
const order = ['alt', 'shift', 'ctrl', 'cmd'];
|
|
222
|
+
const mods = parts.filter(part => part.toLowerCase() in symbols).sort((a, b) => order.indexOf(a.toLowerCase()) - order.indexOf(b.toLowerCase()));
|
|
223
|
+
const keys = parts.filter(part => !(part.toLowerCase() in symbols));
|
|
224
|
+
return `${mods.map(part => symbols[part.toLowerCase()]).join('')}${keys.join('')}`;
|
|
225
|
+
}
|
|
226
|
+
// ---------------------------------------------------------------------------------------------
|
|
227
|
+
// Language
|
|
228
|
+
/**
|
|
229
|
+
* The Language submenu: follow the site, follow the system, then the supported languages by their
|
|
230
|
+
* own names (the one chosen is checked; a community language in use is listed too, so the check is
|
|
231
|
+
* never missing), then the way to the full list in Settings.
|
|
232
|
+
*/
|
|
233
|
+
export function languageRows(list, state, words) {
|
|
234
|
+
const named = (code) => (code ? list.find(language => language.code === code)?.name : undefined);
|
|
235
|
+
const automatic = [
|
|
236
|
+
{ kind: 'item', id: 'lang:site', label: words.site, check: 'radio', checked: state.preference === 'site', hint: state.preference === 'site' ? named(state.locale) : named(state.siteLang) },
|
|
237
|
+
{ kind: 'item', id: 'lang:system', label: words.system, check: 'radio', checked: state.preference === 'system', hint: state.preference === 'system' ? named(state.locale) : undefined },
|
|
238
|
+
];
|
|
239
|
+
const shown = list.filter(language => language.tier !== 'community' || language.code === state.preference);
|
|
240
|
+
const languages = shown.map(language => ({
|
|
241
|
+
kind: 'item', id: `lang:${language.code}`, label: language.name, check: 'radio', checked: state.preference === language.code,
|
|
242
|
+
hint: language.name === language.englishName ? undefined : language.englishName, lang: language.code, dir: language.dir,
|
|
243
|
+
}));
|
|
244
|
+
return joinGroups([automatic, languages, [{ kind: 'item', id: 'lang:more', label: words.more }]]);
|
|
245
|
+
}
|
|
246
|
+
/** The preference a Language row stands for (`lang:tr` → `tr`), or null for "More languages…". */
|
|
247
|
+
export function languageChoice(id) {
|
|
248
|
+
const code = id.startsWith('lang:') ? id.slice(5) : '';
|
|
249
|
+
return code && code !== 'more' ? code : null;
|
|
250
|
+
}
|
|
251
|
+
// ---------------------------------------------------------------------------------------------
|
|
252
|
+
// Zoom
|
|
253
|
+
/** Page zoom limits, as native code clamps them (waybetter-host `zoom.rs`). */
|
|
254
|
+
export const ZOOM_MIN = 0.25;
|
|
255
|
+
export const ZOOM_MAX = 5;
|
|
256
|
+
/** The steps − and + move through (a browser's). */
|
|
257
|
+
export const ZOOM_STEPS = [0.25, 0.33, 0.5, 0.67, 0.75, 0.8, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5];
|
|
258
|
+
/** The next step up (`1`) or down (`-1`) from `factor`, or 100% (`0`). Stays inside the limits. */
|
|
259
|
+
export function zoomStep(factor, direction) {
|
|
260
|
+
if (direction === 0 || !Number.isFinite(factor))
|
|
261
|
+
return 1;
|
|
262
|
+
const near = 0.005;
|
|
263
|
+
if (direction > 0)
|
|
264
|
+
return ZOOM_STEPS.find(step => step > factor + near) ?? ZOOM_MAX;
|
|
265
|
+
return [...ZOOM_STEPS].reverse().find(step => step < factor - near) ?? ZOOM_MIN;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The Updates row for the updater's status. `asked`: the owner asked for a check in this menu;
|
|
269
|
+
* only then does a check that did not get through say so (the timer's own failures stay quiet),
|
|
270
|
+
* and it says it calmly: a short state, never the error text.
|
|
271
|
+
*/
|
|
272
|
+
export function updateRow(status, asked, version) {
|
|
273
|
+
const check = menuText('updateCheck');
|
|
274
|
+
const quiet = version || undefined;
|
|
275
|
+
switch (status.state) {
|
|
276
|
+
case 'ready': return { label: menuText('updateRestart'), hint: status.version || quiet, action: 'restart' };
|
|
277
|
+
case 'installing': return { label: check, hint: menuText('updateInstalling', { version: status.version }), action: 'wait' };
|
|
278
|
+
case 'downloading': return { label: check, hint: menuText('updateDownloading', { version: status.version }), action: 'wait' };
|
|
279
|
+
case 'checking': return { label: check, hint: menuText('updateChecking'), action: 'wait' };
|
|
280
|
+
case 'problem': return { label: tx('wb.update.problemLabel'), hint: quiet, action: 'open' };
|
|
281
|
+
case 'upToDate': return { label: check, hint: version ? menuText('updateUpToDateVersion', { version }) : menuText('updateUpToDate'), action: 'check' };
|
|
282
|
+
case 'error': return asked ? { label: check, hint: menuText('updateFailed'), title: menuText('updateFailedHint'), action: 'check' } : { label: check, hint: quiet, action: 'check' };
|
|
283
|
+
default: return { label: check, hint: quiet, action: 'check' };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// ---------------------------------------------------------------------------------------------
|
|
287
|
+
// Find in page
|
|
288
|
+
/**
|
|
289
|
+
* What the find bar says about a search: "3/17" (the active match of the total), the total alone
|
|
290
|
+
* while no match is active, "No matches" for none. `number` writes a number in the active language.
|
|
291
|
+
*/
|
|
292
|
+
export function findLabel(counts, number = String) {
|
|
293
|
+
if (counts.count <= 0)
|
|
294
|
+
return menuText('findNone');
|
|
295
|
+
if (counts.index < 0 || counts.index >= counts.count)
|
|
296
|
+
return menuText('findTotal', { count: counts.count });
|
|
297
|
+
return menuText('findCount', { index: number(counts.index + 1), count: number(counts.count) });
|
|
298
|
+
}
|
|
299
|
+
/** Whole percent of a factor (1.1 → 110). */
|
|
300
|
+
export const zoomPercent = (factor) => Math.round(factor * 100);
|
|
301
|
+
//# sourceMappingURL=menuModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menuModel.js","sourceRoot":"","sources":["../../../../../../shell/src/frame/menuModel.ts"],"names":[],"mappings":"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","sourcesContent":["// Pure model of the app menu (the titlebar's \"Settings and more\": AppMenu.tsx), unit-tested under\n// node: which rows an app gets (its `ShellSite.menu` groups, the built-in rows and what gates\n// them), how the keyboard moves through them, the language list, the zoom steps and how a shortcut\n// is written. No React, no Tauri.\n//\n// The menu's new words live in menuMessages.en.json (`wb.menu.*`) and are read through the\n// framework i18n with that English as the fallback (`menuText`), so they work before the catalogs\n// have them. Words the panel already has (section names, the mode question) are reused.\n\nimport { formatMessage } from '../../../runtime/src/i18n.ts';\nimport type { UpdateStatus } from '../updateModel.ts';\nimport EN from './menuMessages.en.json' with { type: 'json' };\nimport { tx, type Params } from './i18n.ts';\nimport type { IconName } from './icons.tsx';\nimport type { Choice } from './nativeMenuModel.ts';\n\n/** The menu's English (`wb.menu.*`), for the catalogs. */\nexport const MENU_MESSAGES: Readonly<Record<string, string>> = EN;\n\ntype MenuKey = keyof typeof EN;\n/** A menu message's name: its key without the `wb.menu.` prefix. */\nexport type MenuWord = MenuKey extends `wb.menu.${infer Name}` ? Name : never;\n\n/** A menu message in the active language; English from menuMessages.en.json until a catalog has it. */\nexport function menuText(name: MenuWord, params?: Params): string {\n const key = `wb.menu.${name}`;\n return tx(key, params, '') || formatMessage(MENU_MESSAGES[key] ?? key, params ?? {});\n}\n\n// ---------------------------------------------------------------------------------------------\n// What the app configures (`ShellSite.menu`)\n\n/** The rows the frame brings. Each shows only when the app serves what it needs (`builtinAvailable`). */\nexport const MENU_BUILTINS = [\n 'new-tab', 'split-right', 'split-down', 'new-column',\n 'language', 'theme', 'mode',\n 'zoom',\n 'plugins', 'store', 'strings', 'workspaces',\n 'find', 'shortcuts', 'palette',\n 'developer', 'updates', 'safe-mode', 'report', 'about', 'settings',\n] as const;\nexport type BuiltinMenuItem = typeof MENU_BUILTINS[number];\n\n/** A row of the app's own. */\nexport type MenuItemSpec = {\n /** Unique in the menu (never a built-in's name). */\n id: string;\n label(): string;\n icon?: IconName;\n /** Its keys, written like `Ctrl+Shift+D` (shown as the platform writes them; the app handles the keys). */\n shortcut?: string;\n /** Quiet text at the end of the row instead of a shortcut (a count, a version). */\n hint?(): string;\n /** Leave the row out while this is false. */\n when?(): boolean;\n /** A toggle: the row is a checkbox showing this state; `run` flips it. */\n checked?(): boolean;\n run?(): void | Promise<void>;\n /** A submenu instead of an action (its rows may have submenus of their own). */\n submenu?(): MenuItemSpec[];\n /** Starts a new group (a rule above this row) inside a submenu. */\n separated?: boolean;\n /** The menu stays open after `run` (a toggle someone may flip back). */\n keepOpen?: boolean;\n};\n\nexport type MenuEntry = BuiltinMenuItem | MenuItemSpec;\n/** Ordered groups; a rule is drawn between two groups. */\nexport type MenuGroups = ReadonlyArray<ReadonlyArray<MenuEntry>>;\n\n/** One language of the app (a row of its `languages.json`). */\nexport type MenuLanguage = { code: string; name: string; englishName: string; dir: 'ltr' | 'rtl'; tier: 'supported' | 'community' };\n\n/** The app's language state (frame/language.ts `createLanguage`), as the panel's picker takes it. */\nexport type MenuLanguages = {\n list: readonly MenuLanguage[];\n state(): { preference: string; siteLang: string | null; locale: string };\n subscribe(listener: () => void): () => void;\n set(preference: string): Promise<void>;\n};\n\n/** The app's modes (Basic / Advanced / Power User): the panel's `menuModes()` gives this. */\nexport type MenuModes = {\n list(): ReadonlyArray<{ id: string; label: string }>;\n /** The mode in use (null: none chosen yet). */\n current(): Promise<string | null>;\n /** How many of the owner's own changes choosing `to` would replace (0: nothing to ask). */\n changes(to: string): Promise<number>;\n /** Applies `to`, keeping the owner's own changes or not. */\n apply(to: string, keep: boolean): Promise<void>;\n /** How the features relate to the mode in use (omit: the menu never says \"custom\"). */\n status?(): Promise<MenuModeStatus>;\n /** Applies the mode in use again as it is now: exactly what `status()` listed. */\n reapply?(): Promise<void>;\n /** The \"new features\" note was looked at; it is not shown again. */\n seen?(): Promise<void>;\n};\n\n/**\n * A mode is a starting point. `custom`: the features are no longer what the mode starts from;\n * `on` / `off`: the names of what applying it again would turn on and off; `added`: how many of\n * those the mode gained in an update since it was applied.\n */\nexport type MenuModeStatus = { custom: boolean; on: string[]; off: string[]; added: number };\n\n/** Lines an app adds to the \"Report a problem\" summary (reportModel.ts): ids only, never anything about the person. */\nexport type ReportDetails = ReadonlyArray<{ label: string; values: readonly string[] }>;\n\n/** Where the app menu's trigger is (`ShellMenu.trigger`). */\nexport type AppMenuTrigger = 'pane' | 'button' | 'logo';\n\n/** The trigger in use: `AppMenu`'s own `trigger` (`true` / `false` are the older `button` / `logo`), else the app's, else `pane`. */\nexport function resolveTrigger(trigger: AppMenuTrigger | boolean | undefined, configured: AppMenuTrigger | undefined): AppMenuTrigger {\n if (trigger === true) return 'button';\n if (trigger === false) return 'logo';\n return trigger ?? configured ?? 'pane';\n}\n\n/**\n * What an app adds to the native menu bar (macOS; nativeMenu.ts): lists the frame cannot know, and the\n * actions only the app can run. Whatever is omitted leaves its item out of the bar.\n */\nexport type NativeMenuApp = {\n /** The Plugins menu's checked rows (the app's plugin or area switches) and what flipping one does. */\n plugins?: { list(): Choice[] | Promise<Choice[]>; set(id: string, on: boolean): Promise<void> | void; subscribe?(listener: () => void): () => void };\n /** View > Icon Pack: the packs and picking one. */\n iconPacks?: { list(): Choice[]; set(id: string): Promise<void> | void; subscribe?(listener: () => void): () => void };\n /** File > Install from File…: asks for a file and stages it on the normal install screen. */\n installFromFile?(): void;\n /** File > Share My Setup…. */\n shareSetup?(): void;\n /** Help > Docs. */\n docs?(): void;\n /** Help > the project's site: the label shown (a host name) and the https address opened in the browser. */\n website?: { label: string; url: string };\n};\n\n/** `ShellSite.menu`: the app menu's rows and what the built-in ones need from the app. */\nexport type ShellMenu = {\n /**\n * What opens the menu with a click: `pane` (default), each pane's wayBetter button at the end of\n * its toolbar (the menu hangs under the button that was clicked); `button`, a \"⋮\" button at the\n * end of the titlebar; `logo`, the titlebar's logo. Keys and other requests open it at the same place.\n */\n trigger?: AppMenuTrigger;\n /** The rows, in order. A string is a built-in row, an object one of the app's. Default: `DEFAULT_MENU`. */\n groups?: MenuGroups;\n /** The Language submenu; omit for an app in one language. */\n languages?: MenuLanguages;\n /** The Mode submenu; omit for an app without modes. */\n modes?: MenuModes;\n /** The app's version, shown on the Updates row. */\n version?(): Promise<string>;\n /**\n * \"Report a problem\": the project's new-issue page (https; opened bare in the system browser, the\n * summary is never put in the URL) and lines the app adds to the summary (a fixed English label\n * and ids: what paused itself, which areas are on). Without `issueUrl` the dialog still shows and\n * copies the summary; its button is off and says there is no public page yet.\n */\n report?: { issueUrl?: string; details?(): ReportDetails | Promise<ReportDetails> };\n /** Opens the app's shortcuts sheet (\"Keyboard shortcuts…\"); without one the row opens the command palette. */\n shortcuts?(): void;\n /** The native menu bar (macOS): the app's lists and actions. */\n native?: NativeMenuApp;\n};\n\n/** The menu an app gets without saying anything: every built-in row it can serve. */\nexport const DEFAULT_MENU: MenuGroups = [\n ['new-tab', 'split-right', 'split-down', 'new-column'],\n ['language', 'theme', 'mode'],\n ['zoom'],\n ['plugins', 'store', 'strings', 'workspaces'],\n ['find', 'shortcuts', 'palette'],\n ['developer', 'updates', 'safe-mode', 'report', 'about', 'settings'],\n];\n\nconst entryId = (entry: MenuEntry) => (typeof entry === 'string' ? entry : entry.id);\n\n/** `groups` with the app's `items` put next to the row `anchor` (after it, or before). Without that row they form a last group. */\nexport function menuWith(groups: MenuGroups, anchor: string, items: readonly MenuEntry[], where: 'after' | 'before' = 'after'): MenuGroups {\n let placed = false;\n const out = groups.map(group => {\n const at = group.findIndex(entry => entryId(entry) === anchor);\n if (at < 0 || placed) return group;\n placed = true;\n const index = where === 'after' ? at + 1 : at;\n return [...group.slice(0, index), ...items, ...group.slice(index)];\n });\n return placed ? out : [...out, items];\n}\n\n/** `groups` without the rows named. */\nexport function menuWithout(groups: MenuGroups, ...ids: string[]): MenuGroups {\n return groups.map(group => group.filter(entry => !ids.includes(entryId(entry))));\n}\n\n/** What the app serves, as far as the built-in rows care. */\nexport type MenuEnv = {\n /** Decks of site columns (`site.deck`). */\n decks: boolean;\n /** `menu.languages`. */\n languages: boolean;\n /** `site.themes`. */\n themes: boolean;\n /** `menu.modes` with at least one mode. */\n modes: boolean;\n /** `features`: native page zoom (`tab_zoom`), find in page (`tab_find`), the Store, workspaces, developer mode, the updater. */\n zoom: boolean;\n find: boolean;\n /** `features.safeMode`: safe mode for one run (`safe_mode_get/set`, the host's own). */\n safeMode: boolean;\n store: boolean;\n /** `features.strings`: the panel has the Languages section (string packs). */\n strings: boolean;\n workspaces: boolean;\n developer: boolean;\n updates: boolean;\n};\n\n/** Whether the app can serve a built-in row: a row that would do nothing is never shown. */\nexport function builtinAvailable(id: BuiltinMenuItem, env: MenuEnv): boolean {\n switch (id) {\n case 'new-column': return env.decks;\n case 'language': return env.languages;\n case 'theme': return env.themes;\n case 'mode': return env.modes;\n case 'zoom': return env.zoom;\n case 'find': return env.find;\n case 'safe-mode': return env.safeMode;\n case 'store': return env.store;\n case 'strings': return env.strings;\n case 'workspaces': return env.workspaces;\n case 'developer': return env.developer;\n case 'updates': return env.updates;\n default: return true;\n }\n}\n\nconst isBuiltin = (id: string): id is BuiltinMenuItem => (MENU_BUILTINS as readonly string[]).includes(id);\n\n/**\n * The entries the menu shows, group by group: built-in rows the app cannot serve and rows whose\n * `when()` says no are left out, a row listed twice shows once, a group left empty goes (so no\n * rule is ever doubled), and an unknown name is ignored.\n */\nexport function planMenu(groups: MenuGroups, env: MenuEnv): MenuEntry[][] {\n const seen = new Set<string>();\n const out: MenuEntry[][] = [];\n for (const group of groups) {\n const kept: MenuEntry[] = [];\n for (const entry of group) {\n const id = entryId(entry);\n if (seen.has(id)) continue;\n if (typeof entry === 'string') {\n if (!isBuiltin(entry) || !builtinAvailable(entry, env)) continue;\n } else if (isBuiltin(id) || (entry.when && !entry.when())) {\n continue;\n }\n seen.add(id);\n kept.push(entry);\n }\n if (kept.length) out.push(kept);\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Rows as the menu draws them\n\nexport type MenuItemRow = {\n kind: 'item';\n id: string;\n label: string;\n icon?: IconName;\n shortcut?: string;\n hint?: string;\n disabled?: boolean;\n /** A choice among its neighbours (radio) or a toggle (checkbox), with `checked`. */\n check?: 'radio' | 'checkbox';\n checked?: boolean;\n /** Opens a submenu (its rows come from the menu, by this row's id). */\n submenu?: boolean;\n keepOpen?: boolean;\n /** The label's own language and direction (a language's native name). */\n lang?: string;\n dir?: 'ltr' | 'rtl';\n /** The row's tooltip (a plain sentence that does not fit the row). */\n title?: string;\n /** A theme's colors, drawn as three dots (`ThemeAdapter.dots`). */\n swatch?: { page: string; bg: string; border: string; text: string; accent: string };\n run?(): unknown;\n};\n\nexport type MenuRow =\n | MenuItemRow\n | { kind: 'separator' }\n /** Words that are not a choice: a question above its answers, an empty list, an error. */\n | { kind: 'note'; id: string; title: string; text?: string; tone?: 'error' }\n /** The zoom row: − 100% + and a reset, in one line. */\n | { kind: 'zoom'; id: 'zoom' };\n\n/** Groups of rows as one list with a rule between two groups. */\nexport function joinGroups(groups: ReadonlyArray<readonly MenuRow[]>): MenuRow[] {\n const out: MenuRow[] = [];\n for (const group of groups) {\n if (!group.length) continue;\n if (out.length) out.push({ kind: 'separator' });\n out.push(...group);\n }\n return out;\n}\n\n/** The app's own entries as rows (`submenu` rows are looked up again when they open). */\nexport function specRows(specs: readonly MenuItemSpec[]): MenuRow[] {\n const out: MenuRow[] = [];\n for (const spec of specs) {\n if (spec.when && !spec.when()) continue;\n if (spec.separated && out.length) out.push({ kind: 'separator' });\n const checked = spec.checked?.();\n out.push({\n kind: 'item', id: spec.id, label: spec.label(), icon: spec.icon, shortcut: spec.shortcut, hint: spec.hint?.(),\n check: checked === undefined ? undefined : 'checkbox', checked, submenu: !!spec.submenu, keepOpen: spec.keepOpen, run: spec.run,\n });\n }\n return out;\n}\n\n/** The app's entry with this id, anywhere in its groups or their submenus. */\nexport function findSpec(groups: MenuGroups, id: string): MenuItemSpec | null {\n const walk = (specs: readonly MenuItemSpec[], depth: number): MenuItemSpec | null => {\n for (const spec of specs) {\n if (spec.id === id) return spec;\n const inner = depth < 3 && spec.submenu ? walk(spec.submenu(), depth + 1) : null;\n if (inner) return inner;\n }\n return null;\n };\n return walk(groups.flat().filter((entry): entry is MenuItemSpec => typeof entry !== 'string'), 0);\n}\n\n// ---------------------------------------------------------------------------------------------\n// Keyboard\n\n/** Arrow keys, Home and End over `count` stops; wraps around. -1 (nothing focused) enters at an end. */\nexport function moveStop(index: number, key: string, count: number): number {\n if (count <= 0) return -1;\n if (key === 'Home') return 0;\n if (key === 'End') return count - 1;\n if (key === 'ArrowDown') return index < 0 ? 0 : (index + 1) % count;\n if (key === 'ArrowUp') return index < 0 ? count - 1 : (index - 1 + count) % count;\n return index;\n}\n\nconst fold = (text: string) => text.normalize('NFKD').replace(/\\p{M}/gu, '').toLocaleLowerCase();\n\n/**\n * Type-ahead: the next stop after `from` whose label starts with what was typed (accents and case\n * ignored). One letter typed again cycles through the labels that start with it. -1: none.\n */\nexport function typeahead(labels: readonly string[], from: number, typed: string): number {\n const text = fold(typed);\n if (!text || !labels.length) return -1;\n const chars = [...text];\n const repeat = chars.length > 1 && chars.every(char => char === chars[0]);\n const needle = repeat ? chars[0]! : text;\n // While a word is being typed the focused row may keep matching; a repeated letter moves on.\n const start = chars.length > 1 && !repeat ? Math.max(0, from) : from + 1;\n for (let step = 0; step < labels.length; step += 1) {\n const index = (start + step + labels.length) % labels.length;\n if (fold(labels[index]!).startsWith(needle)) return index;\n }\n return -1;\n}\n\n/** The keys that open and close a submenu: they follow the reading direction. */\nexport function submenuKeys(rtl: boolean): { open: string; close: string } {\n return rtl ? { open: 'ArrowLeft', close: 'ArrowRight' } : { open: 'ArrowRight', close: 'ArrowLeft' };\n}\n\n/**\n * The left edge of a submenu `width` wide beside its menu (`parent`), in a window `viewport` wide.\n * The menu hangs at one edge of the window (under its button at the end, under the logo at the\n * start): the submenu opens toward the middle, else on the other side, else over the menu, and\n * always stays `edge` pixels inside the window.\n */\nexport function submenuLeft(parent: { left: number; right: number }, width: number, viewport: number, edge = 8): number {\n const overlap = 3;\n const before = parent.left - width + overlap;\n const after = parent.right - overlap;\n const fits = (x: number) => x >= edge && x + width <= viewport - edge;\n const first = (parent.left + parent.right) / 2 > viewport / 2 ? before : after;\n const second = first === before ? after : before;\n if (fits(first)) return first;\n if (fits(second)) return second;\n return Math.max(edge, Math.min(first, viewport - width - edge));\n}\n\n/** A shortcut as the platform writes it: `Ctrl+Shift+T` on Windows and Linux, `⇧⌘T` on macOS. */\nexport function shortcutText(shortcut: string, mac: boolean): string {\n const parts = shortcut.split('+').map(part => part.trim()).filter(Boolean);\n if (!mac) return parts.join('+');\n const symbols: Record<string, string> = { ctrl: '⌘', cmd: '⌘', shift: '⇧', alt: '⌥' };\n const order = ['alt', 'shift', 'ctrl', 'cmd'];\n const mods = parts.filter(part => part.toLowerCase() in symbols).sort((a, b) => order.indexOf(a.toLowerCase()) - order.indexOf(b.toLowerCase()));\n const keys = parts.filter(part => !(part.toLowerCase() in symbols));\n return `${mods.map(part => symbols[part.toLowerCase()]).join('')}${keys.join('')}`;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Language\n\n/**\n * The Language submenu: follow the site, follow the system, then the supported languages by their\n * own names (the one chosen is checked; a community language in use is listed too, so the check is\n * never missing), then the way to the full list in Settings.\n */\nexport function languageRows(\n list: readonly MenuLanguage[],\n state: { preference: string; siteLang: string | null; locale: string },\n words: { site: string; system: string; more: string },\n): MenuRow[] {\n const named = (code: string | null) => (code ? list.find(language => language.code === code)?.name : undefined);\n const automatic: MenuRow[] = [\n { kind: 'item', id: 'lang:site', label: words.site, check: 'radio', checked: state.preference === 'site', hint: state.preference === 'site' ? named(state.locale) : named(state.siteLang) },\n { kind: 'item', id: 'lang:system', label: words.system, check: 'radio', checked: state.preference === 'system', hint: state.preference === 'system' ? named(state.locale) : undefined },\n ];\n const shown = list.filter(language => language.tier !== 'community' || language.code === state.preference);\n const languages: MenuRow[] = shown.map(language => ({\n kind: 'item', id: `lang:${language.code}`, label: language.name, check: 'radio', checked: state.preference === language.code,\n hint: language.name === language.englishName ? undefined : language.englishName, lang: language.code, dir: language.dir,\n }));\n return joinGroups([automatic, languages, [{ kind: 'item', id: 'lang:more', label: words.more }]]);\n}\n\n/** The preference a Language row stands for (`lang:tr` → `tr`), or null for \"More languages…\". */\nexport function languageChoice(id: string): string | null {\n const code = id.startsWith('lang:') ? id.slice(5) : '';\n return code && code !== 'more' ? code : null;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Zoom\n\n/** Page zoom limits, as native code clamps them (waybetter-host `zoom.rs`). */\nexport const ZOOM_MIN = 0.25;\nexport const ZOOM_MAX = 5;\n/** The steps − and + move through (a browser's). */\nexport const ZOOM_STEPS = [0.25, 0.33, 0.5, 0.67, 0.75, 0.8, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5] as const;\n\n/** The next step up (`1`) or down (`-1`) from `factor`, or 100% (`0`). Stays inside the limits. */\nexport function zoomStep(factor: number, direction: 1 | -1 | 0): number {\n if (direction === 0 || !Number.isFinite(factor)) return 1;\n const near = 0.005;\n if (direction > 0) return ZOOM_STEPS.find(step => step > factor + near) ?? ZOOM_MAX;\n return [...ZOOM_STEPS].reverse().find(step => step < factor - near) ?? ZOOM_MIN;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Updates\n\n/** The Updates row: what it says and what choosing it does. */\nexport type UpdateRow = {\n label: string;\n /** Quiet text at the end of the row: the state, or the version while nothing is happening. */\n hint?: string;\n /** A plain sentence for the row's tooltip (why a check did not get through). */\n title?: string;\n /** `check`: ask the updater now. `restart`: install the downloaded update. `open`: the Updates section. `wait`: nothing, a step is running. */\n action: 'check' | 'restart' | 'open' | 'wait';\n};\n\n/**\n * The Updates row for the updater's status. `asked`: the owner asked for a check in this menu;\n * only then does a check that did not get through say so (the timer's own failures stay quiet),\n * and it says it calmly: a short state, never the error text.\n */\nexport function updateRow(status: UpdateStatus, asked: boolean, version: string): UpdateRow {\n const check = menuText('updateCheck');\n const quiet = version || undefined;\n switch (status.state) {\n case 'ready': return { label: menuText('updateRestart'), hint: status.version || quiet, action: 'restart' };\n case 'installing': return { label: check, hint: menuText('updateInstalling', { version: status.version }), action: 'wait' };\n case 'downloading': return { label: check, hint: menuText('updateDownloading', { version: status.version }), action: 'wait' };\n case 'checking': return { label: check, hint: menuText('updateChecking'), action: 'wait' };\n case 'problem': return { label: tx('wb.update.problemLabel'), hint: quiet, action: 'open' };\n case 'upToDate': return { label: check, hint: version ? menuText('updateUpToDateVersion', { version }) : menuText('updateUpToDate'), action: 'check' };\n case 'error': return asked ? { label: check, hint: menuText('updateFailed'), title: menuText('updateFailedHint'), action: 'check' } : { label: check, hint: quiet, action: 'check' };\n default: return { label: check, hint: quiet, action: 'check' };\n }\n}\n\n// ---------------------------------------------------------------------------------------------\n// Find in page\n\n/**\n * What the find bar says about a search: \"3/17\" (the active match of the total), the total alone\n * while no match is active, \"No matches\" for none. `number` writes a number in the active language.\n */\nexport function findLabel(counts: { count: number; index: number }, number: (value: number) => string = String): string {\n if (counts.count <= 0) return menuText('findNone');\n if (counts.index < 0 || counts.index >= counts.count) return menuText('findTotal', { count: counts.count });\n return menuText('findCount', { index: number(counts.index + 1), count: number(counts.count) });\n}\n\n/** Whole percent of a factor (1.1 → 110). */\nexport const zoomPercent = (factor: number) => Math.round(factor * 100);\n"]}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// The native menu bar (macOS): the shell describes it (`menu_set`, shell-only) from the same state the
|
|
2
|
+
// in-app menu reads, and runs what the system menu chooses (`native-menu`, an id). Every item goes
|
|
3
|
+
// through what already exists (`shell.shortcut`, `shell.run`, `shell.openPanelSection`, the theme store,
|
|
4
|
+
// the profile list, the safe-mode switch, the workspaces); nothing here is a new capability and tabs
|
|
5
|
+
// gain no command. Elsewhere (Windows, Linux) the in-app menu stays and this does nothing.
|
|
6
|
+
//
|
|
7
|
+
// The bar is described again whenever what it shows changes (a theme or mode picked, an account added,
|
|
8
|
+
// a pane focused, safe mode, workspaces, a plugin switched); equal descriptions are not sent twice.
|
|
9
|
+
import { parseUpdateStatus } from "../updateModel.js";
|
|
10
|
+
import { addAccount, accountName } from "./AccountSwitcher.js";
|
|
11
|
+
import { invoke, listen } from "./bridge.js";
|
|
12
|
+
import { subscribeText, t } from "./i18n.js";
|
|
13
|
+
import { menuText } from "./menuModel.js";
|
|
14
|
+
import { planNativeMenu, runNativeMenu } from "./nativeMenuModel.js";
|
|
15
|
+
import { safe, setSafeMode } from "./safeStore.js";
|
|
16
|
+
import { hasFeature, site } from "./site.js";
|
|
17
|
+
import { isWeb, shell } from "./state.js";
|
|
18
|
+
import { openAppMenu, openReport } from "./AppMenu.js";
|
|
19
|
+
import { isTauri, os } from "./windowBinding.js";
|
|
20
|
+
import { openWorkspaceSave } from "./TitlebarExtras.js";
|
|
21
|
+
import { restoreWorkspace, workspaceList, workspaces } from "./workspaces.js";
|
|
22
|
+
/** Events (window) after which something the bar shows may have changed. */
|
|
23
|
+
const EVENTS = ['bt-zoom', 'bt-workspaces', 'bt-decks', 'bt-site', 'bt-safe', 'bt-profiles', 'bt-active-pane', 'bt-plugins-changed', 'bt-store-changed', 'wb-features-changed'];
|
|
24
|
+
const w = (name, params) => menuText(name, params);
|
|
25
|
+
/** The state the bar shows now. Async parts (the mode in use, the app's plugin list) are awaited here. */
|
|
26
|
+
async function readState() {
|
|
27
|
+
const config = site();
|
|
28
|
+
const menu = config.menu ?? {};
|
|
29
|
+
const native = menu.native ?? {};
|
|
30
|
+
const active = shell.api?.activePanel;
|
|
31
|
+
const themeState = config.themes?.current();
|
|
32
|
+
const themeId = config.themes && themeState ? config.themes.adapter.preset(themeState.choice) : '';
|
|
33
|
+
const themes = config.themes && themeState
|
|
34
|
+
? config.themes.allThemes(themeState.choice).map(theme => ({ id: theme.id, label: theme.name, checked: theme.id === themeId }))
|
|
35
|
+
: undefined;
|
|
36
|
+
let modes;
|
|
37
|
+
if (menu.modes) {
|
|
38
|
+
const current = await menu.modes.current().catch(() => null);
|
|
39
|
+
modes = menu.modes.list().map(entry => ({ id: entry.id, label: entry.label, checked: entry.id === current }));
|
|
40
|
+
}
|
|
41
|
+
const plugins = native.plugins ? await Promise.resolve(native.plugins.list()).catch(() => undefined) : undefined;
|
|
42
|
+
const list = workspaceList();
|
|
43
|
+
return {
|
|
44
|
+
product: config.product,
|
|
45
|
+
hasPanel: !!active,
|
|
46
|
+
canSplit: !!active && (isWeb(active) || active.group.panels.length > 1),
|
|
47
|
+
canFind: (shell.api?.panels ?? []).some(panel => isWeb(panel) && panel.api.isVisible),
|
|
48
|
+
safeActive: safe.active,
|
|
49
|
+
features: {
|
|
50
|
+
find: hasFeature('find'), zoom: hasFeature('zoom'), deck: !!config.deck, store: hasFeature('store'),
|
|
51
|
+
safeMode: hasFeature('safeMode'), workspaces: hasFeature('workspaces'), updates: hasFeature('updates'),
|
|
52
|
+
},
|
|
53
|
+
themes,
|
|
54
|
+
iconPacks: native.iconPacks?.list(),
|
|
55
|
+
modes,
|
|
56
|
+
accounts: shell.profiles.profiles.map(profile => ({ id: profile.id, label: accountName(profile), checked: profile.id === shell.profiles.active })),
|
|
57
|
+
plugins,
|
|
58
|
+
workspaces: hasFeature('workspaces') ? list.map(entry => ({ id: entry.id, label: entry.name, checked: entry.id === workspaces.active })) : undefined,
|
|
59
|
+
installFromFile: !!native.installFromFile,
|
|
60
|
+
shareSetup: !!native.shareSetup,
|
|
61
|
+
docs: !!native.docs,
|
|
62
|
+
website: native.website?.label,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function actions() {
|
|
66
|
+
const menu = site().menu ?? {};
|
|
67
|
+
const native = menu.native ?? {};
|
|
68
|
+
const config = site();
|
|
69
|
+
return {
|
|
70
|
+
about: () => shell.openPanelSection('home'),
|
|
71
|
+
// A real check, then the panel's Updates section says what came of it.
|
|
72
|
+
checkUpdates: () => { void invoke('update_check').then(parseUpdateStatus).catch(() => { }); shell.openPanelSection('updates'); },
|
|
73
|
+
settings: () => shell.openPanelSection('settings'),
|
|
74
|
+
// Turning it on restarts every tab, so the in-app question (what it does, Cancel) asks first; turning it off is one step.
|
|
75
|
+
safeMode: () => { if (safe.active)
|
|
76
|
+
void setSafeMode(false);
|
|
77
|
+
else
|
|
78
|
+
openAppMenu({ submenu: 'safe-mode', keyboard: true }); },
|
|
79
|
+
newTab: () => shell.shortcut('new-tab'),
|
|
80
|
+
splitRight: () => shell.shortcut('split-right'),
|
|
81
|
+
splitDown: () => shell.shortcut('split-down'),
|
|
82
|
+
closeTab: () => shell.shortcut('close-tab'),
|
|
83
|
+
installFromFile: () => native.installFromFile?.(),
|
|
84
|
+
shareSetup: () => native.shareSetup?.(),
|
|
85
|
+
find: () => shell.shortcut('find'),
|
|
86
|
+
findNext: () => shell.shortcut('find-next'),
|
|
87
|
+
findPrevious: () => shell.shortcut('find-previous'),
|
|
88
|
+
zoomIn: () => shell.shortcut('zoom-in'),
|
|
89
|
+
zoomOut: () => shell.shortcut('zoom-out'),
|
|
90
|
+
zoomReset: () => shell.shortcut('zoom-reset'),
|
|
91
|
+
deck: () => shell.shortcut('deck-toggle'),
|
|
92
|
+
// As Appearance does: a theme starts without the customizations made on another one.
|
|
93
|
+
theme: id => { const themes = config.themes; const choice = themes?.current().choice; if (themes && choice)
|
|
94
|
+
void themes.setChoice(themes.adapter.choiceForId(choice, id)); },
|
|
95
|
+
iconPack: id => { void native.iconPacks?.set(id); },
|
|
96
|
+
// Switching asks about the owner's own changes first when there are any: that question is the in-app menu's.
|
|
97
|
+
mode: id => {
|
|
98
|
+
const modes = menu.modes;
|
|
99
|
+
if (!modes)
|
|
100
|
+
return;
|
|
101
|
+
void modes.changes(id).then(changes => { if (changes > 0)
|
|
102
|
+
openAppMenu({ submenu: 'mode', keyboard: true });
|
|
103
|
+
else
|
|
104
|
+
return modes.apply(id, false); }).catch(() => { });
|
|
105
|
+
},
|
|
106
|
+
account: id => shell.openAccount(id),
|
|
107
|
+
addAccount: () => { void addAccount(shell.profiles).catch(() => { }); },
|
|
108
|
+
plugin: (id, on) => { void native.plugins?.set(id, on); },
|
|
109
|
+
store: () => shell.openPanelSection('store'),
|
|
110
|
+
pluginsFolder: () => { void invoke('plugins_open_folder').catch(() => { }); },
|
|
111
|
+
workspace: id => { void restoreWorkspace(id); },
|
|
112
|
+
saveWorkspace: () => openWorkspaceSave(),
|
|
113
|
+
docs: () => native.docs?.(),
|
|
114
|
+
report: () => openReport(),
|
|
115
|
+
website: () => { const url = native.website?.url; if (url)
|
|
116
|
+
void invoke('open_external_url', { url }).catch(() => { }); },
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Starts the bar (once, after the shell is mounted). Returns a function that stops it.
|
|
121
|
+
* No-op where the system menu bar is not the app's (anything but macOS).
|
|
122
|
+
*/
|
|
123
|
+
export function startNativeMenu() {
|
|
124
|
+
if (os !== 'macos' || !isTauri)
|
|
125
|
+
return () => { };
|
|
126
|
+
let last = '';
|
|
127
|
+
let latest = null;
|
|
128
|
+
let timer;
|
|
129
|
+
let stopped = false;
|
|
130
|
+
const send = async () => {
|
|
131
|
+
try {
|
|
132
|
+
const state = await readState();
|
|
133
|
+
if (stopped)
|
|
134
|
+
return;
|
|
135
|
+
latest = state;
|
|
136
|
+
const spec = planNativeMenu(state, w, key => t(key));
|
|
137
|
+
const text = JSON.stringify(spec);
|
|
138
|
+
if (text === last)
|
|
139
|
+
return;
|
|
140
|
+
last = text;
|
|
141
|
+
await invoke('menu_set', { spec });
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
last = '';
|
|
145
|
+
console.error('[shell] menu_set', error);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const soon = () => { clearTimeout(timer); timer = setTimeout(() => void send(), 120); };
|
|
149
|
+
for (const event of EVENTS)
|
|
150
|
+
window.addEventListener(event, soon);
|
|
151
|
+
const config = site();
|
|
152
|
+
const offs = [
|
|
153
|
+
config.themes?.subscribe(soon), config.menu?.languages?.subscribe(soon), subscribeText(soon),
|
|
154
|
+
config.menu?.native?.plugins?.subscribe?.(soon), config.menu?.native?.iconPacks?.subscribe?.(soon),
|
|
155
|
+
];
|
|
156
|
+
let unlisten;
|
|
157
|
+
void listen('native-menu', event => {
|
|
158
|
+
if (typeof event.payload !== 'string' || !latest)
|
|
159
|
+
return;
|
|
160
|
+
// Focus goes back to the shell first: a choice made in the system menu is the shell's action.
|
|
161
|
+
runNativeMenu(event.payload, latest, actions());
|
|
162
|
+
soon();
|
|
163
|
+
}).then(off => { if (stopped)
|
|
164
|
+
off();
|
|
165
|
+
else
|
|
166
|
+
unlisten = off; }).catch(() => { });
|
|
167
|
+
soon();
|
|
168
|
+
return () => {
|
|
169
|
+
stopped = true;
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
for (const event of EVENTS)
|
|
172
|
+
window.removeEventListener(event, soon);
|
|
173
|
+
for (const off of offs)
|
|
174
|
+
off?.();
|
|
175
|
+
unlisten?.();
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=nativeMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nativeMenu.js","sourceRoot":"","sources":["../../../../../../shell/src/frame/nativeMenu.ts"],"names":[],"mappings":"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","sourcesContent":["// The native menu bar (macOS): the shell describes it (`menu_set`, shell-only) from the same state the\n// in-app menu reads, and runs what the system menu chooses (`native-menu`, an id). Every item goes\n// through what already exists (`shell.shortcut`, `shell.run`, `shell.openPanelSection`, the theme store,\n// the profile list, the safe-mode switch, the workspaces); nothing here is a new capability and tabs\n// gain no command. Elsewhere (Windows, Linux) the in-app menu stays and this does nothing.\n//\n// The bar is described again whenever what it shows changes (a theme or mode picked, an account added,\n// a pane focused, safe mode, workspaces, a plugin switched); equal descriptions are not sent twice.\n\nimport { parseUpdateStatus } from '../updateModel.ts';\nimport { addAccount, accountName } from './AccountSwitcher.tsx';\nimport { invoke, listen } from './bridge.ts';\nimport { subscribeText, t } from './i18n.ts';\nimport { menuText, type MenuWord } from './menuModel.ts';\nimport { planNativeMenu, runNativeMenu, type Choice, type NativeMenuActions, type NativeMenuState } from './nativeMenuModel.ts';\nimport { safe, setSafeMode } from './safeStore.ts';\nimport { hasFeature, site } from './site.ts';\nimport { isWeb, shell } from './state.ts';\nimport { openAppMenu, openReport } from './AppMenu.tsx';\nimport { isTauri, os } from './windowBinding.ts';\nimport { openWorkspaceSave } from './TitlebarExtras.tsx';\nimport { restoreWorkspace, workspaceList, workspaces } from './workspaces.ts';\n\n/** Events (window) after which something the bar shows may have changed. */\nconst EVENTS = ['bt-zoom', 'bt-workspaces', 'bt-decks', 'bt-site', 'bt-safe', 'bt-profiles', 'bt-active-pane', 'bt-plugins-changed', 'bt-store-changed', 'wb-features-changed'];\n\nconst w = (name: string, params?: Record<string, string | number>) => menuText(name as MenuWord, params);\n\n/** The state the bar shows now. Async parts (the mode in use, the app's plugin list) are awaited here. */\nasync function readState(): Promise<NativeMenuState> {\n const config = site();\n const menu = config.menu ?? {};\n const native = menu.native ?? {};\n const active = shell.api?.activePanel;\n const themeState = config.themes?.current();\n const themeId = config.themes && themeState ? config.themes.adapter.preset(themeState.choice) : '';\n const themes: Choice[] | undefined = config.themes && themeState\n ? config.themes.allThemes(themeState.choice).map(theme => ({ id: theme.id, label: theme.name, checked: theme.id === themeId }))\n : undefined;\n let modes: Choice[] | undefined;\n if (menu.modes) {\n const current = await menu.modes.current().catch(() => null);\n modes = menu.modes.list().map(entry => ({ id: entry.id, label: entry.label, checked: entry.id === current }));\n }\n const plugins = native.plugins ? await Promise.resolve(native.plugins.list()).catch(() => undefined) : undefined;\n const list = workspaceList();\n return {\n product: config.product,\n hasPanel: !!active,\n canSplit: !!active && (isWeb(active) || active.group.panels.length > 1),\n canFind: (shell.api?.panels ?? []).some(panel => isWeb(panel) && panel.api.isVisible),\n safeActive: safe.active,\n features: {\n find: hasFeature('find'), zoom: hasFeature('zoom'), deck: !!config.deck, store: hasFeature('store'),\n safeMode: hasFeature('safeMode'), workspaces: hasFeature('workspaces'), updates: hasFeature('updates'),\n },\n themes,\n iconPacks: native.iconPacks?.list(),\n modes,\n accounts: shell.profiles.profiles.map(profile => ({ id: profile.id, label: accountName(profile), checked: profile.id === shell.profiles.active })),\n plugins,\n workspaces: hasFeature('workspaces') ? list.map(entry => ({ id: entry.id, label: entry.name, checked: entry.id === workspaces.active })) : undefined,\n installFromFile: !!native.installFromFile,\n shareSetup: !!native.shareSetup,\n docs: !!native.docs,\n website: native.website?.label,\n };\n}\n\nfunction actions(): NativeMenuActions {\n const menu = site().menu ?? {};\n const native = menu.native ?? {};\n const config = site();\n return {\n about: () => shell.openPanelSection('home'),\n // A real check, then the panel's Updates section says what came of it.\n checkUpdates: () => { void invoke('update_check').then(parseUpdateStatus).catch(() => {}); shell.openPanelSection('updates'); },\n settings: () => shell.openPanelSection('settings'),\n // Turning it on restarts every tab, so the in-app question (what it does, Cancel) asks first; turning it off is one step.\n safeMode: () => { if (safe.active) void setSafeMode(false); else openAppMenu({ submenu: 'safe-mode', keyboard: true }); },\n newTab: () => shell.shortcut('new-tab'),\n splitRight: () => shell.shortcut('split-right'),\n splitDown: () => shell.shortcut('split-down'),\n closeTab: () => shell.shortcut('close-tab'),\n installFromFile: () => native.installFromFile?.(),\n shareSetup: () => native.shareSetup?.(),\n find: () => shell.shortcut('find'),\n findNext: () => shell.shortcut('find-next'),\n findPrevious: () => shell.shortcut('find-previous'),\n zoomIn: () => shell.shortcut('zoom-in'),\n zoomOut: () => shell.shortcut('zoom-out'),\n zoomReset: () => shell.shortcut('zoom-reset'),\n deck: () => shell.shortcut('deck-toggle'),\n // As Appearance does: a theme starts without the customizations made on another one.\n theme: id => { const themes = config.themes; const choice = themes?.current().choice; if (themes && choice) void themes.setChoice(themes.adapter.choiceForId(choice, id)); },\n iconPack: id => { void native.iconPacks?.set(id); },\n // Switching asks about the owner's own changes first when there are any: that question is the in-app menu's.\n mode: id => {\n const modes = menu.modes;\n if (!modes) return;\n void modes.changes(id).then(changes => { if (changes > 0) openAppMenu({ submenu: 'mode', keyboard: true }); else return modes.apply(id, false); }).catch(() => {});\n },\n account: id => shell.openAccount(id),\n addAccount: () => { void addAccount(shell.profiles).catch(() => {}); },\n plugin: (id, on) => { void native.plugins?.set(id, on); },\n store: () => shell.openPanelSection('store'),\n pluginsFolder: () => { void invoke('plugins_open_folder').catch(() => {}); },\n workspace: id => { void restoreWorkspace(id); },\n saveWorkspace: () => openWorkspaceSave(),\n docs: () => native.docs?.(),\n report: () => openReport(),\n website: () => { const url = native.website?.url; if (url) void invoke('open_external_url', { url }).catch(() => {}); },\n };\n}\n\n/**\n * Starts the bar (once, after the shell is mounted). Returns a function that stops it.\n * No-op where the system menu bar is not the app's (anything but macOS).\n */\nexport function startNativeMenu(): () => void {\n if (os !== 'macos' || !isTauri) return () => {};\n let last = '';\n let latest: NativeMenuState | null = null;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n\n const send = async () => {\n try {\n const state = await readState();\n if (stopped) return;\n latest = state;\n const spec = planNativeMenu(state, w, key => t(key));\n const text = JSON.stringify(spec);\n if (text === last) return;\n last = text;\n await invoke('menu_set', { spec });\n } catch (error) {\n last = '';\n console.error('[shell] menu_set', error);\n }\n };\n const soon = () => { clearTimeout(timer); timer = setTimeout(() => void send(), 120); };\n\n for (const event of EVENTS) window.addEventListener(event, soon);\n const config = site();\n const offs = [\n config.themes?.subscribe(soon), config.menu?.languages?.subscribe(soon), subscribeText(soon),\n config.menu?.native?.plugins?.subscribe?.(soon), config.menu?.native?.iconPacks?.subscribe?.(soon),\n ];\n let unlisten: (() => void) | undefined;\n void listen<string>('native-menu', event => {\n if (typeof event.payload !== 'string' || !latest) return;\n // Focus goes back to the shell first: a choice made in the system menu is the shell's action.\n runNativeMenu(event.payload, latest, actions());\n soon();\n }).then(off => { if (stopped) off(); else unlisten = off; }).catch(() => {});\n soon();\n\n return () => {\n stopped = true;\n clearTimeout(timer);\n for (const event of EVENTS) window.removeEventListener(event, soon);\n for (const off of offs) off?.();\n unlisten?.();\n };\n}\n"]}
|