Files
kandev/apps/web/lib/state/dockview-measure.ts
T
2026-07-28 23:29:32 +01:00

85 lines
3.7 KiB
TypeScript

import type { DockviewApi } from "dockview-react";
const dockviewRoots = new WeakMap<DockviewApi, HTMLElement>();
export function registerDockviewRoot(api: DockviewApi, root: HTMLElement): void {
dockviewRoots.set(api, root);
}
export function unregisterDockviewRoot(api: DockviewApi): void {
dockviewRoots.delete(api);
}
export function getDockviewElement(api: DockviewApi): HTMLElement | null {
const root = dockviewRoots.get(api);
if (root) return root.querySelector<HTMLElement>(".dv-dockview");
return typeof document !== "undefined"
? document.querySelector<HTMLElement>(".dv-dockview")
: null;
}
export function measureDockviewGridWidth(api: DockviewApi): number | undefined {
const dockview = getDockviewElement(api);
const domWidth = dockview?.clientWidth || dockview?.parentElement?.clientWidth;
if (domWidth && domWidth > 0) return domWidth;
if (api.width > 0) return api.width;
return undefined;
}
/**
* Measure the live size of the dockview container element.
*
* `api.width` / `api.height` reflect dockview's *internal* grid dimensions,
* which can drift out of sync with the actual DOM container — e.g. after a
* `fromJSON` whose recorded `grid.width` differs from the live container, or
* after a window/devtools resize that the library's ResizeObserver missed.
* Reading `clientWidth/Height` of `.dv-dockview`'s parent element is the
* source of truth and lets us recover from a drifted internal width.
*/
export function measureDockviewContainer(api: DockviewApi): { width: number; height: number } {
const live = liveContainerSize(api);
if (live) return live;
// No laid-out container (e.g. a fresh client-side navigation mounts dockview
// before the grid has painted). `api.width/height` are 0 at that point, and
// building the default layout at width 0 makes dockview collapse the
// horizontal columns into a vertical stack (chat / files+changes / terminal).
// Fall back to the viewport so the default builds horizontally; the resize
// observer then snaps the grid to the exact container size.
return {
width: api.width > 0 ? api.width : viewportWidth(),
height: api.height > 0 ? api.height : viewportHeight(),
};
}
// The dockview content area sits next to the unified AppSidebar (#1165). During
// initial root layout, `onReady` can read the parent at a tiny positive width —
// building the default layout at that width collapses the horizontal columns
// into a vertical stack. The sidebar maxes at 30vw (desktop-only surface), so a
// healthy content width is always >= ~70vw; anything below half the viewport is
// a transient and must be treated like a not-yet-laid-out (0-width) container.
const MIN_PLAUSIBLE_WIDTH_RATIO = 0.5;
function liveContainerSize(api: DockviewApi): { width: number; height: number } | null {
const dv = getDockviewElement(api);
const parent = dv?.parentElement;
if (!parent || parent.clientWidth <= 0 || parent.clientHeight <= 0) return null;
// Root flex layout changes are horizontal, so only the width can be a
// transient — the live height is reliable. Fall back just the width to the
// viewport so the build stays horizontal; keep the real height to avoid a
// needless vertical transient. The resize observer then snaps the width to
// the exact size.
const plausibleWidth = parent.clientWidth >= viewportWidth() * MIN_PLAUSIBLE_WIDTH_RATIO;
return {
width: plausibleWidth ? parent.clientWidth : viewportWidth(),
height: parent.clientHeight,
};
}
function viewportWidth(): number {
return typeof window !== "undefined" && window.innerWidth > 0 ? window.innerWidth : 1280;
}
function viewportHeight(): number {
return typeof window !== "undefined" && window.innerHeight > 0 ? window.innerHeight : 800;
}