On a mobile device (or ?mobile=1) the editors run canvas-only with touch gestures driving the view: - touch-gestures.ts: pure recognizer (unit-tested) + DOM shim installed in boot preRun — one-finger drag → synthetic middle-drag (pan), pinch → synthetic wheel at the centroid (zoom-to-cursor), tap → left click. preRun registration order is what lets stopImmediatePropagation suppress the wx layer's single-finger→LEFT-drag touch mapping. - kicadSetChrome(bool) embind: hides all AUI panes except DrawFrame + the menubar/status bar via generic wx APIs (kicad fork untouched); boot polls it after runtime init. Pairs with the wxwidgets IsShown layout fix. - mobile-mode.ts: ?mobile=1/0 override or UA-CH/coarse-pointer autodetect; shell hides its overlays and the inherent-to-mobile preflight warnings. - e2e: mobile-chromium project (Pixel 7) + 4 specs (chrome-less, tap, pinch, pan) with screenshot-invertibility assertions; also fixes tool-switch.spec's stale pre-scope-refactor URLs (was broken on main). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { isMobileMode, type MobileModeWindow } from "./mobile-mode";
|
|
|
|
/**
|
|
* Mobile-mode resolution (features/mobile): the explicit `?mobile=` URL param
|
|
* always wins; otherwise fall back to device detection (UA-CH mobile flag, or
|
|
* coarse pointer + narrow viewport — the same signals capabilities.ts warns on).
|
|
*/
|
|
|
|
function fakeWin(opts: {
|
|
search?: string;
|
|
uaMobile?: boolean;
|
|
coarse?: boolean;
|
|
narrow?: boolean;
|
|
noMatchMedia?: boolean;
|
|
}): MobileModeWindow {
|
|
return {
|
|
location: { search: opts.search ?? "" },
|
|
navigator: { userAgentData: opts.uaMobile === undefined ? undefined : { mobile: opts.uaMobile } },
|
|
matchMedia: opts.noMatchMedia
|
|
? undefined
|
|
: (query: string) => ({
|
|
matches: query.includes("pointer") ? (opts.coarse ?? false) : (opts.narrow ?? false),
|
|
}),
|
|
};
|
|
}
|
|
|
|
describe("isMobileMode", () => {
|
|
it("?mobile=1 forces mobile mode on a desktop device", () => {
|
|
expect(isMobileMode(fakeWin({ search: "?mobile=1" }))).toBe(true);
|
|
expect(isMobileMode(fakeWin({ search: "?foo=bar&mobile=true" }))).toBe(true);
|
|
});
|
|
|
|
it("?mobile=0 forces desktop mode on a mobile device", () => {
|
|
expect(
|
|
isMobileMode(fakeWin({ search: "?mobile=0", uaMobile: true, coarse: true, narrow: true })),
|
|
).toBe(false);
|
|
expect(isMobileMode(fakeWin({ search: "?mobile=false", uaMobile: true }))).toBe(false);
|
|
});
|
|
|
|
it("auto-detects via userAgentData.mobile", () => {
|
|
expect(isMobileMode(fakeWin({ uaMobile: true }))).toBe(true);
|
|
expect(isMobileMode(fakeWin({ uaMobile: false }))).toBe(false);
|
|
});
|
|
|
|
it("auto-detects via coarse pointer + narrow viewport", () => {
|
|
expect(isMobileMode(fakeWin({ coarse: true, narrow: true }))).toBe(true);
|
|
// a touch-screen desktop (coarse but wide) is NOT mobile
|
|
expect(isMobileMode(fakeWin({ coarse: true, narrow: false }))).toBe(false);
|
|
expect(isMobileMode(fakeWin({ coarse: false, narrow: true }))).toBe(false);
|
|
});
|
|
|
|
it("defaults to desktop when nothing is detectable", () => {
|
|
expect(isMobileMode(fakeWin({}))).toBe(false);
|
|
expect(isMobileMode(fakeWin({ noMatchMedia: true }))).toBe(false);
|
|
});
|
|
});
|