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
225 lines
7.0 KiB
TypeScript
225 lines
7.0 KiB
TypeScript
function clamp(value: number, min: number, max: number): number {
|
|
return Math.min(max, Math.max(min, value));
|
|
}
|
|
|
|
// Keep aligned with the issue manager task-list rail dimensions.
|
|
export const AGENT_GUI_CONVERSATION_RAIL_DEFAULT_WIDTH_PX = 280;
|
|
export const AGENT_GUI_CONVERSATION_RAIL_MIN_WIDTH_PX = 248;
|
|
export const AGENT_GUI_CONVERSATION_RAIL_MAX_WIDTH_PX = 520;
|
|
export const AGENT_GUI_DETAIL_MIN_WIDTH_PX = 220;
|
|
export const AGENT_GUI_HOME_COMPOSER_MIN_WIDTH_PX = 320;
|
|
export const AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX = 10;
|
|
export const AGENT_GUI_PROVIDER_RAIL_WIDTH_PX = 52;
|
|
export const AGENT_GUI_COLLAPSED_MIN_WIDTH_PX = 460;
|
|
export const AGENT_GUI_STANDALONE_MIDDLE_CONTENT_MIN_WIDTH_PX =
|
|
AGENT_GUI_COLLAPSED_MIN_WIDTH_PX - AGENT_GUI_PROVIDER_RAIL_WIDTH_PX;
|
|
export const AGENT_GUI_EXPANDED_MIN_WIDTH_PX =
|
|
AGENT_GUI_CONVERSATION_RAIL_MIN_WIDTH_PX +
|
|
AGENT_GUI_DETAIL_MIN_WIDTH_PX +
|
|
AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX;
|
|
// One package-owned responsive threshold applies to every Agent GUI surface:
|
|
// 248px conversation rail + 320px composer + 10px resize handle + 52px
|
|
// provider rail = 630px. Hosts project container width and persisted user
|
|
// preference only; they do not replace this layout policy.
|
|
export const AGENT_GUI_AUTO_COLLAPSE_WIDTH_PX =
|
|
AGENT_GUI_CONVERSATION_RAIL_MIN_WIDTH_PX +
|
|
AGENT_GUI_HOME_COMPOSER_MIN_WIDTH_PX +
|
|
AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX +
|
|
AGENT_GUI_PROVIDER_RAIL_WIDTH_PX;
|
|
export const AGENT_GUI_EXPANDED_TARGET_WIDTH_PX = 800;
|
|
|
|
export interface AgentGUIConversationRailPresentation {
|
|
conversationRailWidthPx: number;
|
|
isAutoCollapsed: boolean;
|
|
isCollapsed: boolean;
|
|
}
|
|
|
|
export type AgentGUIConversationRailAutoCollapseMode =
|
|
| "default"
|
|
| "preserve-middle-content";
|
|
|
|
export interface AgentGUIExpandedWindowFrameInput {
|
|
position: { x: number; y: number };
|
|
width: number;
|
|
height: number;
|
|
desktopSize: { width: number; height: number };
|
|
conversationRailWidthPx: number | null | undefined;
|
|
}
|
|
|
|
export function shouldAutoCollapseAgentGUIConversationRail(
|
|
containerWidthPx: number,
|
|
options?: {
|
|
mode?: AgentGUIConversationRailAutoCollapseMode;
|
|
conversationRailWidthPx?: number | null;
|
|
}
|
|
): boolean {
|
|
if (
|
|
options?.mode === "preserve-middle-content" &&
|
|
Number.isFinite(containerWidthPx) &&
|
|
containerWidthPx > 0
|
|
) {
|
|
return (
|
|
containerWidthPx <
|
|
resolveStandaloneAgentGUIViewportMinimumWidthPx({
|
|
conversationRailCollapsed: false,
|
|
conversationRailWidthPx: options.conversationRailWidthPx
|
|
})
|
|
);
|
|
}
|
|
return (
|
|
Number.isFinite(containerWidthPx) &&
|
|
containerWidthPx > 0 &&
|
|
containerWidthPx <= AGENT_GUI_AUTO_COLLAPSE_WIDTH_PX
|
|
);
|
|
}
|
|
|
|
export function resolveAgentGUIConversationRailPresentation(input: {
|
|
autoCollapseMode?: AgentGUIConversationRailAutoCollapseMode;
|
|
containerWidthPx: number;
|
|
conversationRailCollapsed?: boolean | null;
|
|
conversationRailWidthPx?: number | null;
|
|
}): AgentGUIConversationRailPresentation {
|
|
const isAutoCollapsed = shouldAutoCollapseAgentGUIConversationRail(
|
|
input.containerWidthPx,
|
|
{
|
|
mode: input.autoCollapseMode,
|
|
conversationRailWidthPx: input.conversationRailWidthPx
|
|
}
|
|
);
|
|
return {
|
|
conversationRailWidthPx: clampAgentGUIConversationRailWidthPx(
|
|
input.conversationRailWidthPx,
|
|
input.containerWidthPx
|
|
),
|
|
isAutoCollapsed,
|
|
isCollapsed: input.conversationRailCollapsed === true || isAutoCollapsed
|
|
};
|
|
}
|
|
|
|
export function resolveStandaloneAgentGUIViewportMinimumWidthPx(input: {
|
|
conversationRailCollapsed?: boolean | null;
|
|
conversationRailWidthPx?: number | null;
|
|
}): number {
|
|
if (input.conversationRailCollapsed === true) {
|
|
return AGENT_GUI_COLLAPSED_MIN_WIDTH_PX;
|
|
}
|
|
return (
|
|
AGENT_GUI_PROVIDER_RAIL_WIDTH_PX +
|
|
resolvePreferredConversationRailWidthPx(input.conversationRailWidthPx) +
|
|
AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX +
|
|
AGENT_GUI_STANDALONE_MIDDLE_CONTENT_MIN_WIDTH_PX
|
|
);
|
|
}
|
|
|
|
export function resolveAgentGUIConversationRailMaxWidthPx(
|
|
containerWidthPx: number
|
|
): number {
|
|
const safeContainerWidth =
|
|
Number.isFinite(containerWidthPx) && containerWidthPx > 0
|
|
? containerWidthPx
|
|
: AGENT_GUI_CONVERSATION_RAIL_DEFAULT_WIDTH_PX +
|
|
AGENT_GUI_DETAIL_MIN_WIDTH_PX +
|
|
AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX;
|
|
|
|
return Math.max(
|
|
AGENT_GUI_CONVERSATION_RAIL_MIN_WIDTH_PX,
|
|
Math.min(
|
|
AGENT_GUI_CONVERSATION_RAIL_MAX_WIDTH_PX,
|
|
safeContainerWidth -
|
|
AGENT_GUI_DETAIL_MIN_WIDTH_PX -
|
|
AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX
|
|
)
|
|
);
|
|
}
|
|
|
|
export function clampAgentGUIConversationRailWidthPx(
|
|
widthPx: number | null | undefined,
|
|
containerWidthPx: number
|
|
): number {
|
|
return clamp(
|
|
resolvePreferredConversationRailWidthPx(widthPx),
|
|
AGENT_GUI_CONVERSATION_RAIL_MIN_WIDTH_PX,
|
|
resolveAgentGUIConversationRailMaxWidthPx(containerWidthPx)
|
|
);
|
|
}
|
|
|
|
function resolvePreferredConversationRailWidthPx(
|
|
widthPx: number | null | undefined
|
|
): number {
|
|
const preferredWidthPx =
|
|
typeof widthPx === "number" && Number.isFinite(widthPx)
|
|
? Math.round(widthPx)
|
|
: AGENT_GUI_CONVERSATION_RAIL_DEFAULT_WIDTH_PX;
|
|
return clamp(
|
|
preferredWidthPx,
|
|
AGENT_GUI_CONVERSATION_RAIL_MIN_WIDTH_PX,
|
|
AGENT_GUI_CONVERSATION_RAIL_MAX_WIDTH_PX
|
|
);
|
|
}
|
|
|
|
export function resolveNextAgentGUIConversationRailWidthPx(input: {
|
|
currentWidthPx: number | null | undefined;
|
|
requestedWidthPx: number;
|
|
containerWidthPx: number;
|
|
}): number | null {
|
|
const nextWidthPx = clampAgentGUIConversationRailWidthPx(
|
|
input.requestedWidthPx,
|
|
input.containerWidthPx
|
|
);
|
|
if (input.currentWidthPx === nextWidthPx) {
|
|
return input.currentWidthPx;
|
|
}
|
|
|
|
if (input.currentWidthPx === null || input.currentWidthPx === undefined) {
|
|
const defaultWidthPx = clampAgentGUIConversationRailWidthPx(
|
|
null,
|
|
input.containerWidthPx
|
|
);
|
|
if (nextWidthPx === defaultWidthPx) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return nextWidthPx;
|
|
}
|
|
|
|
export function resolveAgentGUIExpandedWindowFrame(
|
|
input: AgentGUIExpandedWindowFrameInput
|
|
): {
|
|
position: { x: number; y: number };
|
|
size: { width: number; height: number };
|
|
} {
|
|
const desiredRailWidthPx = clampAgentGUIConversationRailWidthPx(
|
|
input.conversationRailWidthPx,
|
|
AGENT_GUI_CONVERSATION_RAIL_MAX_WIDTH_PX +
|
|
AGENT_GUI_DETAIL_MIN_WIDTH_PX +
|
|
AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX
|
|
);
|
|
const desiredWidthPx = Math.max(
|
|
Math.round(input.width),
|
|
desiredRailWidthPx +
|
|
AGENT_GUI_DETAIL_MIN_WIDTH_PX +
|
|
AGENT_GUI_RAIL_RESIZE_HANDLE_WIDTH_PX,
|
|
AGENT_GUI_EXPANDED_MIN_WIDTH_PX,
|
|
AGENT_GUI_EXPANDED_TARGET_WIDTH_PX
|
|
);
|
|
const desktopWidthPx =
|
|
Number.isFinite(input.desktopSize.width) && input.desktopSize.width > 0
|
|
? Math.round(input.desktopSize.width)
|
|
: desiredWidthPx;
|
|
const nextWidthPx = Math.min(desiredWidthPx, desktopWidthPx);
|
|
const maxX = Math.max(0, desktopWidthPx - nextWidthPx);
|
|
const nextX = clamp(Math.round(input.position.x), 0, maxX);
|
|
|
|
return {
|
|
position: {
|
|
x: nextX,
|
|
y: input.position.y
|
|
},
|
|
size: {
|
|
width: nextWidthPx,
|
|
height: input.height
|
|
}
|
|
};
|
|
}
|