From dc22e8c974e6d636dfd7086323e90af8eb890bc4 Mon Sep 17 00:00:00 2001 From: ezreal <71426102+Ezreal-byte@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:55:23 +0800 Subject: [PATCH] feat(plugins): configurable plugin shortcut dock positions --- apps/desktop/src/App.vue | 3 + .../AppSidebar.connectionMultiSelect.spec.ts | 2 + .../src/components/layout/AppSidebar.vue | 4 + .../src/components/layout/AppToolbar.vue | 50 +--- .../plugins/PluginBottomDock.spec.ts | 18 +- .../components/plugins/PluginBottomDock.vue | 5 + .../plugins/PluginContributionsPanel.vue | 2 + .../plugins/PluginShortcutBar.spec.ts | 137 +++++++++++ .../components/plugins/PluginShortcutBar.vue | 219 +++++++++++++++++ .../plugins/PluginShortcutSettings.spec.ts | 74 ++++++ .../plugins/PluginShortcutSettings.vue | 90 +++++++ .../plugins/PluginShortcutToolbar.spec.ts | 160 ++++++++++++ .../plugins/PluginShortcutToolbar.vue | 230 ++++++++++++++++++ .../PluginContributionsPanelBatch.spec.ts | 3 + .../__tests__/usePluginShortcutHeight.spec.ts | 136 +++++++++++ .../usePluginShortcutPreferences.spec.ts | 41 ++++ .../__tests__/usePluginShortcuts.spec.ts | 187 ++++++++++++++ .../composables/usePluginShortcutHeight.ts | 139 +++++++++++ .../usePluginShortcutPreferences.ts | 30 +++ .../src/composables/usePluginShortcutSort.ts | 135 ++++++++++ .../src/composables/usePluginShortcuts.ts | 74 ++++++ apps/desktop/src/i18n/locales/az.ts | 13 + apps/desktop/src/i18n/locales/en.ts | 18 ++ apps/desktop/src/i18n/locales/es.ts | 18 ++ apps/desktop/src/i18n/locales/it.ts | 18 ++ apps/desktop/src/i18n/locales/ja.ts | 18 ++ apps/desktop/src/i18n/locales/ko.ts | 18 ++ apps/desktop/src/i18n/locales/pt-BR.ts | 18 ++ apps/desktop/src/i18n/locales/ru.ts | 18 ++ apps/desktop/src/i18n/locales/tr.ts | 13 + apps/desktop/src/i18n/locales/zh-CN.ts | 18 ++ apps/desktop/src/i18n/locales/zh-TW.ts | 18 ++ .../src/lib/plugins/pluginCommandRegistry.ts | 1 + .../src/lib/plugins/pluginShortcuts.spec.ts | 87 +++++++ .../src/lib/plugins/pluginShortcuts.ts | 124 ++++++++++ .../settingsStore.pluginShortcuts.spec.ts | 31 +++ apps/desktop/src/stores/queryStore.ts | 8 +- apps/desktop/src/stores/settingsStore.ts | 5 + apps/desktop/src/types/database.ts | 2 + docs/content/docs/plugin-development.cn.mdx | 14 ++ docs/content/docs/plugin-development.mdx | 14 ++ 41 files changed, 2172 insertions(+), 41 deletions(-) create mode 100644 apps/desktop/src/components/plugins/PluginShortcutBar.spec.ts create mode 100644 apps/desktop/src/components/plugins/PluginShortcutBar.vue create mode 100644 apps/desktop/src/components/plugins/PluginShortcutSettings.spec.ts create mode 100644 apps/desktop/src/components/plugins/PluginShortcutSettings.vue create mode 100644 apps/desktop/src/components/plugins/PluginShortcutToolbar.spec.ts create mode 100644 apps/desktop/src/components/plugins/PluginShortcutToolbar.vue create mode 100644 apps/desktop/src/composables/__tests__/usePluginShortcutHeight.spec.ts create mode 100644 apps/desktop/src/composables/__tests__/usePluginShortcutPreferences.spec.ts create mode 100644 apps/desktop/src/composables/__tests__/usePluginShortcuts.spec.ts create mode 100644 apps/desktop/src/composables/usePluginShortcutHeight.ts create mode 100644 apps/desktop/src/composables/usePluginShortcutPreferences.ts create mode 100644 apps/desktop/src/composables/usePluginShortcutSort.ts create mode 100644 apps/desktop/src/composables/usePluginShortcuts.ts create mode 100644 apps/desktop/src/lib/plugins/pluginShortcuts.spec.ts create mode 100644 apps/desktop/src/lib/plugins/pluginShortcuts.ts create mode 100644 apps/desktop/src/stores/__tests__/settingsStore.pluginShortcuts.spec.ts diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 64afd838d2..2f05b1d8aa 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -8,6 +8,7 @@ import { TooltipProvider } from "@/components/ui/tooltip"; import AppToolbar from "@/components/layout/AppToolbar.vue"; import AppTabBar from "@/components/layout/AppTabBar.vue"; import { createGroupTabBarPortal, GROUP_TAB_BAR_PORTAL } from "@/components/layout/groupTabBarPortal"; +import PluginShortcutBar from "@/components/plugins/PluginShortcutBar.vue"; import AppSidebar from "@/components/layout/AppSidebar.vue"; import SqlEditorWorkspace from "@/components/layout/SqlEditorWorkspace.vue"; import { EDITOR_TOOLBAR_ACTIONS } from "@/components/layout/editorToolbarActions"; @@ -4154,6 +4155,7 @@ onUnmounted(() => { />
+ {
+ ({ store: null as any, @@ -175,6 +176,7 @@ async function mountSidebar() { setup: () => () => h(AppSidebar, { sidebarWidth: 260 }), }), ); + app.use(createPinia()); app.mount(host); mountedApps.push({ unmount: () => app.unmount(), host }); await nextTick(); diff --git a/apps/desktop/src/components/layout/AppSidebar.vue b/apps/desktop/src/components/layout/AppSidebar.vue index b21b4660cb..d5fb359cf1 100644 --- a/apps/desktop/src/components/layout/AppSidebar.vue +++ b/apps/desktop/src/components/layout/AppSidebar.vue @@ -8,6 +8,8 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from " import { Input } from "@/components/ui/input"; import LightDropdown from "@/components/ui/LightDropdown.vue"; import LightTooltip from "@/components/ui/LightTooltip.vue"; +import PluginShortcutBar from "@/components/plugins/PluginShortcutBar.vue"; +import { useSettingsStore } from "@/stores/settingsStore"; import ConnectionTree from "@/components/sidebar/ConnectionTree.vue"; import { applyConnectionMultiSelection, emptyConnectionMultiSelection, isExitConnectionMultiSelectionShortcut } from "@/lib/sidebar/sidebarConnectionMultiSelect"; import { connectionGroupDestinationRows } from "@/lib/sidebar/sidebarLayout"; @@ -33,6 +35,7 @@ type ImportSource = "dbx" | "navicat" | "dbeaver" | "datagrip"; const { t } = useI18n(); const connectionStore = useConnectionStore(); +const settingsStore = useSettingsStore(); const { toast } = useToast(); const connectionTreeRef = ref>(); const showDeleteSelectedConfirm = ref(false); @@ -296,6 +299,7 @@ defineExpose({ focusSearch, locateTabInSidebar });
+
diff --git a/apps/desktop/src/components/layout/AppToolbar.vue b/apps/desktop/src/components/layout/AppToolbar.vue index 3d64586437..4ae0833e4c 100644 --- a/apps/desktop/src/components/layout/AppToolbar.vue +++ b/apps/desktop/src/components/layout/AppToolbar.vue @@ -9,11 +9,9 @@ import LightDropdown from "@/components/ui/LightDropdown.vue"; import WindowControls from "@/components/layout/WindowControls.vue"; import ExportProgressPopover from "@/components/export/ExportProgressPopover.vue"; import ToolbarUpdateIcon from "@/components/layout/ToolbarUpdateIcon.vue"; +import PluginShortcutToolbar from "@/components/plugins/PluginShortcutToolbar.vue"; import { MAC_TRAFFIC_LIGHT_X, macTrafficLightInsetPaddingForScale, shouldReserveMacTrafficLightInset, useWindowControls } from "@/composables/useWindowControls"; import { useToast } from "@/composables/useToast"; -import PluginIcon from "@/components/plugins/PluginIcon.vue"; -import { setDockVisible, usePluginBottomDock } from "@/lib/plugins/pluginBottomDock"; -import { usePluginToolbarCommands, type PluginToolbarCommandEntry } from "@/lib/plugins/pluginCommandRegistry"; import { useSettingsStore } from "@/stores/settingsStore"; import { isSystemAppThemeMode, type AppThemeMode } from "@/lib/app/appTheme"; @@ -80,27 +78,6 @@ const emit = defineEmits<{ const { t } = useI18n(); const { toast } = useToast(); -// PR-A4 appToolbar surface (HOST_PLUGIN_UI_SPEC §5.1): plugin commands render as icons -// surface (next to Settings/AI); clicking runs the command (presentation: panel -> the global bottom dock), -// clicking again collapses an open dock command. -const { entries: pluginCommandEntries, open: openPluginCommand } = usePluginToolbarCommands(); -const { entries: pluginDockEntries, visible: dockVisible } = usePluginBottomDock(); -// The toolbar icon toggles panel visibility (panel hide keeps the webviews -// mounted, so sessions and height survive); with a hidden-but-populated dock -// the first click just restores it, and only an empty dock runs the command -// (otherwise "getting the panel back" would keep spawning new terminals). -function togglePluginCommand(entry: PluginToolbarCommandEntry) { - if (dockVisible.value) { - setDockVisible(false); - return; - } - if (pluginDockEntries.value.length) { - setDockVisible(true); - return; - } - const result = openPluginCommand(entry); - if (result.error) toast(result.error, 5000); -} const settingsStore = useSettingsStore(); const toolbarItems = computed(() => settingsStore.editorSettings.toolbarItems); const showToolbarUpdateEntry = computed(() => toolbarItems.value.checkUpdates || props.hasUpdateAvailable); @@ -186,6 +163,8 @@ function onToolbarDblClick(e: MouseEvent) { const toolbarEl = ref(); const newConnectionLabelEl = ref(); const toolbarCollapsed = ref(false); +const pluginCenterGroup = ref(null); +const showPluginCenterShortcuts = computed(() => settingsStore.editorSettings.pluginShortcuts.enabled && settingsStore.editorSettings.pluginShortcuts.position === "plugin-center"); const shouldReserveTrafficLightInset = computed(() => shouldReserveMacTrafficLightInset(isMac, isFullscreen.value, isDesktop)); function checkToolbarWidth() { @@ -611,10 +590,13 @@ const toolbarStyle = computed(() => { - +
+ + +
{