From 43cfb85e528d9775b2e7f227b6cdd0523309d409 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Sun, 1 Nov 2026 12:55:34 +0000 Subject: [PATCH] fix(hub-ui): hide the panel while a toggle action is on The floating dock panel showed whenever a dock was selected, so a toggle action set `session.open` to false but the panel stayed on the page. The panel now shows only while `session.open` is true, like edge mode. The selection stays, so the same view and route come back when the tool turns off. While the panel is hidden, the dock bar no longer marks the selected view as active. A click on that view now opens it again, instead of clearing the selection. Refs #469 Co-Authored-By: Claude Opus 5.5 --- .../1.guide/22.build-your-own-hub-ui.md | 3 ++- .../src/client/components/dock/Dock.vue | 12 +++++++--- .../src/client/components/dock/DockEdge.vue | 18 +++++++-------- .../src/client/components/dock/DockPanel.vue | 4 ++-- .../state/client-script.integration.test.ts | 22 +++++++++++++++++++ .../hub-ui/src/client/state/dock-settings.ts | 13 +++++++++++ 6 files changed, 56 insertions(+), 16 deletions(-) diff --git a/docs/content/1.guide/22.build-your-own-hub-ui.md b/docs/content/1.guide/22.build-your-own-hub-ui.md index 6d06716bc..dad1c6ea1 100644 --- a/docs/content/1.guide/22.build-your-own-hub-ui.md +++ b/docs/content/1.guide/22.build-your-own-hub-ui.md @@ -40,7 +40,8 @@ Render the built-in variants of the open dock union (`DevframeDockEntryRegistry`, `@devframes/hub/types`): `iframe` (the entry's `url` in a kept-alive iframe, honoring `subTabs` soft nav), `action` (a dock-rail button running its client script, shown as active while a -`toggle: true` action is on), `custom-render` (a container its +`toggle: true` action is on, which also closes `panel.session.open` without +clearing the selection, so show the panel only while it is `true`), `custom-render` (a container its client script mounts into), `launcher` (a launch call-to-action reflecting `launcher.status`), `group` (one dock-rail button collapsing its members), and `~builtin` (your native views for reserved ids). What to render per type is in diff --git a/packages/hub-ui/src/client/components/dock/Dock.vue b/packages/hub-ui/src/client/components/dock/Dock.vue index 7ac06d31a..9a5c575c2 100644 --- a/packages/hub-ui/src/client/components/dock/Dock.vue +++ b/packages/hub-ui/src/client/components/dock/Dock.vue @@ -123,6 +123,12 @@ const selectedEntry = computed(() => { return context.docks.selected }) +// The bar marks the selection only while its panel shows: a toggle action +// hides the panel, and a click on the hidden view then reopens it. +const barSelectedEntry = computed(() => { + return context.panel.session.open ? selectedEntry.value : null +}) + // Remember a recent dock whenever the selection lands on an entry that has // no slot on the bar as currently rendered (grouped members always; top-level // entries when picked from the overflow popover). Observing the selection @@ -350,7 +356,7 @@ onMounted(() => { :context="context" :groups="splitEntries.visible" :is-vertical="context.panel.isVertical" - :selected="selectedEntry" + :selected="barSelectedEntry" @select="(e) => context.docks.switchEntry(e?.id)" /> @@ -360,7 +366,7 @@ onMounted(() => { :context="context" :entries="splitEntries.recent" :is-vertical="context.panel.isVertical" - :selected="selectedEntry" + :selected="barSelectedEntry" @select="(e) => context.docks.switchEntry(e?.id)" /> @@ -370,7 +376,7 @@ onMounted(() => { :context="context" :is-vertical="context.panel.isVertical" :groups="splitEntries.overflow" - :selected="selectedEntry" + :selected="barSelectedEntry" @select="(e) => context.docks.switchEntry(e?.id)" @activity="bringUp" /> diff --git a/packages/hub-ui/src/client/components/dock/DockEdge.vue b/packages/hub-ui/src/client/components/dock/DockEdge.vue index 9c0937df3..285556984 100644 --- a/packages/hub-ui/src/client/components/dock/DockEdge.vue +++ b/packages/hub-ui/src/client/components/dock/DockEdge.vue @@ -7,7 +7,7 @@ import { useEventListener } from '@vueuse/core' import { computed, h, onMounted, ref, useTemplateRef, watch } from 'vue' import { BUILTIN_ENTRY_SETTINGS } from '../../constants' import { t } from '../../i18n' -import { docksSplitGroupsBySize, getEntryGroup } from '../../state/dock-settings' +import { docksSplitGroupsBySize, getEntryGroup, getPanelViewEntry } from '../../state/dock-settings' import { setEdgePositionDropdown, setFloatingTooltip, useDocksGroupPanel, useDocksOverflowPanel, useEdgePositionDropdown } from '../../state/floating-tooltip' import { useSettings } from '../../state/settings-defaults' import { getEntryPaneKey, useIframePanes } from '../../utils/useIframePanes' @@ -74,13 +74,11 @@ const splitEntries = computed(() => { return docksSplitGroupsBySize(groupedEntries.value, (isVertical.value ? height : width) - settingsSize, item, gap, separatorSize) }) const selectedEntry = computed(() => context.docks.selected) +// The bar marks the selection only while its panel shows: a toggle action +// hides the panel, and a click on the hidden view then reopens it. +const barSelectedEntry = computed(() => context.panel.session.open ? selectedEntry.value : null) const activeGroup = computed(() => getEntryGroup(context.docks.entries, selectedEntry.value)) -const hasPanelContent = computed(() => { - const entry = selectedEntry.value - return context.panel.session.open - && !!entry - && entry.type !== 'action' -}) +const hasPanelContent = computed(() => !!getPanelViewEntry(context)) // Auto-collapse (on by default, opt out via `settings.autoCollapseEdgeToolbar: // false`): idle-timeout + hover tracking, mirroring Dock.vue's own float-bar @@ -480,7 +478,7 @@ const dragPreviewStyle = computed(() => { :groups="splitEntries.visible" :is-vertical="isVertical" :rotate="false" - :selected="selectedEntry" + :selected="barSelectedEntry" @select="(e) => context.docks.switchEntry(e?.id)" /> (() => { :groups="splitEntries.overflow" :is-vertical="false" :placement="positionDropdownPlacement[store.position]" - :selected="selectedEntry" + :selected="barSelectedEntry" @select="(e) => context.docks.switchEntry(e?.id)" @activity="bringUp" /> @@ -503,7 +501,7 @@ const dragPreviewStyle = computed(() => { :context="context" :entries="settingsEntries" :is-vertical="false" - :selected="selectedEntry" + :selected="barSelectedEntry" @select="(e) => context.docks.switchEntry(e?.id)" /> diff --git a/packages/hub-ui/src/client/components/dock/DockPanel.vue b/packages/hub-ui/src/client/components/dock/DockPanel.vue index 878e80f3b..27439da3e 100644 --- a/packages/hub-ui/src/client/components/dock/DockPanel.vue +++ b/packages/hub-ui/src/client/components/dock/DockPanel.vue @@ -5,7 +5,7 @@ import type { CSSProperties } from 'vue' import type { DockLayout } from './dock-layout' import { useElementBounding, useWindowSize } from '@vueuse/core' import { computed, onMounted, reactive, ref, toRefs, useTemplateRef } from 'vue' -import { getEntryGroup } from '../../state/dock-settings' +import { getEntryGroup, getPanelViewEntry } from '../../state/dock-settings' import { getEntryPaneKey, useIframePanes } from '../../utils/useIframePanes' import ViewEntry from '../views/ViewEntry.vue' import { DEFAULT_DOCK_LAYOUT, resolveDockAnchor } from './dock-layout' @@ -173,7 +173,7 @@ onMounted(() => {