Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

185 lines
4.8 KiB
TypeScript

import {
DESKTOP_DOCK_HEIGHT,
DESKTOP_WINDOW_CASCADE_OFFSET,
DESKTOP_WINDOW_EDGE_MARGIN,
DESKTOP_WINDOW_MIN_HEIGHT,
DESKTOP_WINDOW_MIN_WIDTH,
DESKTOP_WINDOW_TOP_MARGIN
} from "./constants";
import type { DesktopSize, WindowRect, WindowState } from "./types";
const DEFAULT_WINDOW_WIDTH = 720;
const DEFAULT_WINDOW_HEIGHT = 460;
export const DESKTOP_WINDOW_MIN_VISIBLE_PX = 40;
export function resolveDesktopBottomInset(desktopSize: DesktopSize): number {
return Math.max(
DESKTOP_DOCK_HEIGHT,
Math.round(desktopSize.bottomInset ?? DESKTOP_DOCK_HEIGHT)
);
}
function availableHeight(desktopSize: DesktopSize): number {
return Math.max(
DESKTOP_WINDOW_MIN_HEIGHT,
desktopSize.height -
resolveDesktopBottomInset(desktopSize) -
DESKTOP_WINDOW_TOP_MARGIN
);
}
export function clampWindowRect(
rect: WindowRect,
desktopSize: DesktopSize
): WindowRect {
const width = Math.max(
DESKTOP_WINDOW_MIN_WIDTH,
Math.min(rect.width, desktopSize.width)
);
const height = Math.max(
DESKTOP_WINDOW_MIN_HEIGHT,
Math.min(rect.height, availableHeight(desktopSize))
);
const maxX = Math.max(
DESKTOP_WINDOW_EDGE_MARGIN,
desktopSize.width - width - DESKTOP_WINDOW_EDGE_MARGIN
);
const maxY = Math.max(
DESKTOP_WINDOW_TOP_MARGIN + DESKTOP_WINDOW_EDGE_MARGIN,
DESKTOP_WINDOW_TOP_MARGIN +
availableHeight(desktopSize) -
height -
DESKTOP_WINDOW_EDGE_MARGIN
);
return {
x: Math.round(Math.min(Math.max(DESKTOP_WINDOW_EDGE_MARGIN, rect.x), maxX)),
y: Math.round(
Math.min(
Math.max(
DESKTOP_WINDOW_TOP_MARGIN + DESKTOP_WINDOW_EDGE_MARGIN,
rect.y
),
maxY
)
),
width: Math.round(width),
height: Math.round(height)
};
}
export function clampWindowRectToVisibleArea(
rect: WindowRect,
desktopSize: DesktopSize,
minVisiblePx = DESKTOP_WINDOW_MIN_VISIBLE_PX
): WindowRect {
const normalized = clampWindowRect(
{ ...rect, x: 0, y: DESKTOP_WINDOW_TOP_MARGIN },
desktopSize
);
const minVisibleX = Math.min(minVisiblePx, desktopSize.width);
const minVisibleY = Math.min(minVisiblePx, availableHeight(desktopSize));
const minX = minVisibleX - normalized.width;
const maxX = desktopSize.width - minVisibleX;
// Keep the draggable header reachable even when legacy/persisted rects overflow the workspace bounds.
const minY = DESKTOP_WINDOW_TOP_MARGIN;
const maxY =
DESKTOP_WINDOW_TOP_MARGIN + availableHeight(desktopSize) - minVisibleY;
return {
...normalized,
x: Math.round(Math.min(Math.max(minX, rect.x), maxX)),
y: Math.round(Math.min(Math.max(minY, rect.y), maxY))
};
}
export function clampWindowDragRect(
rect: WindowRect,
desktopSize: DesktopSize
): WindowRect {
const visibleRect = clampWindowRectToVisibleArea(rect, desktopSize);
return {
...visibleRect,
y: Math.max(DESKTOP_WINDOW_TOP_MARGIN, visibleRect.y)
};
}
export function centerWindowRect(
desktopSize: DesktopSize,
preferred?: Partial<Pick<WindowRect, "width" | "height">>
): WindowRect {
const width =
preferred?.width ??
Math.min(
DEFAULT_WINDOW_WIDTH,
desktopSize.width - DESKTOP_WINDOW_EDGE_MARGIN * 2
);
const height =
preferred?.height ??
Math.min(
DEFAULT_WINDOW_HEIGHT,
availableHeight(desktopSize) - DESKTOP_WINDOW_EDGE_MARGIN * 2
);
return clampWindowRect(
{
x: (desktopSize.width - width) / 2,
y:
DESKTOP_WINDOW_TOP_MARGIN + (availableHeight(desktopSize) - height) / 2,
width,
height
},
desktopSize
);
}
export function resolveNextWindowRect({
existingWindows,
desktopSize,
preferredRect
}: {
existingWindows: readonly WindowState[];
desktopSize: DesktopSize;
preferredRect?: Partial<WindowRect>;
}): WindowRect {
const width = preferredRect?.width ?? DEFAULT_WINDOW_WIDTH;
const height = preferredRect?.height ?? DEFAULT_WINDOW_HEIGHT;
if (
typeof preferredRect?.x === "number" &&
typeof preferredRect.y === "number"
) {
return clampWindowRect(
{ x: preferredRect.x, y: preferredRect.y, width, height },
desktopSize
);
}
if (existingWindows.length === 0) {
return centerWindowRect(desktopSize, { width, height });
}
const topWindow = existingWindows.at(-1);
const cascadeBase =
topWindow?.rect ?? centerWindowRect(desktopSize, { width, height });
return clampWindowRect(
{
x: cascadeBase.x + DESKTOP_WINDOW_CASCADE_OFFSET,
y: cascadeBase.y + DESKTOP_WINDOW_CASCADE_OFFSET,
width,
height
},
desktopSize
);
}
export function resolveMaximizedRect(desktopSize: DesktopSize): WindowRect {
return {
x: 0,
y: DESKTOP_WINDOW_TOP_MARGIN,
width: Math.max(DESKTOP_WINDOW_MIN_WIDTH, desktopSize.width),
height: availableHeight(desktopSize)
};
}