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,250 @@
|
|
|
1
|
+
// Workspaces: named snapshots of the tab/pane/deck layout (each tab's URL, account profile and pane
|
|
2
|
+
// theme), restored with one click, switched from Ctrl+K and the titlebar menu, one optionally
|
|
3
|
+
// opened on start. Pure (no Tauri/DOM imports) so it is unit-tested under node. Stored natively
|
|
4
|
+
// (`workspaces_get` / `workspaces_set`; waybetter-host `workspaces` re-checks the same bounds).
|
|
5
|
+
//
|
|
6
|
+
// A workspace keeps dockview's own serialized layout, reduced to what reopening needs: panels of
|
|
7
|
+
// the kinds the shell knows, their params without transient state (loading, nonces), http(s)
|
|
8
|
+
// URLs only. Restoring gives every web/new-tab page a fresh id (so it opens as a new webview and
|
|
9
|
+
// never inherits a live tab's state); deck tabs and the app's panel keep their fixed ids.
|
|
10
|
+
import { t } from "./i18n.js";
|
|
11
|
+
export const WORKSPACE_LIMITS = Object.freeze({
|
|
12
|
+
/** Saved workspaces. */
|
|
13
|
+
count: 20,
|
|
14
|
+
name: 60,
|
|
15
|
+
/** Tabs in one workspace. */
|
|
16
|
+
tabs: 64,
|
|
17
|
+
/** Serialized size of one workspace (JSON characters). */
|
|
18
|
+
bytes: 256 * 1024,
|
|
19
|
+
url: 4096,
|
|
20
|
+
title: 300,
|
|
21
|
+
});
|
|
22
|
+
export const EMPTY_WORKSPACES = { version: 1, default: null, items: [] };
|
|
23
|
+
const KINDS = new Set(['web', 'panel', 'start', 'deck']);
|
|
24
|
+
/** Params each kind keeps; everything else (loading, nonce, favicon, besideOf, ...) is dropped. */
|
|
25
|
+
const KEEP = {
|
|
26
|
+
web: ['kind', 'url', 'profile', 'title', 'pinned'],
|
|
27
|
+
panel: ['kind', 'section', 'pinned'],
|
|
28
|
+
start: ['kind', 'profile', 'pinned'],
|
|
29
|
+
deck: ['kind', 'deckId', 'pinned'],
|
|
30
|
+
};
|
|
31
|
+
const ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
32
|
+
const THEME_ID = /^[a-z0-9-]{1,40}$/;
|
|
33
|
+
const WORKSPACE_ID = /^[a-z0-9-]{1,40}$/;
|
|
34
|
+
const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
35
|
+
export function httpUrl(value) {
|
|
36
|
+
if (typeof value !== 'string' || value.length > WORKSPACE_LIMITS.url)
|
|
37
|
+
return null;
|
|
38
|
+
try {
|
|
39
|
+
const url = new URL(value);
|
|
40
|
+
return url.protocol === 'https:' || url.protocol === 'http:' ? url.href : null;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function cleanName(value) {
|
|
47
|
+
// eslint-disable-next-line no-control-regex
|
|
48
|
+
return typeof value === 'string' ? value.replace(/[\u0000-\u001f\u007f]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, WORKSPACE_LIMITS.name) : '';
|
|
49
|
+
}
|
|
50
|
+
/** A slug for a new workspace id, unique among `taken`. */
|
|
51
|
+
export function workspaceId(name, taken) {
|
|
52
|
+
const used = new Set(taken);
|
|
53
|
+
const base = cleanName(name).toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 32) || 'workspace';
|
|
54
|
+
let id = base;
|
|
55
|
+
for (let n = 2; used.has(id); n += 1)
|
|
56
|
+
id = `${base}-${n}`;
|
|
57
|
+
return id;
|
|
58
|
+
}
|
|
59
|
+
/** A panel's params reduced to what reopening needs, or null when the panel cannot be kept. */
|
|
60
|
+
function cleanParams(params) {
|
|
61
|
+
if (!isObject(params) || typeof params.kind !== 'string' || !KINDS.has(params.kind))
|
|
62
|
+
return null;
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const key of KEEP[params.kind]) {
|
|
65
|
+
const value = params[key];
|
|
66
|
+
if (value === undefined || value === null)
|
|
67
|
+
continue;
|
|
68
|
+
if (key === 'url') {
|
|
69
|
+
const url = httpUrl(value);
|
|
70
|
+
if (!url)
|
|
71
|
+
return null;
|
|
72
|
+
out.url = url;
|
|
73
|
+
}
|
|
74
|
+
else if (key === 'pinned') {
|
|
75
|
+
if (value === true)
|
|
76
|
+
out.pinned = true;
|
|
77
|
+
}
|
|
78
|
+
else if (key === 'title') {
|
|
79
|
+
if (typeof value === 'string')
|
|
80
|
+
out.title = value.slice(0, WORKSPACE_LIMITS.title);
|
|
81
|
+
}
|
|
82
|
+
else if (typeof value === 'string' && value.length <= 64) {
|
|
83
|
+
out[key] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (params.kind === 'web' && !out.url)
|
|
87
|
+
return null;
|
|
88
|
+
if (params.kind === 'deck' && typeof out.deckId !== 'string')
|
|
89
|
+
return null;
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
/** Visits every group's `views`/`activeView` in the grid and in floating/popout groups. */
|
|
93
|
+
function mapGroups(value, fn) {
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
for (const item of value)
|
|
96
|
+
mapGroups(item, fn);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!isObject(value))
|
|
100
|
+
return;
|
|
101
|
+
if (Array.isArray(value.views))
|
|
102
|
+
fn(value);
|
|
103
|
+
for (const [key, child] of Object.entries(value))
|
|
104
|
+
if (key !== 'views')
|
|
105
|
+
mapGroups(child, fn);
|
|
106
|
+
}
|
|
107
|
+
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
108
|
+
/** Drops views whose panel is gone; returns the tab ids still referenced. */
|
|
109
|
+
function pruneViews(layout) {
|
|
110
|
+
const referenced = new Set();
|
|
111
|
+
mapGroups([layout.grid, layout.floatingGroups, layout.popoutGroups], group => {
|
|
112
|
+
const views = group.views.filter((id) => typeof id === 'string' && id in layout.panels);
|
|
113
|
+
group.views = views;
|
|
114
|
+
if (typeof group.activeView === 'string' && !views.includes(group.activeView))
|
|
115
|
+
group.activeView = views[0];
|
|
116
|
+
for (const id of views)
|
|
117
|
+
referenced.add(id);
|
|
118
|
+
});
|
|
119
|
+
return referenced;
|
|
120
|
+
}
|
|
121
|
+
/** Makes a workspace from the live dockview layout. Throws when there is nothing to keep or it is too large. */
|
|
122
|
+
export function captureWorkspace(input) {
|
|
123
|
+
const name = cleanName(input.name);
|
|
124
|
+
if (!name)
|
|
125
|
+
throw new Error('A workspace needs a name.');
|
|
126
|
+
if (!WORKSPACE_ID.test(input.id))
|
|
127
|
+
throw new Error(t('wb.ws.err.id'));
|
|
128
|
+
const raw = input.layout;
|
|
129
|
+
if (!isObject(raw) || !isObject(raw.grid) || !isObject(raw.panels))
|
|
130
|
+
throw new Error(t('wb.ws.err.noLayout'));
|
|
131
|
+
const layout = clone(raw);
|
|
132
|
+
const panels = {};
|
|
133
|
+
for (const [id, panel] of Object.entries(layout.panels)) {
|
|
134
|
+
if (!ID.test(id) || !isObject(panel))
|
|
135
|
+
continue;
|
|
136
|
+
const params = cleanParams(panel.params);
|
|
137
|
+
if (!params)
|
|
138
|
+
continue;
|
|
139
|
+
const title = typeof panel.title === 'string' ? panel.title.slice(0, WORKSPACE_LIMITS.title) : undefined;
|
|
140
|
+
panels[id] = { id, contentComponent: panel.contentComponent, params, ...(title ? { title } : {}) };
|
|
141
|
+
}
|
|
142
|
+
layout.panels = panels;
|
|
143
|
+
const kept = pruneViews(layout);
|
|
144
|
+
for (const id of Object.keys(layout.panels))
|
|
145
|
+
if (!kept.has(id))
|
|
146
|
+
delete layout.panels[id];
|
|
147
|
+
const count = Object.keys(layout.panels).length;
|
|
148
|
+
if (!count)
|
|
149
|
+
throw new Error(t('wb.ws.err.noTabs'));
|
|
150
|
+
if (count > WORKSPACE_LIMITS.tabs)
|
|
151
|
+
throw new Error(`A workspace holds at most ${WORKSPACE_LIMITS.tabs} tabs.`);
|
|
152
|
+
const paneThemes = {};
|
|
153
|
+
for (const [tab, theme] of Object.entries(input.paneThemes ?? {})) {
|
|
154
|
+
if (tab in layout.panels && typeof theme === 'string' && THEME_ID.test(theme))
|
|
155
|
+
paneThemes[tab] = theme;
|
|
156
|
+
}
|
|
157
|
+
const deckIds = new Set(Object.values(layout.panels).map(panel => panel.params.deckId).filter((id) => typeof id === 'string'));
|
|
158
|
+
const decks = (input.decks ?? []).filter(deck => deckIds.has(deck.id));
|
|
159
|
+
const workspace = {
|
|
160
|
+
id: input.id,
|
|
161
|
+
name,
|
|
162
|
+
savedAt: (input.now ?? new Date()).toISOString(),
|
|
163
|
+
layout,
|
|
164
|
+
paneThemes,
|
|
165
|
+
...(decks.length ? { decks: clone(decks) } : {}),
|
|
166
|
+
};
|
|
167
|
+
if (JSON.stringify(workspace).length > WORKSPACE_LIMITS.bytes)
|
|
168
|
+
throw new Error(t('wb.ws.err.tooLarge'));
|
|
169
|
+
return workspace;
|
|
170
|
+
}
|
|
171
|
+
/** Tabs of a workspace in layout order (for menus: "3 tabs · x.com/home, …"). */
|
|
172
|
+
export function workspaceTabs(workspace) {
|
|
173
|
+
const order = [];
|
|
174
|
+
mapGroups([workspace.layout.grid, workspace.layout.floatingGroups], group => { for (const id of group.views)
|
|
175
|
+
if (!order.includes(id))
|
|
176
|
+
order.push(id); });
|
|
177
|
+
return order.map(id => workspace.layout.panels[id]?.params).filter((tab) => !!tab);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The layout to hand dockview's `fromJSON`: web and new-tab pages get fresh ids from `newId`
|
|
181
|
+
* (a live tab never keeps its webview across a switch), deck and wayBetter tabs keep theirs.
|
|
182
|
+
* `profileKnown` drops accounts that no longer exist (the tab opens in the default account).
|
|
183
|
+
*/
|
|
184
|
+
export function planRestore(workspace, newId, profileKnown = () => true) {
|
|
185
|
+
const layout = clone(workspace.layout);
|
|
186
|
+
const ids = {};
|
|
187
|
+
const panels = {};
|
|
188
|
+
for (const [id, panel] of Object.entries(layout.panels)) {
|
|
189
|
+
const params = cleanParams(panel.params);
|
|
190
|
+
if (!params)
|
|
191
|
+
continue;
|
|
192
|
+
if (typeof params.profile === 'string' && !profileKnown(params.profile))
|
|
193
|
+
delete params.profile;
|
|
194
|
+
const fresh = params.kind === 'web' || params.kind === 'start' ? newId() : id;
|
|
195
|
+
ids[id] = fresh;
|
|
196
|
+
panels[fresh] = { ...panel, id: fresh, params };
|
|
197
|
+
}
|
|
198
|
+
layout.panels = panels;
|
|
199
|
+
mapGroups([layout.grid, layout.floatingGroups, layout.popoutGroups], group => {
|
|
200
|
+
group.views = group.views.map(id => (typeof id === 'string' ? ids[id] : undefined)).filter((id) => !!id);
|
|
201
|
+
if (typeof group.activeView === 'string')
|
|
202
|
+
group.activeView = ids[group.activeView] ?? group.views[0];
|
|
203
|
+
});
|
|
204
|
+
pruneViews(layout);
|
|
205
|
+
const paneThemes = {};
|
|
206
|
+
for (const [tab, theme] of Object.entries(workspace.paneThemes ?? {}))
|
|
207
|
+
if (ids[tab])
|
|
208
|
+
paneThemes[ids[tab]] = theme;
|
|
209
|
+
return { layout, paneThemes, ids };
|
|
210
|
+
}
|
|
211
|
+
/** Parses what native code returned; anything malformed is dropped, never thrown. */
|
|
212
|
+
export function normalizeWorkspaces(value) {
|
|
213
|
+
if (!isObject(value) || !Array.isArray(value.items))
|
|
214
|
+
return { ...EMPTY_WORKSPACES, items: [] };
|
|
215
|
+
const items = [];
|
|
216
|
+
for (const item of value.items) {
|
|
217
|
+
if (!isObject(item) || typeof item.id !== 'string' || !WORKSPACE_ID.test(item.id) || items.some(other => other.id === item.id))
|
|
218
|
+
continue;
|
|
219
|
+
const name = cleanName(item.name);
|
|
220
|
+
if (!name || !isObject(item.layout) || !isObject(item.layout.grid) || !isObject(item.layout.panels))
|
|
221
|
+
continue;
|
|
222
|
+
const paneThemes = isObject(item.paneThemes) ? Object.fromEntries(Object.entries(item.paneThemes).filter(([, theme]) => typeof theme === 'string' && THEME_ID.test(theme))) : {};
|
|
223
|
+
items.push({ id: item.id, name, savedAt: typeof item.savedAt === 'string' ? item.savedAt : '', layout: item.layout, paneThemes, ...(Array.isArray(item.decks) ? { decks: item.decks } : {}) });
|
|
224
|
+
if (items.length >= WORKSPACE_LIMITS.count)
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
const fallback = typeof value.default === 'string' && items.some(item => item.id === value.default) ? value.default : null;
|
|
228
|
+
return { version: 1, default: fallback, items };
|
|
229
|
+
}
|
|
230
|
+
/** Adds or replaces (same id) a workspace; the list stays bounded. */
|
|
231
|
+
export function upsertWorkspace(store, workspace) {
|
|
232
|
+
const exists = store.items.some(item => item.id === workspace.id);
|
|
233
|
+
if (!exists && store.items.length >= WORKSPACE_LIMITS.count)
|
|
234
|
+
throw new Error(t('wb.ws.err.tooMany', { count: WORKSPACE_LIMITS.count }));
|
|
235
|
+
const items = exists ? store.items.map(item => (item.id === workspace.id ? workspace : item)) : [...store.items, workspace];
|
|
236
|
+
return { ...store, items };
|
|
237
|
+
}
|
|
238
|
+
export function removeWorkspace(store, id) {
|
|
239
|
+
return { version: 1, default: store.default === id ? null : store.default, items: store.items.filter(item => item.id !== id) };
|
|
240
|
+
}
|
|
241
|
+
export function renameWorkspace(store, id, name) {
|
|
242
|
+
const clean = cleanName(name);
|
|
243
|
+
if (!clean)
|
|
244
|
+
throw new Error('A workspace needs a name.');
|
|
245
|
+
return { ...store, items: store.items.map(item => (item.id === id ? { ...item, name: clean } : item)) };
|
|
246
|
+
}
|
|
247
|
+
export function setDefaultWorkspace(store, id) {
|
|
248
|
+
return { ...store, default: id && store.items.some(item => item.id === id) ? id : null };
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=workspaceModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspaceModel.js","sourceRoot":"","sources":["../../../../../../shell/src/frame/workspaceModel.ts"],"names":[],"mappings":"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","sourcesContent":["// Workspaces: named snapshots of the tab/pane/deck layout (each tab's URL, account profile and pane\n// theme), restored with one click, switched from Ctrl+K and the titlebar menu, one optionally\n// opened on start. Pure (no Tauri/DOM imports) so it is unit-tested under node. Stored natively\n// (`workspaces_get` / `workspaces_set`; waybetter-host `workspaces` re-checks the same bounds).\n//\n// A workspace keeps dockview's own serialized layout, reduced to what reopening needs: panels of\n// the kinds the shell knows, their params without transient state (loading, nonces), http(s)\n// URLs only. Restoring gives every web/new-tab page a fresh id (so it opens as a new webview and\n// never inherits a live tab's state); deck tabs and the app's panel keep their fixed ids.\n\nimport { t } from './i18n.ts';\n\nexport const WORKSPACE_LIMITS = Object.freeze({\n /** Saved workspaces. */\n count: 20,\n name: 60,\n /** Tabs in one workspace. */\n tabs: 64,\n /** Serialized size of one workspace (JSON characters). */\n bytes: 256 * 1024,\n url: 4096,\n title: 300,\n});\n\nexport type WorkspaceTab = { kind: 'web' | 'panel' | 'start' | 'deck'; url?: string; profile?: string; title?: string };\n\nexport type Workspace = {\n id: string;\n name: string;\n savedAt: string;\n /** dockview's `SerializedDockview`, reduced (see `captureWorkspace`). */\n layout: SerializedLayout;\n /** Pane themes of this workspace's tabs: tab id (in `layout`) → theme id. */\n paneThemes: Record<string, string>;\n /** Decks the workspace shows (Power User Mode), so a deck deleted since can come back. */\n decks?: unknown[];\n};\n\nexport type WorkspaceStore = { version: 1; default: string | null; items: Workspace[] };\n\nexport const EMPTY_WORKSPACES: WorkspaceStore = { version: 1, default: null, items: [] };\n\ntype Json = Record<string, unknown>;\nexport type SerializedLayout = { grid: Json; panels: Record<string, Json>; activeGroup?: string; floatingGroups?: unknown[]; popoutGroups?: unknown[] };\n\nconst KINDS = new Set(['web', 'panel', 'start', 'deck']);\n/** Params each kind keeps; everything else (loading, nonce, favicon, besideOf, ...) is dropped. */\nconst KEEP: Record<string, readonly string[]> = {\n web: ['kind', 'url', 'profile', 'title', 'pinned'],\n panel: ['kind', 'section', 'pinned'],\n start: ['kind', 'profile', 'pinned'],\n deck: ['kind', 'deckId', 'pinned'],\n};\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\nconst THEME_ID = /^[a-z0-9-]{1,40}$/;\nconst WORKSPACE_ID = /^[a-z0-9-]{1,40}$/;\n\nconst isObject = (value: unknown): value is Json => !!value && typeof value === 'object' && !Array.isArray(value);\n\nexport function httpUrl(value: unknown): string | null {\n if (typeof value !== 'string' || value.length > WORKSPACE_LIMITS.url) return null;\n try {\n const url = new URL(value);\n return url.protocol === 'https:' || url.protocol === 'http:' ? url.href : null;\n } catch { return null; }\n}\n\nexport function cleanName(value: unknown): string {\n // eslint-disable-next-line no-control-regex\n return typeof value === 'string' ? value.replace(/[\\u0000-\\u001f\\u007f]+/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, WORKSPACE_LIMITS.name) : '';\n}\n\n/** A slug for a new workspace id, unique among `taken`. */\nexport function workspaceId(name: string, taken: Iterable<string>): string {\n const used = new Set(taken);\n const base = cleanName(name).toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 32) || 'workspace';\n let id = base;\n for (let n = 2; used.has(id); n += 1) id = `${base}-${n}`;\n return id;\n}\n\n/** A panel's params reduced to what reopening needs, or null when the panel cannot be kept. */\nfunction cleanParams(params: unknown): Json | null {\n if (!isObject(params) || typeof params.kind !== 'string' || !KINDS.has(params.kind)) return null;\n const out: Json = {};\n for (const key of KEEP[params.kind]!) {\n const value = params[key];\n if (value === undefined || value === null) continue;\n if (key === 'url') {\n const url = httpUrl(value);\n if (!url) return null;\n out.url = url;\n } else if (key === 'pinned') {\n if (value === true) out.pinned = true;\n } else if (key === 'title') {\n if (typeof value === 'string') out.title = value.slice(0, WORKSPACE_LIMITS.title);\n } else if (typeof value === 'string' && value.length <= 64) {\n out[key] = value;\n }\n }\n if (params.kind === 'web' && !out.url) return null;\n if (params.kind === 'deck' && typeof out.deckId !== 'string') return null;\n return out;\n}\n\n/** Visits every group's `views`/`activeView` in the grid and in floating/popout groups. */\nfunction mapGroups(value: unknown, fn: (group: Json) => void): void {\n if (Array.isArray(value)) { for (const item of value) mapGroups(item, fn); return; }\n if (!isObject(value)) return;\n if (Array.isArray(value.views)) fn(value);\n for (const [key, child] of Object.entries(value)) if (key !== 'views') mapGroups(child, fn);\n}\n\nconst clone = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\n/** Drops views whose panel is gone; returns the tab ids still referenced. */\nfunction pruneViews(layout: SerializedLayout): Set<string> {\n const referenced = new Set<string>();\n mapGroups([layout.grid, layout.floatingGroups, layout.popoutGroups], group => {\n const views = (group.views as unknown[]).filter((id): id is string => typeof id === 'string' && id in layout.panels);\n group.views = views;\n if (typeof group.activeView === 'string' && !views.includes(group.activeView)) group.activeView = views[0];\n for (const id of views) referenced.add(id);\n });\n return referenced;\n}\n\nexport type CaptureInput = {\n id: string;\n name: string;\n layout: unknown;\n /** The pane theme map (`ThemeScopes.panes`); only this layout's tabs are kept. */\n paneThemes?: Record<string, string>;\n decks?: Array<{ id: string }>;\n now?: Date;\n};\n\n/** Makes a workspace from the live dockview layout. Throws when there is nothing to keep or it is too large. */\nexport function captureWorkspace(input: CaptureInput): Workspace {\n const name = cleanName(input.name);\n if (!name) throw new Error('A workspace needs a name.');\n if (!WORKSPACE_ID.test(input.id)) throw new Error(t('wb.ws.err.id'));\n const raw = input.layout as Partial<SerializedLayout> | null;\n if (!isObject(raw) || !isObject(raw.grid) || !isObject(raw.panels)) throw new Error(t('wb.ws.err.noLayout'));\n const layout = clone(raw) as SerializedLayout;\n const panels: Record<string, Json> = {};\n for (const [id, panel] of Object.entries(layout.panels)) {\n if (!ID.test(id) || !isObject(panel)) continue;\n const params = cleanParams(panel.params);\n if (!params) continue;\n const title = typeof panel.title === 'string' ? panel.title.slice(0, WORKSPACE_LIMITS.title) : undefined;\n panels[id] = { id, contentComponent: panel.contentComponent, params, ...(title ? { title } : {}) };\n }\n layout.panels = panels;\n const kept = pruneViews(layout);\n for (const id of Object.keys(layout.panels)) if (!kept.has(id)) delete layout.panels[id];\n const count = Object.keys(layout.panels).length;\n if (!count) throw new Error(t('wb.ws.err.noTabs'));\n if (count > WORKSPACE_LIMITS.tabs) throw new Error(`A workspace holds at most ${WORKSPACE_LIMITS.tabs} tabs.`);\n const paneThemes: Record<string, string> = {};\n for (const [tab, theme] of Object.entries(input.paneThemes ?? {})) {\n if (tab in layout.panels && typeof theme === 'string' && THEME_ID.test(theme)) paneThemes[tab] = theme;\n }\n const deckIds = new Set(Object.values(layout.panels).map(panel => (panel.params as Json).deckId).filter((id): id is string => typeof id === 'string'));\n const decks = (input.decks ?? []).filter(deck => deckIds.has(deck.id));\n const workspace: Workspace = {\n id: input.id,\n name,\n savedAt: (input.now ?? new Date()).toISOString(),\n layout,\n paneThemes,\n ...(decks.length ? { decks: clone(decks) } : {}),\n };\n if (JSON.stringify(workspace).length > WORKSPACE_LIMITS.bytes) throw new Error(t('wb.ws.err.tooLarge'));\n return workspace;\n}\n\n/** Tabs of a workspace in layout order (for menus: \"3 tabs · x.com/home, …\"). */\nexport function workspaceTabs(workspace: Workspace): WorkspaceTab[] {\n const order: string[] = [];\n mapGroups([workspace.layout.grid, workspace.layout.floatingGroups], group => { for (const id of group.views as string[]) if (!order.includes(id)) order.push(id); });\n return order.map(id => workspace.layout.panels[id]?.params as WorkspaceTab | undefined).filter((tab): tab is WorkspaceTab => !!tab);\n}\n\nexport type RestorePlan = {\n layout: SerializedLayout;\n /** Pane themes for the restored tab ids. */\n paneThemes: Record<string, string>;\n /** Tab id in the workspace → id it gets now. */\n ids: Record<string, string>;\n};\n\n/**\n * The layout to hand dockview's `fromJSON`: web and new-tab pages get fresh ids from `newId`\n * (a live tab never keeps its webview across a switch), deck and wayBetter tabs keep theirs.\n * `profileKnown` drops accounts that no longer exist (the tab opens in the default account).\n */\nexport function planRestore(workspace: Workspace, newId: () => string, profileKnown: (id: string) => boolean = () => true): RestorePlan {\n const layout = clone(workspace.layout);\n const ids: Record<string, string> = {};\n const panels: Record<string, Json> = {};\n for (const [id, panel] of Object.entries(layout.panels)) {\n const params = cleanParams(panel.params);\n if (!params) continue;\n if (typeof params.profile === 'string' && !profileKnown(params.profile)) delete params.profile;\n const fresh = params.kind === 'web' || params.kind === 'start' ? newId() : id;\n ids[id] = fresh;\n panels[fresh] = { ...panel, id: fresh, params };\n }\n layout.panels = panels;\n mapGroups([layout.grid, layout.floatingGroups, layout.popoutGroups], group => {\n group.views = (group.views as unknown[]).map(id => (typeof id === 'string' ? ids[id] : undefined)).filter((id): id is string => !!id);\n if (typeof group.activeView === 'string') group.activeView = ids[group.activeView] ?? (group.views as string[])[0];\n });\n pruneViews(layout);\n const paneThemes: Record<string, string> = {};\n for (const [tab, theme] of Object.entries(workspace.paneThemes ?? {})) if (ids[tab]) paneThemes[ids[tab]!] = theme;\n return { layout, paneThemes, ids };\n}\n\n/** Parses what native code returned; anything malformed is dropped, never thrown. */\nexport function normalizeWorkspaces(value: unknown): WorkspaceStore {\n if (!isObject(value) || !Array.isArray(value.items)) return { ...EMPTY_WORKSPACES, items: [] };\n const items: Workspace[] = [];\n for (const item of value.items) {\n if (!isObject(item) || typeof item.id !== 'string' || !WORKSPACE_ID.test(item.id) || items.some(other => other.id === item.id)) continue;\n const name = cleanName(item.name);\n if (!name || !isObject(item.layout) || !isObject(item.layout.grid) || !isObject(item.layout.panels)) continue;\n const paneThemes = isObject(item.paneThemes) ? Object.fromEntries(Object.entries(item.paneThemes).filter(([, theme]) => typeof theme === 'string' && THEME_ID.test(theme))) as Record<string, string> : {};\n items.push({ id: item.id, name, savedAt: typeof item.savedAt === 'string' ? item.savedAt : '', layout: item.layout as SerializedLayout, paneThemes, ...(Array.isArray(item.decks) ? { decks: item.decks } : {}) });\n if (items.length >= WORKSPACE_LIMITS.count) break;\n }\n const fallback = typeof value.default === 'string' && items.some(item => item.id === value.default) ? value.default : null;\n return { version: 1, default: fallback, items };\n}\n\n/** Adds or replaces (same id) a workspace; the list stays bounded. */\nexport function upsertWorkspace(store: WorkspaceStore, workspace: Workspace): WorkspaceStore {\n const exists = store.items.some(item => item.id === workspace.id);\n if (!exists && store.items.length >= WORKSPACE_LIMITS.count) throw new Error(t('wb.ws.err.tooMany', { count: WORKSPACE_LIMITS.count }));\n const items = exists ? store.items.map(item => (item.id === workspace.id ? workspace : item)) : [...store.items, workspace];\n return { ...store, items };\n}\n\nexport function removeWorkspace(store: WorkspaceStore, id: string): WorkspaceStore {\n return { version: 1, default: store.default === id ? null : store.default, items: store.items.filter(item => item.id !== id) };\n}\n\nexport function renameWorkspace(store: WorkspaceStore, id: string, name: string): WorkspaceStore {\n const clean = cleanName(name);\n if (!clean) throw new Error('A workspace needs a name.');\n return { ...store, items: store.items.map(item => (item.id === id ? { ...item, name: clean } : item)) };\n}\n\nexport function setDefaultWorkspace(store: WorkspaceStore, id: string | null): WorkspaceStore {\n return { ...store, default: id && store.items.some(item => item.id === id) ? id : null };\n}\n"]}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* Titlebar extras (TitlebarExtras.tsx): workspace menu and the dev/sync chips. Tokens from shell.css.
|
|
2
|
+
Their measured width is reserved in the tab strip through --bt-tb-extra. */
|
|
3
|
+
|
|
4
|
+
.bt-tb-extras { align-self: center; display: flex; align-items: center; gap: 6px; margin-inline-end: 8px; }
|
|
5
|
+
|
|
6
|
+
.bt-ws-button {
|
|
7
|
+
display: inline-flex; align-items: center; gap: 7px; height: 30px; max-width: 170px; padding-block: 0; padding-inline: 8px 10px; box-sizing: border-box;
|
|
8
|
+
border: 1px solid var(--bt-line-soft); border-radius: calc(15px * var(--bt-rs, 1)); background: rgb(var(--bt-text-rgb) / .035); color: var(--bt-text-2);
|
|
9
|
+
font: 500 12.5px/1 var(--bt-ui); cursor: pointer; transition: background-color var(--bt-t1) var(--bt-ease), border-color var(--bt-t1) var(--bt-ease), color var(--bt-t1) var(--bt-ease);
|
|
10
|
+
}
|
|
11
|
+
.bt-ws-button:not(:has(.bt-ws-button-name)) { width: 30px; padding: 0; justify-content: center; }
|
|
12
|
+
.bt-ws-button:hover { background: var(--bt-hover); border-color: var(--bt-line-strong); color: var(--bt-text); }
|
|
13
|
+
.bt-ws-button[aria-expanded=true] { background: var(--bt-hover); border-color: rgb(var(--bt-accent-rgb) / .5); color: var(--bt-text); }
|
|
14
|
+
.bt-ws-button:focus-visible { outline: 0; box-shadow: var(--bt-ring); }
|
|
15
|
+
.bt-ws-button-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--bt-text); }
|
|
16
|
+
.bt-ws-glyph { width: 15px; height: 15px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.35; stroke-linejoin: round; }
|
|
17
|
+
|
|
18
|
+
.bt-ws-menu { width: 330px; max-height: calc(100vh - 64px); overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--bt-line-strong) transparent; }
|
|
19
|
+
.bt-ws-head { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; }
|
|
20
|
+
.bt-ws-head strong { font: 600 13px/1.2 var(--bt-ui); color: var(--bt-text); }
|
|
21
|
+
.bt-ws-head span { font: 400 12px/1.35 var(--bt-ui); color: var(--bt-muted); }
|
|
22
|
+
.bt-ws-empty { margin: 2px 6px 6px; padding: 10px 12px; border-radius: calc(9px * var(--bt-rs, 1)); background: rgb(var(--bt-text-rgb) / .035); color: var(--bt-muted); font: 400 12.5px/1.45 var(--bt-ui); }
|
|
23
|
+
|
|
24
|
+
.bt-ws-row { position: relative; display: flex; align-items: center; border-radius: calc(10px * var(--bt-rs, 1)); }
|
|
25
|
+
.bt-ws-row.is-active { background: rgb(var(--bt-accent-rgb) / .08); box-shadow: inset 0 0 0 1px rgb(var(--bt-accent-rgb) / .22); }
|
|
26
|
+
.bt-ws-row.is-active::before { content: ''; position: absolute; inset-inline-start: -5px; top: 10px; height: 22px; width: 3px; border-start-start-radius: 0; border-start-end-radius: calc(3px * var(--bt-rs, 1)); border-end-end-radius: calc(3px * var(--bt-rs, 1)); border-end-start-radius: 0; background: var(--bt-accent); }
|
|
27
|
+
.bt-menu .bt-ws-main { flex: 1; min-width: 0; height: auto; flex-direction: column; align-items: flex-start; gap: 4px; padding: 8px 10px; border-radius: calc(10px * var(--bt-rs, 1)); }
|
|
28
|
+
.bt-menu .bt-ws-main:focus-visible, .bt-menu .bt-ws-save:focus-visible { box-shadow: var(--bt-ring); }
|
|
29
|
+
.bt-ws-name { display: flex; align-items: center; gap: 6px; max-width: 100%; font: 600 13.5px/1.15 var(--bt-ui); color: var(--bt-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
30
|
+
.bt-ws-meta { font: 400 12px/1.2 var(--bt-ui); color: var(--bt-muted); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
|
31
|
+
.bt-ws-badge { flex: none; padding: 2px 6px; border-radius: calc(999px * var(--bt-rs, 1)); background: rgb(var(--bt-accent-rgb) / .14); color: var(--bt-accent-ink); font: 500 10.5px/1.2 var(--bt-ui); }
|
|
32
|
+
|
|
33
|
+
.bt-ws-actions { display: flex; gap: 1px; padding-inline-end: 4px; opacity: 0; transition: opacity var(--bt-t1) var(--bt-ease); }
|
|
34
|
+
.bt-ws-row:hover .bt-ws-actions, .bt-ws-row:focus-within .bt-ws-actions, .bt-ws-row.is-active .bt-ws-actions { opacity: 1; }
|
|
35
|
+
.bt-menu .bt-ws-icon { width: 26px; height: 26px; padding: 0; justify-content: center; color: var(--bt-muted); }
|
|
36
|
+
.bt-menu .bt-ws-icon .bt-glyph { width: 14px; height: 14px; }
|
|
37
|
+
.bt-menu .bt-ws-icon[aria-pressed=true] { color: var(--bt-accent-ink); }
|
|
38
|
+
|
|
39
|
+
.bt-menu .bt-ws-save { height: auto; gap: 11px; padding: 8px; border-radius: calc(10px * var(--bt-rs, 1)); }
|
|
40
|
+
.bt-ws-save-icon { display: grid; place-items: center; width: 30px; height: 30px; flex: none; border-radius: calc(9px * var(--bt-rs, 1)); box-shadow: inset 0 0 0 1px var(--bt-line-strong); color: var(--bt-muted); }
|
|
41
|
+
.bt-ws-save-icon .bt-glyph { width: 15px; height: 15px; }
|
|
42
|
+
.bt-menu .bt-ws-save:hover .bt-ws-save-icon { box-shadow: inset 0 0 0 1px rgb(var(--bt-accent-rgb) / .6); color: var(--bt-accent-ink); }
|
|
43
|
+
.bt-ws-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
|
|
44
|
+
.bt-ws-save .bt-ws-name { font-weight: 500; color: var(--bt-text-2); }
|
|
45
|
+
|
|
46
|
+
.bt-ws-form { display: flex; gap: 6px; padding: 6px; }
|
|
47
|
+
.bt-ws-input {
|
|
48
|
+
flex: 1; min-width: 0; height: 30px; padding: 0 10px; box-sizing: border-box; border: 1px solid var(--bt-line-strong); border-radius: calc(8px * var(--bt-rs, 1));
|
|
49
|
+
background: var(--bt-field); color: var(--bt-text); font: 500 13px/1 var(--bt-ui); outline: 0;
|
|
50
|
+
}
|
|
51
|
+
.bt-ws-input:focus { border-color: rgb(var(--bt-accent-rgb) / .6); box-shadow: 0 0 0 3px rgb(var(--bt-accent-rgb) / .14); }
|
|
52
|
+
.bt-menu .bt-ws-primary { height: 30px; padding: 0 12px; background: var(--bt-accent); color: var(--bt-on-accent, #fff); font-weight: 600; }
|
|
53
|
+
.bt-menu .bt-ws-primary:hover:not(:disabled) { background: var(--bt-accent); filter: brightness(1.08); }
|
|
54
|
+
.bt-menu .bt-ws-primary:disabled { background: var(--bt-hover); color: var(--bt-faint); }
|
|
55
|
+
|
|
56
|
+
.bt-ws-confirm {
|
|
57
|
+
display: flex; flex-direction: column; gap: 10px; margin: 2px 6px 6px; padding: 10px 12px; border-radius: calc(9px * var(--bt-rs, 1));
|
|
58
|
+
background: rgba(229, 72, 77, .08); box-shadow: inset 0 0 0 1px rgba(229, 72, 77, .25); color: var(--bt-text-2); font: 400 12.5px/1.45 var(--bt-ui);
|
|
59
|
+
}
|
|
60
|
+
.bt-ws-confirm strong { color: var(--bt-text); font-weight: 600; }
|
|
61
|
+
.bt-ws-confirm div { display: flex; gap: 6px; }
|
|
62
|
+
.bt-menu .bt-ws-confirm button { height: 28px; padding: 0 12px; font-weight: 600; font-size: 12.5px; }
|
|
63
|
+
.bt-menu .bt-ws-danger { background: var(--bt-danger); color: #fff; }
|
|
64
|
+
.bt-menu .bt-ws-danger:hover:not(:disabled) { background: #d93d42; }
|
|
65
|
+
.bt-menu .bt-ws-cancel { background: var(--bt-hover); }
|
|
66
|
+
.bt-ws-error { padding: 6px 10px; color: var(--bt-danger-ink); font: 12px/1.4 var(--bt-ui); }
|
|
67
|
+
|
|
68
|
+
/* Quiet chips: only while there is something to say. */
|
|
69
|
+
.bt-tb-chip {
|
|
70
|
+
display: inline-flex; align-items: center; gap: 7px; height: 28px; max-width: 220px; padding-block: 0; padding-inline: 9px 11px; box-sizing: border-box;
|
|
71
|
+
border: 1px solid var(--bt-line-soft); border-radius: calc(14px * var(--bt-rs, 1)); background: rgb(var(--bt-text-rgb) / .035); color: var(--bt-text-2);
|
|
72
|
+
font: 500 12px/1 var(--bt-ui); white-space: nowrap; cursor: pointer; transition: background-color var(--bt-t1) var(--bt-ease), border-color var(--bt-t1) var(--bt-ease);
|
|
73
|
+
}
|
|
74
|
+
.bt-tb-chip:hover { background: var(--bt-hover); border-color: var(--bt-line-strong); color: var(--bt-text); }
|
|
75
|
+
.bt-tb-chip:focus-visible { outline: 0; box-shadow: var(--bt-ring); }
|
|
76
|
+
.bt-tb-chip-text { overflow: hidden; text-overflow: ellipsis; }
|
|
77
|
+
.bt-tb-chip .bt-glyph { width: 14px; height: 14px; color: var(--bt-accent-ink); }
|
|
78
|
+
.bt-tb-chip-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #3dd68c; box-shadow: 0 0 0 3px rgba(61, 214, 140, .14); }
|
|
79
|
+
.bt-dev-chip { font-family: var(--bt-mono); font-size: 11.5px; }
|
|
80
|
+
.bt-dev-chip.is-fresh .bt-tb-chip-dot { animation: bt-dev-pulse 1s var(--bt-ease-out) 2; }
|
|
81
|
+
@keyframes bt-dev-pulse { 50% { box-shadow: 0 0 0 6px rgba(61, 214, 140, .22); } }
|
|
82
|
+
.bt-dev-chip.is-error { border-color: rgba(229, 72, 77, .38); background: rgba(229, 72, 77, .09); color: #ffb4ab; }
|
|
83
|
+
.bt-dev-chip.is-error .bt-tb-chip-dot { background: var(--bt-danger); box-shadow: 0 0 0 3px rgba(229, 72, 77, .18); }
|
|
84
|
+
.bt-sync-chip { border-color: rgb(var(--bt-accent-rgb) / .35); background: rgb(var(--bt-accent-rgb) / .08); color: var(--bt-text); }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Workspace, type WorkspaceStore } from './workspaceModel.ts';
|
|
2
|
+
export declare const workspaces: {
|
|
3
|
+
store: WorkspaceStore;
|
|
4
|
+
/** The workspace restored or saved last (the titlebar shows its name). */
|
|
5
|
+
active: string | null;
|
|
6
|
+
loaded: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function loadWorkspaces(): Promise<WorkspaceStore>;
|
|
9
|
+
/** Saves the live layout as `name` (a new workspace, or over `replaceId`). */
|
|
10
|
+
export declare function saveCurrentWorkspace(name: string, replaceId?: string): Promise<Workspace>;
|
|
11
|
+
/** Replaces every tab with workspace `id`'s tabs, splits and pane themes. */
|
|
12
|
+
export declare function restoreWorkspace(id: string): Promise<void>;
|
|
13
|
+
export declare const renameSavedWorkspace: (id: string, name: string) => Promise<void>;
|
|
14
|
+
export declare const deleteWorkspace: (id: string) => Promise<void>;
|
|
15
|
+
export declare const setStartWorkspace: (id: string | null) => Promise<void>;
|
|
16
|
+
/** Palette/menu view: names and tab counts. */
|
|
17
|
+
export declare function workspaceList(): {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
tabs: number;
|
|
21
|
+
isDefault: boolean;
|
|
22
|
+
}[];
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Workspaces in the shell: the saved list (native `workspaces_get` / `workspaces_set`), saving the
|
|
2
|
+
// live layout, restoring one with one click (Ctrl+K, the titlebar menu, the default on start).
|
|
3
|
+
// The pure part (capture, restore plan, bounds) is workspaceModel.ts.
|
|
4
|
+
import { invoke } from "./bridge.js";
|
|
5
|
+
import { decks, ensureDecks } from "./deckStore.js";
|
|
6
|
+
import { t } from "./i18n.js";
|
|
7
|
+
import { site } from "./site.js";
|
|
8
|
+
import { newTabId, panelId, patchParams, saveLayoutSoon, scheduleSync, shell } from "./state.js";
|
|
9
|
+
import { EMPTY_WORKSPACES, captureWorkspace, normalizeWorkspaces, planRestore, removeWorkspace, renameWorkspace, setDefaultWorkspace, upsertWorkspace, workspaceId, workspaceTabs, } from "./workspaceModel.js";
|
|
10
|
+
const ACTIVE_KEY = 'bt.shell.workspace.active';
|
|
11
|
+
export const workspaces = {
|
|
12
|
+
store: EMPTY_WORKSPACES,
|
|
13
|
+
/** The workspace restored or saved last (the titlebar shows its name). */
|
|
14
|
+
active: (() => { try {
|
|
15
|
+
return localStorage.getItem(ACTIVE_KEY);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
} })(),
|
|
20
|
+
loaded: false,
|
|
21
|
+
};
|
|
22
|
+
const emit = () => window.dispatchEvent(new CustomEvent('bt-workspaces'));
|
|
23
|
+
const errorText = (error) => (error instanceof Error ? error.message : String(error)).replace(/^VALIDATION: /, '');
|
|
24
|
+
function setActive(id) {
|
|
25
|
+
workspaces.active = id;
|
|
26
|
+
try {
|
|
27
|
+
if (id)
|
|
28
|
+
localStorage.setItem(ACTIVE_KEY, id);
|
|
29
|
+
else
|
|
30
|
+
localStorage.removeItem(ACTIVE_KEY);
|
|
31
|
+
}
|
|
32
|
+
catch { /* best effort */ }
|
|
33
|
+
}
|
|
34
|
+
export async function loadWorkspaces() {
|
|
35
|
+
try {
|
|
36
|
+
workspaces.store = normalizeWorkspaces(await invoke('workspaces_get'));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
workspaces.store = EMPTY_WORKSPACES;
|
|
40
|
+
}
|
|
41
|
+
if (workspaces.active && !workspaces.store.items.some(item => item.id === workspaces.active))
|
|
42
|
+
setActive(null);
|
|
43
|
+
workspaces.loaded = true;
|
|
44
|
+
emit();
|
|
45
|
+
return workspaces.store;
|
|
46
|
+
}
|
|
47
|
+
async function persist(next) {
|
|
48
|
+
try {
|
|
49
|
+
workspaces.store = normalizeWorkspaces(await invoke('workspaces_set', { store: next }));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw new Error(errorText(error));
|
|
53
|
+
}
|
|
54
|
+
if (workspaces.active && !workspaces.store.items.some(item => item.id === workspaces.active))
|
|
55
|
+
setActive(null);
|
|
56
|
+
emit();
|
|
57
|
+
}
|
|
58
|
+
/** Pane themes of the open layout (none without themes). */
|
|
59
|
+
const paneThemes = () => site().themes?.themeState().scopes.panes ?? {};
|
|
60
|
+
/** Saves the live layout as `name` (a new workspace, or over `replaceId`). */
|
|
61
|
+
export async function saveCurrentWorkspace(name, replaceId) {
|
|
62
|
+
const api = shell.api;
|
|
63
|
+
if (!api)
|
|
64
|
+
throw new Error(t('wb.ws.notReady'));
|
|
65
|
+
const id = replaceId ?? workspaceId(name, workspaces.store.items.map(item => item.id));
|
|
66
|
+
const workspace = captureWorkspace({
|
|
67
|
+
id, name, layout: api.toJSON(), paneThemes: paneThemes(),
|
|
68
|
+
decks: decks.list,
|
|
69
|
+
});
|
|
70
|
+
await persist(upsertWorkspace(workspaces.store, workspace));
|
|
71
|
+
setActive(id);
|
|
72
|
+
emit();
|
|
73
|
+
return workspace;
|
|
74
|
+
}
|
|
75
|
+
/** Replaces every tab with workspace `id`'s tabs, splits and pane themes. */
|
|
76
|
+
export async function restoreWorkspace(id) {
|
|
77
|
+
const api = shell.api;
|
|
78
|
+
const workspace = workspaces.store.items.find(item => item.id === id);
|
|
79
|
+
if (!api || !workspace)
|
|
80
|
+
return;
|
|
81
|
+
if (workspace.decks?.length)
|
|
82
|
+
ensureDecks(workspace.decks);
|
|
83
|
+
const known = new Set(shell.profiles.profiles.map(profile => profile.id));
|
|
84
|
+
const plan = planRestore(workspace, newTabId, profile => known.has(profile));
|
|
85
|
+
// The tabs of the layout being left can come back with Ctrl+Shift+T.
|
|
86
|
+
for (const panel of api.panels) {
|
|
87
|
+
const params = panel.params;
|
|
88
|
+
if (params?.kind === 'web')
|
|
89
|
+
shell.closed.push({ url: params.url, profile: params.profile, title: panel.api.title || undefined });
|
|
90
|
+
}
|
|
91
|
+
shell.restoring = true;
|
|
92
|
+
try {
|
|
93
|
+
api.fromJSON(plan.layout);
|
|
94
|
+
api.getPanel(panelId())?.api.setTitle(site().panel.title);
|
|
95
|
+
for (const panel of api.panels)
|
|
96
|
+
if (panel.params.kind === 'web')
|
|
97
|
+
patchParams(panel, { loading: false });
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
shell.restoring = false;
|
|
101
|
+
}
|
|
102
|
+
setActive(id);
|
|
103
|
+
emit();
|
|
104
|
+
scheduleSync();
|
|
105
|
+
saveLayoutSoon();
|
|
106
|
+
// Pane themes follow the new tab ids; panes of the layout left behind are dropped.
|
|
107
|
+
const themes = site().themes;
|
|
108
|
+
if (!themes)
|
|
109
|
+
return;
|
|
110
|
+
const open = new Set(api.panels.map(panel => panel.id));
|
|
111
|
+
const kept = Object.fromEntries(Object.entries(themes.themeState().scopes.panes).filter(([tab]) => open.has(tab)));
|
|
112
|
+
const panes = { ...kept, ...plan.paneThemes };
|
|
113
|
+
try {
|
|
114
|
+
await themes.setScopes({ ...themes.themeState().scopes, panes });
|
|
115
|
+
}
|
|
116
|
+
catch { /* the layout is restored either way */ }
|
|
117
|
+
}
|
|
118
|
+
export const renameSavedWorkspace = (id, name) => persist(renameWorkspace(workspaces.store, id, name));
|
|
119
|
+
export const deleteWorkspace = (id) => persist(removeWorkspace(workspaces.store, id));
|
|
120
|
+
export const setStartWorkspace = (id) => persist(setDefaultWorkspace(workspaces.store, id));
|
|
121
|
+
/** Palette/menu view: names and tab counts. */
|
|
122
|
+
export function workspaceList() {
|
|
123
|
+
return workspaces.store.items.map(item => ({ id: item.id, name: item.name, tabs: workspaceTabs(item).length, isDefault: workspaces.store.default === item.id }));
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=workspaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../../../../shell/src/frame/workspaces.ts"],"names":[],"mappings":"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","sourcesContent":["// Workspaces in the shell: the saved list (native `workspaces_get` / `workspaces_set`), saving the\n// live layout, restoring one with one click (Ctrl+K, the titlebar menu, the default on start).\n// The pure part (capture, restore plan, bounds) is workspaceModel.ts.\n\nimport type { SerializedDockview } from 'dockview';\nimport { invoke } from './bridge.ts';\nimport { decks, ensureDecks } from './deckStore.ts';\nimport { t } from './i18n.ts';\nimport { site } from './site.ts';\nimport { newTabId, panelId, patchParams, saveLayoutSoon, scheduleSync, shell, type TabParams } from './state.ts';\nimport {\n EMPTY_WORKSPACES, captureWorkspace, normalizeWorkspaces, planRestore, removeWorkspace, renameWorkspace, setDefaultWorkspace,\n upsertWorkspace, workspaceId, workspaceTabs, type Workspace, type WorkspaceStore,\n} from './workspaceModel.ts';\n\nconst ACTIVE_KEY = 'bt.shell.workspace.active';\n\nexport const workspaces = {\n store: EMPTY_WORKSPACES as WorkspaceStore,\n /** The workspace restored or saved last (the titlebar shows its name). */\n active: (() => { try { return localStorage.getItem(ACTIVE_KEY); } catch { return null; } })() as string | null,\n loaded: false,\n};\n\nconst emit = () => window.dispatchEvent(new CustomEvent('bt-workspaces'));\nconst errorText = (error: unknown) => (error instanceof Error ? error.message : String(error)).replace(/^VALIDATION: /, '');\n\nfunction setActive(id: string | null) {\n workspaces.active = id;\n try { if (id) localStorage.setItem(ACTIVE_KEY, id); else localStorage.removeItem(ACTIVE_KEY); } catch { /* best effort */ }\n}\n\nexport async function loadWorkspaces(): Promise<WorkspaceStore> {\n try { workspaces.store = normalizeWorkspaces(await invoke('workspaces_get')); } catch { workspaces.store = EMPTY_WORKSPACES; }\n if (workspaces.active && !workspaces.store.items.some(item => item.id === workspaces.active)) setActive(null);\n workspaces.loaded = true;\n emit();\n return workspaces.store;\n}\n\nasync function persist(next: WorkspaceStore) {\n try { workspaces.store = normalizeWorkspaces(await invoke('workspaces_set', { store: next })); }\n catch (error) { throw new Error(errorText(error)); }\n if (workspaces.active && !workspaces.store.items.some(item => item.id === workspaces.active)) setActive(null);\n emit();\n}\n\n/** Pane themes of the open layout (none without themes). */\nconst paneThemes = () => site().themes?.themeState().scopes.panes ?? {};\n\n/** Saves the live layout as `name` (a new workspace, or over `replaceId`). */\nexport async function saveCurrentWorkspace(name: string, replaceId?: string): Promise<Workspace> {\n const api = shell.api;\n if (!api) throw new Error(t('wb.ws.notReady'));\n const id = replaceId ?? workspaceId(name, workspaces.store.items.map(item => item.id));\n const workspace = captureWorkspace({\n id, name, layout: api.toJSON(), paneThemes: paneThemes(),\n decks: decks.list,\n });\n await persist(upsertWorkspace(workspaces.store, workspace));\n setActive(id);\n emit();\n return workspace;\n}\n\n/** Replaces every tab with workspace `id`'s tabs, splits and pane themes. */\nexport async function restoreWorkspace(id: string): Promise<void> {\n const api = shell.api;\n const workspace = workspaces.store.items.find(item => item.id === id);\n if (!api || !workspace) return;\n if (workspace.decks?.length) ensureDecks(workspace.decks);\n const known = new Set(shell.profiles.profiles.map(profile => profile.id));\n const plan = planRestore(workspace, newTabId, profile => known.has(profile));\n // The tabs of the layout being left can come back with Ctrl+Shift+T.\n for (const panel of api.panels) {\n const params = panel.params as TabParams | undefined;\n if (params?.kind === 'web') shell.closed.push({ url: params.url, profile: params.profile, title: panel.api.title || undefined });\n }\n shell.restoring = true;\n try {\n api.fromJSON(plan.layout as unknown as SerializedDockview);\n api.getPanel(panelId())?.api.setTitle(site().panel.title);\n for (const panel of api.panels) if ((panel.params as TabParams).kind === 'web') patchParams(panel, { loading: false });\n } finally {\n shell.restoring = false;\n }\n setActive(id);\n emit();\n scheduleSync();\n saveLayoutSoon();\n // Pane themes follow the new tab ids; panes of the layout left behind are dropped.\n const themes = site().themes;\n if (!themes) return;\n const open = new Set(api.panels.map(panel => panel.id));\n const kept = Object.fromEntries(Object.entries(themes.themeState().scopes.panes).filter(([tab]) => open.has(tab)));\n const panes = { ...kept, ...plan.paneThemes };\n try { await themes.setScopes({ ...themes.themeState().scopes, panes }); } catch { /* the layout is restored either way */ }\n}\n\nexport const renameSavedWorkspace = (id: string, name: string) => persist(renameWorkspace(workspaces.store, id, name));\nexport const deleteWorkspace = (id: string) => persist(removeWorkspace(workspaces.store, id));\nexport const setStartWorkspace = (id: string | null) => persist(setDefaultWorkspace(workspaces.store, id));\n\n/** Palette/menu view: names and tab counts. */\nexport function workspaceList() {\n return workspaces.store.items.map(item => ({ id: item.id, name: item.name, tabs: workspaceTabs(item).length, isDefault: workspaces.store.default === item.id }));\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The factor tab `id` shows, as last answered by native code (100% until asked). */
|
|
2
|
+
export declare const zoomOf: (id: string | null) => number;
|
|
3
|
+
/** The active pane's page: the tab in front, or the focused column of the deck in front. None for the app's own tabs. */
|
|
4
|
+
export declare function zoomTarget(): string | null;
|
|
5
|
+
/** Reads a tab's remembered zoom (a restored tab may come back zoomed). */
|
|
6
|
+
export declare function readZoom(id: string | null): Promise<void>;
|
|
7
|
+
/** Sets a tab's zoom; native code answers the factor in effect. */
|
|
8
|
+
export declare function setZoom(id: string, factor: number): Promise<number>;
|
|
9
|
+
/** One step in (`1`), out (`-1`) or back to 100% (`0`) for tab `id` (default: the active pane's page). */
|
|
10
|
+
export declare function stepZoom(direction: 1 | -1 | 0, id?: string | null): Promise<void>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Page zoom of the tab webviews, as the shell knows it. Native code owns it (`tab_zoom`, shell-only:
|
|
2
|
+
// the engine's own page zoom, clamped and remembered per tab; waybetter-host `zoom.rs`); the shell
|
|
3
|
+
// asks for a factor and keeps what native code answered, for the app menu's zoom row and the
|
|
4
|
+
// titlebar chip. Nothing here touches a page's CSS.
|
|
5
|
+
import { invoke } from "./bridge.js";
|
|
6
|
+
import { deckById, decks } from "./deckStore.js";
|
|
7
|
+
import { zoomStep } from "./menuModel.js";
|
|
8
|
+
import { hasFeature } from "./site.js";
|
|
9
|
+
import { isWeb, shell } from "./state.js";
|
|
10
|
+
const factors = new Map();
|
|
11
|
+
/** The factor tab `id` shows, as last answered by native code (100% until asked). */
|
|
12
|
+
export const zoomOf = (id) => (id ? factors.get(id) ?? 1 : 1);
|
|
13
|
+
function note(id, factor) {
|
|
14
|
+
if (!Number.isFinite(factor) || zoomOf(id) === factor)
|
|
15
|
+
return;
|
|
16
|
+
if (factor === 1)
|
|
17
|
+
factors.delete(id);
|
|
18
|
+
else
|
|
19
|
+
factors.set(id, factor);
|
|
20
|
+
window.dispatchEvent(new CustomEvent('bt-zoom', { detail: id }));
|
|
21
|
+
}
|
|
22
|
+
/** The active pane's page: the tab in front, or the focused column of the deck in front. None for the app's own tabs. */
|
|
23
|
+
export function zoomTarget() {
|
|
24
|
+
const panel = shell.api?.activePanel;
|
|
25
|
+
if (!panel)
|
|
26
|
+
return null;
|
|
27
|
+
if (isWeb(panel))
|
|
28
|
+
return panel.id;
|
|
29
|
+
const params = panel.params;
|
|
30
|
+
if (params?.kind !== 'deck')
|
|
31
|
+
return null;
|
|
32
|
+
const deck = deckById(params.deckId);
|
|
33
|
+
const focused = decks.focus.get(params.deckId);
|
|
34
|
+
return deck?.columns.find(column => column.id === focused)?.tabId ?? null;
|
|
35
|
+
}
|
|
36
|
+
/** Reads a tab's remembered zoom (a restored tab may come back zoomed). */
|
|
37
|
+
export async function readZoom(id) {
|
|
38
|
+
if (!id || !hasFeature('zoom'))
|
|
39
|
+
return;
|
|
40
|
+
try {
|
|
41
|
+
note(id, await invoke('tab_zoom', { id }));
|
|
42
|
+
}
|
|
43
|
+
catch { /* No such webview yet: 100%. */ }
|
|
44
|
+
}
|
|
45
|
+
/** Sets a tab's zoom; native code answers the factor in effect. */
|
|
46
|
+
export async function setZoom(id, factor) {
|
|
47
|
+
const applied = await invoke('tab_zoom', { id, factor });
|
|
48
|
+
note(id, applied);
|
|
49
|
+
return applied;
|
|
50
|
+
}
|
|
51
|
+
/** One step in (`1`), out (`-1`) or back to 100% (`0`) for tab `id` (default: the active pane's page). */
|
|
52
|
+
export async function stepZoom(direction, id = zoomTarget()) {
|
|
53
|
+
if (!id || !hasFeature('zoom'))
|
|
54
|
+
return;
|
|
55
|
+
const next = zoomStep(zoomOf(id), direction);
|
|
56
|
+
if (next === zoomOf(id))
|
|
57
|
+
return;
|
|
58
|
+
try {
|
|
59
|
+
await setZoom(id, next);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('[shell] tab_zoom', error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=zoomStore.js.map
|