feat: introduce subtasks, allow sessions to task reuse executor (#419)
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import type { DockviewReadyEvent } from "dockview-react";
|
||||
import { useDockviewStore } from "@/lib/state/dockview-store";
|
||||
import { getRootSplitview } from "@/lib/state/dockview-layout-builders";
|
||||
import { setSessionLayout } from "@/lib/local-storage";
|
||||
import { panelPortalManager } from "@/lib/layout/panel-portal-manager";
|
||||
import { stopVscode } from "@/lib/api/domains/vscode-api";
|
||||
import { stopUserShell } from "@/lib/api/domains/user-shell-api";
|
||||
|
||||
const LAYOUT_STORAGE_KEY = "dockview-layout-v1";
|
||||
|
||||
function trackPinnedWidths(api: DockviewReadyEvent["api"]): void {
|
||||
if (useDockviewStore.getState().isRestoringLayout) return;
|
||||
if (api.hasMaximizedGroup() || useDockviewStore.getState().preMaximizeLayout !== null) return;
|
||||
const sv = getRootSplitview(api);
|
||||
if (!sv || sv.length < 2) return;
|
||||
try {
|
||||
const sidebarW = sv.getViewSize(0);
|
||||
if (sidebarW > 50) {
|
||||
const current = useDockviewStore.getState().pinnedWidths.get("sidebar");
|
||||
if (current !== sidebarW) {
|
||||
useDockviewStore.getState().setPinnedWidth("sidebar", sidebarW);
|
||||
}
|
||||
}
|
||||
if (sv.length >= 3) {
|
||||
const rightIdx = sv.length - 1;
|
||||
const rightW = sv.getViewSize(rightIdx);
|
||||
if (rightW > 50) {
|
||||
const current = useDockviewStore.getState().pinnedWidths.get("right");
|
||||
if (current !== rightW) {
|
||||
useDockviewStore.getState().setPinnedWidth("right", rightW);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* noop */
|
||||
}
|
||||
}
|
||||
|
||||
export function setupGroupTracking(api: DockviewReadyEvent["api"]): () => void {
|
||||
const d1 = api.onDidActiveGroupChange((group) => {
|
||||
useDockviewStore.setState({ activeGroupId: group?.id ?? null });
|
||||
});
|
||||
useDockviewStore.setState({ activeGroupId: api.activeGroup?.id ?? null });
|
||||
const d2 = api.onDidLayoutChange(() => trackPinnedWidths(api));
|
||||
trackPinnedWidths(api);
|
||||
return () => {
|
||||
d1.dispose();
|
||||
d2.dispose();
|
||||
};
|
||||
}
|
||||
|
||||
export function setupLayoutPersistence(
|
||||
api: DockviewReadyEvent["api"],
|
||||
saveTimerRef: React.MutableRefObject<ReturnType<typeof setTimeout> | null>,
|
||||
sessionIdRef: React.MutableRefObject<string | null>,
|
||||
): void {
|
||||
api.onDidLayoutChange(() => {
|
||||
if (useDockviewStore.getState().isRestoringLayout) return;
|
||||
|
||||
if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
|
||||
saveTimerRef.current = setTimeout(() => {
|
||||
try {
|
||||
const json = api.toJSON();
|
||||
localStorage.setItem(LAYOUT_STORAGE_KEY, JSON.stringify(json));
|
||||
const sid = sessionIdRef.current;
|
||||
if (sid) {
|
||||
setSessionLayout(sid, json);
|
||||
}
|
||||
} catch {
|
||||
// Ignore serialization errors
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
|
||||
export function setupPortalCleanup(api: DockviewReadyEvent["api"]): void {
|
||||
api.onDidRemovePanel((panel) => {
|
||||
if (useDockviewStore.getState().isRestoringLayout) return;
|
||||
const isMax = useDockviewStore.getState().preMaximizeLayout !== null;
|
||||
const remaining = api.panels.filter((p) => p.id !== panel.id);
|
||||
const nonSidebar = remaining.filter((p) => p.api.component !== "sidebar");
|
||||
// If we're in maximize mode and the last non-sidebar panel was just closed,
|
||||
// exit maximize to restore the pre-maximize layout (avoids empty view).
|
||||
// Then remove the closed panel from the restored layout so it doesn't reappear.
|
||||
if (isMax && nonSidebar.length === 0) {
|
||||
const removedId = panel.id;
|
||||
requestAnimationFrame(() => {
|
||||
useDockviewStore.getState().exitMaximizedLayout();
|
||||
// exitMaximizedLayout schedules a rAF to finalize — wait for that, then
|
||||
// remove the panel that was closed (it was re-created from preMaximizeLayout).
|
||||
requestAnimationFrame(() => {
|
||||
const restoredPanel = api.getPanel(removedId);
|
||||
if (restoredPanel) {
|
||||
restoredPanel.api.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
const entry = panelPortalManager.get(panel.id);
|
||||
if (entry?.component === "vscode" && entry.sessionId) stopVscode(entry.sessionId);
|
||||
if (entry?.component === "terminal" && entry.sessionId) {
|
||||
const terminalId = entry.params.terminalId as string | undefined;
|
||||
if (terminalId) stopUserShell(entry.sessionId, terminalId);
|
||||
}
|
||||
panelPortalManager.release(panel.id);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user