feat: add iOS simulator worktree preview
Adds the worktree service, setup cache seeding, and Metro preview mount for /.sim. Also includes the related startup and mobile sidebar fixes needed by the updated worktree app flow.
This commit is contained in:
@@ -1,7 +1,22 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
canCloseLeftSidebarGesture,
|
||||
canCloseRightSidebarGesture,
|
||||
canOpenLeftSidebarGesture,
|
||||
canOpenRightSidebarGesture,
|
||||
getLeftSidebarAnimationTargets,
|
||||
getRightSidebarAnimationTargets,
|
||||
MOBILE_PANEL_STATE_AGENT,
|
||||
MOBILE_PANEL_STATE_AGENT_LIST_CLOSING,
|
||||
MOBILE_PANEL_STATE_AGENT_LIST_OPEN,
|
||||
MOBILE_PANEL_STATE_AGENT_LIST_OPENING,
|
||||
MOBILE_PANEL_STATE_FILE_EXPLORER_CLOSING,
|
||||
MOBILE_PANEL_STATE_FILE_EXPLORER_OPEN,
|
||||
MOBILE_PANEL_STATE_FILE_EXPLORER_OPENING,
|
||||
MOBILE_PANEL_TARGET_AGENT,
|
||||
MOBILE_PANEL_TARGET_AGENT_LIST,
|
||||
MOBILE_PANEL_TARGET_FILE_EXPLORER,
|
||||
shouldSettleMobilePanelTransition,
|
||||
shouldSyncSidebarAnimation,
|
||||
} from "./sidebar-animation-state";
|
||||
|
||||
@@ -41,4 +56,56 @@ describe("sidebar-animation-state", () => {
|
||||
backdropOpacity: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("allows the left open gesture only after the app is settled on the agent panel", () => {
|
||||
expect(canOpenLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT, -430, 430)).toBe(true);
|
||||
expect(canOpenLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT, -240, 430)).toBe(false);
|
||||
expect(canOpenLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT_LIST_CLOSING, -430, 430)).toBe(false);
|
||||
expect(canOpenLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT_LIST_OPENING, -430, 430)).toBe(false);
|
||||
expect(canOpenLeftSidebarGesture(MOBILE_PANEL_STATE_FILE_EXPLORER_OPEN, -430, 430)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows the left close gesture only while the left sidebar is settled open", () => {
|
||||
expect(canCloseLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT_LIST_OPEN)).toBe(true);
|
||||
expect(canCloseLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT_LIST_OPENING)).toBe(false);
|
||||
expect(canCloseLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT_LIST_CLOSING)).toBe(false);
|
||||
expect(canCloseLeftSidebarGesture(MOBILE_PANEL_STATE_AGENT)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows the right open gesture only after the app is settled on the agent panel", () => {
|
||||
expect(canOpenRightSidebarGesture(MOBILE_PANEL_STATE_AGENT, 430, 430)).toBe(true);
|
||||
expect(canOpenRightSidebarGesture(MOBILE_PANEL_STATE_AGENT, 240, 430)).toBe(false);
|
||||
expect(canOpenRightSidebarGesture(MOBILE_PANEL_STATE_FILE_EXPLORER_CLOSING, 430, 430)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(canOpenRightSidebarGesture(MOBILE_PANEL_STATE_FILE_EXPLORER_OPENING, 430, 430)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(canOpenRightSidebarGesture(MOBILE_PANEL_STATE_AGENT_LIST_OPEN, 430, 430)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows the right close gesture only while the right sidebar is settled open", () => {
|
||||
expect(canCloseRightSidebarGesture(MOBILE_PANEL_STATE_FILE_EXPLORER_OPEN)).toBe(true);
|
||||
expect(canCloseRightSidebarGesture(MOBILE_PANEL_STATE_FILE_EXPLORER_OPENING)).toBe(false);
|
||||
expect(canCloseRightSidebarGesture(MOBILE_PANEL_STATE_FILE_EXPLORER_CLOSING)).toBe(false);
|
||||
expect(canCloseRightSidebarGesture(MOBILE_PANEL_STATE_AGENT)).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects stale settle callbacks from a panel that is no longer the destination", () => {
|
||||
expect(
|
||||
shouldSettleMobilePanelTransition(
|
||||
MOBILE_PANEL_TARGET_AGENT_LIST,
|
||||
MOBILE_PANEL_TARGET_AGENT_LIST,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
shouldSettleMobilePanelTransition(MOBILE_PANEL_TARGET_AGENT_LIST, MOBILE_PANEL_TARGET_AGENT),
|
||||
).toBe(false);
|
||||
expect(
|
||||
shouldSettleMobilePanelTransition(
|
||||
MOBILE_PANEL_TARGET_AGENT_LIST,
|
||||
MOBILE_PANEL_TARGET_FILE_EXPLORER,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,20 @@ interface SidebarAnimationTargets {
|
||||
backdropOpacity: number;
|
||||
}
|
||||
|
||||
export const MOBILE_PANEL_STATE_AGENT = 0;
|
||||
export const MOBILE_PANEL_STATE_AGENT_LIST_OPENING = 1;
|
||||
export const MOBILE_PANEL_STATE_AGENT_LIST_OPEN = 2;
|
||||
export const MOBILE_PANEL_STATE_AGENT_LIST_CLOSING = 3;
|
||||
export const MOBILE_PANEL_STATE_FILE_EXPLORER_OPENING = 4;
|
||||
export const MOBILE_PANEL_STATE_FILE_EXPLORER_OPEN = 5;
|
||||
export const MOBILE_PANEL_STATE_FILE_EXPLORER_CLOSING = 6;
|
||||
|
||||
export const MOBILE_PANEL_TARGET_AGENT = 0;
|
||||
export const MOBILE_PANEL_TARGET_AGENT_LIST = 1;
|
||||
export const MOBILE_PANEL_TARGET_FILE_EXPLORER = 2;
|
||||
|
||||
const CLOSED_POSITION_TOLERANCE = 1;
|
||||
|
||||
export function shouldSyncSidebarAnimation(input: SidebarAnimationSyncInput): boolean {
|
||||
return (
|
||||
input.previousIsOpen !== input.nextIsOpen || input.previousWindowWidth !== input.nextWindowWidth
|
||||
@@ -38,3 +52,45 @@ export function getRightSidebarAnimationTargets(
|
||||
backdropOpacity: input.isOpen ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function canOpenLeftSidebarGesture(
|
||||
mobilePanelState: number,
|
||||
translateX: number,
|
||||
windowWidth: number,
|
||||
): boolean {
|
||||
"worklet";
|
||||
return (
|
||||
mobilePanelState === MOBILE_PANEL_STATE_AGENT &&
|
||||
translateX <= -windowWidth + CLOSED_POSITION_TOLERANCE
|
||||
);
|
||||
}
|
||||
|
||||
export function canCloseLeftSidebarGesture(mobilePanelState: number): boolean {
|
||||
"worklet";
|
||||
return mobilePanelState === MOBILE_PANEL_STATE_AGENT_LIST_OPEN;
|
||||
}
|
||||
|
||||
export function canOpenRightSidebarGesture(
|
||||
mobilePanelState: number,
|
||||
translateX: number,
|
||||
windowWidth: number,
|
||||
): boolean {
|
||||
"worklet";
|
||||
return (
|
||||
mobilePanelState === MOBILE_PANEL_STATE_AGENT &&
|
||||
translateX >= windowWidth - CLOSED_POSITION_TOLERANCE
|
||||
);
|
||||
}
|
||||
|
||||
export function canCloseRightSidebarGesture(mobilePanelState: number): boolean {
|
||||
"worklet";
|
||||
return mobilePanelState === MOBILE_PANEL_STATE_FILE_EXPLORER_OPEN;
|
||||
}
|
||||
|
||||
export function shouldSettleMobilePanelTransition(
|
||||
activeTarget: number,
|
||||
settledTarget: number,
|
||||
): boolean {
|
||||
"worklet";
|
||||
return activeTarget === settledTarget;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user