3e676e3bb2
* feat(web): double-click tab to toggle maximize Adds a dblclick gesture on dockview tabs that toggles maximize/restore on the containing group, mirroring the existing maximize button. Preview tabs keep their promote-to-pin shortcut: first dblclick pins, then dblclick maximizes. Sidebar group is excluded. * fix(web): add cursor-pointer to tab wrappers for consistency * fix(web): add cursor-pointer to plan-tab wrapper * fix(web): make tab dblclick maximize resilient to layout rebuild Refactor the dblclick-to-maximize handler so it reads the panel's group ID and store state at call time instead of from a render-time closure, and guards against firing while the dockview store is mid-layout-restore. This addresses oscillation when double-clicking a session tab on a task that has multiple sessions: the maximize call now stops event propagation, respects isRestoringLayout, and always uses the live api.group.id, so it remains correct even after dockview rebuilds groups via fromJSON.
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback } from "react";
|
|
import { DockviewDefaultTab, type IDockviewPanelHeaderProps } from "dockview-react";
|
|
import {
|
|
ContextMenu,
|
|
ContextMenuContent,
|
|
ContextMenuItem,
|
|
ContextMenuTrigger,
|
|
} from "@kandev/ui/context-menu";
|
|
import { useTabMaximizeOnDoubleClick } from "./use-tab-maximize";
|
|
|
|
/** An item in the tab right-click context menu.
|
|
* Items are ephemeral — not serialized to the saved layout. */
|
|
export type TabContextMenuItem = {
|
|
label: string;
|
|
onSelect: () => void;
|
|
disabled?: boolean;
|
|
};
|
|
|
|
/** Params shape that panels can use to inject context menu items. */
|
|
export type TabContextMenuParams = {
|
|
contextMenuItems?: TabContextMenuItem[];
|
|
};
|
|
|
|
/** Default tab component — wraps DockviewDefaultTab with a right-click menu.
|
|
* Always provides "Close Others". Panels may inject additional items via
|
|
* props.params.contextMenuItems. */
|
|
export function ContextMenuTab(props: IDockviewPanelHeaderProps) {
|
|
const { api, containerApi } = props;
|
|
const onDoubleClick = useTabMaximizeOnDoubleClick(api);
|
|
|
|
const handleCloseOthers = useCallback(() => {
|
|
const toClose = api.group.panels.filter(
|
|
(p) => p.id !== api.id && p.id !== "chat" && !p.id.startsWith("session:"),
|
|
);
|
|
for (const panel of toClose) containerApi.removePanel(panel);
|
|
}, [api, containerApi]);
|
|
|
|
const extraItems: TabContextMenuItem[] =
|
|
(props.params as TabContextMenuParams | undefined)?.contextMenuItems ?? [];
|
|
|
|
return (
|
|
<ContextMenu>
|
|
<ContextMenuTrigger
|
|
className="flex h-full items-center cursor-pointer select-none"
|
|
onDoubleClick={onDoubleClick}
|
|
>
|
|
<DockviewDefaultTab {...props} />
|
|
</ContextMenuTrigger>
|
|
<ContextMenuContent>
|
|
{extraItems.map((item) => (
|
|
<ContextMenuItem key={item.label} onSelect={item.onSelect} disabled={item.disabled}>
|
|
{item.label}
|
|
</ContextMenuItem>
|
|
))}
|
|
<ContextMenuItem onSelect={handleCloseOthers}>Close Others</ContextMenuItem>
|
|
</ContextMenuContent>
|
|
</ContextMenu>
|
|
);
|
|
}
|