import { assert, describe, it } from "@effect/vitest";import * as Cause from "effect/Cause";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Layer from "effect/Layer";import * as Option from "effect/Option"; import * as ElectronWindow from "../../electron/ElectronWindow.ts";import * as ElectronDialog from "../../electron/ElectronDialog.ts";import * as DesktopSnapShot from "../../snapShot/DesktopSnapShot.ts";import { checkSnapShotShortcut, requestSnapShotPermissions, setupSnapShot, previewSnapShotConfig, applySnapShotConfig, setSnapShotAnimationDestination, setSnapShotShortcutSuppressed, snapShotScreenFrame, snapShotRelativeFrame,} from "./snapShot.ts"; describe("window capture IPC", () => { const configPreview = { id: "12345678-1234-1234-1234-123456789abc", path: "/config/niri/config.kdl", resolvedPath: "/config/niri/config.kdl", before: "binds {}\n", after: "binds {\n}\n", shortcut: "Ctrl+Shift+2", operation: "install" as const, }; it.effect("requires a trusted renderer for both config read and write approval", () => { const calls: string[] = []; return Effect.gen(function* () { const request = { operation: "install" as const, chooseFile: false }; const untrustedRead = yield* Effect.exit( previewSnapShotConfig.handler(request, { sender: { id: 8 } }), ); const untrustedWrite = yield* Effect.exit( applySnapShotConfig.handler(configPreview.id, { sender: { id: 8 } }), ); assert(Exit.isFailure(untrustedRead)); assert(Exit.isFailure(untrustedWrite)); assert.deepEqual(calls, []); yield* previewSnapShotConfig.handler(request, { sender: { id: 7 } }); assert.deepEqual(calls, ["read"]); yield* applySnapShotConfig.handler(configPreview.id, { sender: { id: 7 } }); assert.deepEqual(calls, ["read", configPreview.id]); }).pipe( Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { main: Effect.succeed(Option.some({ webContents: { id: 7 } })), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { previewConfig: () => Effect.sync(() => { calls.push("read"); return configPreview; }), applyConfig: (id: string) => Effect.sync(() => { calls.push(id); return { backupPath: null, warning: null }; }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), Layer.succeed( ElectronDialog.ElectronDialog, {} as ElectronDialog.ElectronDialog["Service"], ), ), ), ); }); it.effect("cancelling custom file selection reads and writes nothing", () => { let read = false; return Effect.gen(function* () { const preview = yield* previewSnapShotConfig.handler( { operation: "install", chooseFile: true }, { sender: { id: 7 } }, ); assert.isNull(preview); assert.isFalse(read); }).pipe( Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { main: Effect.succeed(Option.some({ webContents: { id: 7 } })), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { state: Effect.succeed({ linuxBackend: "niri", shortcutConfigPath: "/config/niri/config.kdl", }), previewConfig: () => Effect.sync(() => { read = true; return configPreview; }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), Layer.succeed(ElectronDialog.ElectronDialog, { pickFiles: () => Effect.succeed([]), } as unknown as ElectronDialog.ElectronDialog["Service"]), ), ), ); }); it.effect("uses only the file returned by the native custom config picker", () => { let path: string | undefined; return Effect.gen(function* () { yield* previewSnapShotConfig.handler( { operation: "install", chooseFile: true }, { sender: { id: 7 } }, ); assert.equal(path, "/chosen/config.kdl"); }).pipe( Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { main: Effect.succeed(Option.some({ webContents: { id: 7 } })), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { state: Effect.succeed({ linuxBackend: "niri" }), previewConfig: (_: unknown, selected: string) => Effect.sync(() => { path = selected; return configPreview; }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), Layer.succeed(ElectronDialog.ElectronDialog, { pickFiles: () => Effect.succeed(["/chosen/config.kdl"]), } as unknown as ElectronDialog.ElectronDialog["Service"]), ), ), ); }); it("converts renderer viewport coordinates from the content origin using the window zoom", () => { assert.deepEqual( snapShotScreenFrame( { x: 12, y: 20, width: 208, height: 112 }, { x: 100, y: 80, width: 1_000, height: 700 }, 1.25, ), { x: 115, y: 105, width: 260, height: 140 }, ); }); it.effect("forwards a trusted renderer animation destination in screen coordinates", () => { let received: unknown; const webContents = { id: 7, getZoomFactor: () => 1.25 }; const layer = Layer.mergeAll( Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ main: Effect.succeed( Option.some({ getBounds: () => ({ x: 100, y: 80, width: 1_000, height: 700 }), getContentBounds: () => ({ x: 100, y: 118, width: 1_000, height: 662 }), webContents, }), ), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( DesktopSnapShot.DesktopSnapShot, DesktopSnapShot.DesktopSnapShot.of({ setAnimationDestination: (id: string, destination: unknown) => Effect.sync(() => { received = { id, destination }; }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), ), ); return Effect.gen(function* () { yield* setSnapShotAnimationDestination.handler( { id: "12345678-1234-1234-1234-123456789abc", viewportFrame: { x: 12, y: 20, width: 208, height: 112 }, backgroundColor: "rgb(20, 20, 20)", borderColor: "rgba(80, 80, 80, 0.8)", borderWidth: 1, cornerRadius: 8, details: { appName: "T3 Code", windowTitle: "Capture animation", appIconDataUrl: "data:image/png;base64,aWNvbg==", }, }, { sender: webContents }, ); assert.deepEqual(received, { id: "12345678-1234-1234-1234-123456789abc", destination: { relativeFrame: { x: 15 / 1000, y: 25 / 662, width: 260 / 1000, height: 140 / 662 }, frame: { x: 115, y: 143, width: 260, height: 140 }, backgroundColor: "rgb(20, 20, 20)", borderColor: "rgba(80, 80, 80, 0.8)", borderWidth: 1.25, cornerRadius: 10, scaleFactor: 1.25, details: { appName: "T3 Code", windowTitle: "Capture animation", appIconDataUrl: "data:image/png;base64,aWNvbg==", }, }, }); }).pipe(Effect.provide(layer)); }); it.effect("forwards the accessibility permission preference from a trusted renderer", () => { let includeAccessibility: boolean | undefined; const webContents = { id: 7 }; const layer = Layer.mergeAll( Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ main: Effect.succeed(Option.some({ webContents })), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( DesktopSnapShot.DesktopSnapShot, DesktopSnapShot.DesktopSnapShot.of({ requestPermissions: (include: boolean) => Effect.sync(() => { includeAccessibility = include; }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), ), ); return Effect.gen(function* () { yield* requestSnapShotPermissions.handler(false, { sender: webContents }); assert.isFalse(includeAccessibility); }).pipe(Effect.provide(layer)); }); it.effect("rejects an untrusted renderer at the IPC boundary", () => Effect.gen(function* () { const exit = yield* Effect.exit( requestSnapShotPermissions.handler(false, { sender: { id: 8 } }), ); assert(Exit.isFailure(exit)); const failure = Cause.findErrorOption(exit.cause); assert(Option.isSome(failure)); const error = failure.value; assert.equal((error as { readonly _tag: string })._tag, "SnapShotIpcUnauthorizedSenderError"); assert.equal((error as Error).message, "Snapshot request was rejected."); }).pipe( Effect.provideService( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ main: Effect.succeed(Option.some({ webContents: { id: 7 } })), } as ElectronWindow.ElectronWindow["Service"]), ), Effect.provideService(DesktopSnapShot.DesktopSnapShot, null as never), ), ); it.effect("allows capture setup only from the trusted main renderer", () => { const actions: string[] = []; return Effect.gen(function* () { yield* setupSnapShot.handler("install-extension", { sender: { id: 7 } }); assert.deepEqual(actions, ["install-extension"]); const rejected = yield* Effect.exit( setupSnapShot.handler("enable-extension", { sender: { id: 8 } }), ); assert(Exit.isFailure(rejected)); assert.deepEqual(actions, ["install-extension"]); }).pipe( Effect.provide( Layer.mergeAll( Layer.succeed(ElectronWindow.ElectronWindow, { main: Effect.succeed(Option.some({ webContents: { id: 7 } })), } as ElectronWindow.ElectronWindow["Service"]), Layer.succeed(DesktopSnapShot.DesktopSnapShot, { setup: (action: string) => Effect.sync(() => { actions.push(action); }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), ), ), ); }); it.effect("checks shortcut availability for a trusted renderer", () => { const layer = Layer.mergeAll( Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ main: Effect.succeed(Option.some({ webContents: { id: 7 } })), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( DesktopSnapShot.DesktopSnapShot, DesktopSnapShot.DesktopSnapShot.of({ checkShortcut: () => Effect.succeed({ available: true, message: null }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), ), ); return Effect.gen(function* () { const result = yield* checkSnapShotShortcut.handler( { kind: "both-shift-keys" }, { sender: { id: 7 } }, ); assert.deepEqual(result, { available: true, message: null }); }).pipe(Effect.provide(layer)); }); it.effect("suppresses the active shortcut for a trusted renderer", () => { let suppressed = false; const layer = Layer.mergeAll( Layer.succeed( ElectronWindow.ElectronWindow, ElectronWindow.ElectronWindow.of({ main: Effect.succeed(Option.some({ webContents: { id: 7 } })), } as ElectronWindow.ElectronWindow["Service"]), ), Layer.succeed( DesktopSnapShot.DesktopSnapShot, DesktopSnapShot.DesktopSnapShot.of({ setShortcutSuppressed: (next: boolean) => Effect.sync(() => { suppressed = next; }), } as unknown as DesktopSnapShot.DesktopSnapShot["Service"]), ), ); return Effect.gen(function* () { yield* setSnapShotShortcutSuppressed.handler(true, { sender: { id: 7 } }); assert.isTrue(suppressed); }).pipe(Effect.provide(layer)); });}); it("normalizes Wayland attachment coordinates without trusting Electron's screen origin", () => { const frame = { x: 100, y: 300, width: 200, height: 100 }; const bounds = { x: 0, y: 0, width: 1000, height: 600 }; assert.deepEqual(snapShotRelativeFrame(frame, bounds, 1.25), { x: 0.125, y: 0.625, width: 0.25, height: 125 / 600, }); assert.deepEqual( snapShotRelativeFrame(frame, { ...bounds, x: -3840, y: 900 }, 1.25), snapShotRelativeFrame(frame, bounds, 1.25), ); assert.isUndefined(snapShotRelativeFrame(frame, { ...bounds, width: 0 }, 1));});
import { assert, describe, it } from "@effect/vitest";
import * as Cause from "effect/Cause";
import * as Effect from "effect/Effect";
import * as Exit from "effect/Exit";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as ElectronWindow from "../../electron/ElectronWindow.ts";
import * as ElectronDialog from "../../electron/ElectronDialog.ts";
import * as DesktopSnapShot from "../../snapShot/DesktopSnapShot.ts";
import {
checkSnapShotShortcut,
requestSnapShotPermissions,
setupSnapShot,
previewSnapShotConfig,
applySnapShotConfig,
setSnapShotAnimationDestination,
setSnapShotShortcutSuppressed,
snapShotScreenFrame,
snapShotRelativeFrame,
} from "./snapShot.ts";
describe("window capture IPC", () => {
const configPreview = {
id: "12345678-1234-1234-1234-123456789abc",
path: "/config/niri/config.kdl",
resolvedPath: "/config/niri/config.kdl",
before: "binds {}\n",
after: "binds {\n}\n",
shortcut: "Ctrl+Shift+2",
operation: "install" as const,
};
it.effect("requires a trusted renderer for both config read and write approval", () => {
const calls: string[] = [];
return Effect.gen(function* () {
const request = { operation: "install" as const, chooseFile: false };
const untrustedRead = yield* Effect.exit(
previewSnapShotConfig.handler(request, { sender: { id: 8 } }),
);
const untrustedWrite = yield* Effect.exit(
applySnapShotConfig.handler(configPreview.id, { sender: { id: 8 } }),
);
assert(Exit.isFailure(untrustedRead));
assert(Exit.isFailure(untrustedWrite));
assert.deepEqual(calls, []);
yield* previewSnapShotConfig.handler(request, { sender: { id: 7 } });
assert.deepEqual(calls, ["read"]);
yield* applySnapShotConfig.handler(configPreview.id, { sender: { id: 7 } });
assert.deepEqual(calls, ["read", configPreview.id]);
}).pipe(
Effect.provide(
Layer.mergeAll(
Layer.succeed(ElectronWindow.ElectronWindow, {
main: Effect.succeed(Option.some({ webContents: { id: 7 } })),
} as ElectronWindow.ElectronWindow["Service"]),
Layer.succeed(DesktopSnapShot.DesktopSnapShot, {
previewConfig: () =>
Effect.sync(() => {
calls.push("read");
return configPreview;
}),
applyConfig: (id: string) =>
Effect.sync(() => {
calls.push(id);
return { backupPath: null, warning: null };
}),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
Layer.succeed(
ElectronDialog.ElectronDialog,
{} as ElectronDialog.ElectronDialog["Service"],
),
),
),
);
});
it.effect("cancelling custom file selection reads and writes nothing", () => {
let read = false;
return Effect.gen(function* () {
const preview = yield* previewSnapShotConfig.handler(
{ operation: "install", chooseFile: true },
{ sender: { id: 7 } },
);
assert.isNull(preview);
assert.isFalse(read);
}).pipe(
Effect.provide(
Layer.mergeAll(
Layer.succeed(ElectronWindow.ElectronWindow, {
main: Effect.succeed(Option.some({ webContents: { id: 7 } })),
} as ElectronWindow.ElectronWindow["Service"]),
Layer.succeed(DesktopSnapShot.DesktopSnapShot, {
state: Effect.succeed({
linuxBackend: "niri",
shortcutConfigPath: "/config/niri/config.kdl",
}),
previewConfig: () =>
Effect.sync(() => {
read = true;
return configPreview;
}),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
Layer.succeed(ElectronDialog.ElectronDialog, {
pickFiles: () => Effect.succeed([]),
} as unknown as ElectronDialog.ElectronDialog["Service"]),
),
),
);
});
it.effect("uses only the file returned by the native custom config picker", () => {
let path: string | undefined;
return Effect.gen(function* () {
yield* previewSnapShotConfig.handler(
{ operation: "install", chooseFile: true },
{ sender: { id: 7 } },
);
assert.equal(path, "/chosen/config.kdl");
}).pipe(
Effect.provide(
Layer.mergeAll(
Layer.succeed(ElectronWindow.ElectronWindow, {
main: Effect.succeed(Option.some({ webContents: { id: 7 } })),
} as ElectronWindow.ElectronWindow["Service"]),
Layer.succeed(DesktopSnapShot.DesktopSnapShot, {
state: Effect.succeed({ linuxBackend: "niri" }),
previewConfig: (_: unknown, selected: string) =>
Effect.sync(() => {
path = selected;
return configPreview;
}),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
Layer.succeed(ElectronDialog.ElectronDialog, {
pickFiles: () => Effect.succeed(["/chosen/config.kdl"]),
} as unknown as ElectronDialog.ElectronDialog["Service"]),
),
),
);
});
it("converts renderer viewport coordinates from the content origin using the window zoom", () => {
assert.deepEqual(
snapShotScreenFrame(
{ x: 12, y: 20, width: 208, height: 112 },
{ x: 100, y: 80, width: 1_000, height: 700 },
1.25,
),
{ x: 115, y: 105, width: 260, height: 140 },
);
});
it.effect("forwards a trusted renderer animation destination in screen coordinates", () => {
let received: unknown;
const webContents = { id: 7, getZoomFactor: () => 1.25 };
const layer = Layer.mergeAll(
Layer.succeed(
ElectronWindow.ElectronWindow,
ElectronWindow.ElectronWindow.of({
main: Effect.succeed(
Option.some({
getBounds: () => ({ x: 100, y: 80, width: 1_000, height: 700 }),
getContentBounds: () => ({ x: 100, y: 118, width: 1_000, height: 662 }),
webContents,
}),
),
} as ElectronWindow.ElectronWindow["Service"]),
),
Layer.succeed(
DesktopSnapShot.DesktopSnapShot,
DesktopSnapShot.DesktopSnapShot.of({
setAnimationDestination: (id: string, destination: unknown) =>
Effect.sync(() => {
received = { id, destination };
}),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
),
);
return Effect.gen(function* () {
yield* setSnapShotAnimationDestination.handler(
{
id: "12345678-1234-1234-1234-123456789abc",
viewportFrame: { x: 12, y: 20, width: 208, height: 112 },
backgroundColor: "rgb(20, 20, 20)",
borderColor: "rgba(80, 80, 80, 0.8)",
borderWidth: 1,
cornerRadius: 8,
details: {
appName: "T3 Code",
windowTitle: "Capture animation",
appIconDataUrl: "data:image/png;base64,aWNvbg==",
},
},
{ sender: webContents },
);
assert.deepEqual(received, {
id: "12345678-1234-1234-1234-123456789abc",
destination: {
relativeFrame: { x: 15 / 1000, y: 25 / 662, width: 260 / 1000, height: 140 / 662 },
frame: { x: 115, y: 143, width: 260, height: 140 },
backgroundColor: "rgb(20, 20, 20)",
borderColor: "rgba(80, 80, 80, 0.8)",
borderWidth: 1.25,
cornerRadius: 10,
scaleFactor: 1.25,
details: {
appName: "T3 Code",
windowTitle: "Capture animation",
appIconDataUrl: "data:image/png;base64,aWNvbg==",
},
},
});
}).pipe(Effect.provide(layer));
});
it.effect("forwards the accessibility permission preference from a trusted renderer", () => {
let includeAccessibility: boolean | undefined;
const webContents = { id: 7 };
const layer = Layer.mergeAll(
Layer.succeed(
ElectronWindow.ElectronWindow,
ElectronWindow.ElectronWindow.of({
main: Effect.succeed(Option.some({ webContents })),
} as ElectronWindow.ElectronWindow["Service"]),
),
Layer.succeed(
DesktopSnapShot.DesktopSnapShot,
DesktopSnapShot.DesktopSnapShot.of({
requestPermissions: (include: boolean) =>
Effect.sync(() => {
includeAccessibility = include;
}),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
),
);
return Effect.gen(function* () {
yield* requestSnapShotPermissions.handler(false, { sender: webContents });
assert.isFalse(includeAccessibility);
}).pipe(Effect.provide(layer));
});
it.effect("rejects an untrusted renderer at the IPC boundary", () =>
Effect.gen(function* () {
const exit = yield* Effect.exit(
requestSnapShotPermissions.handler(false, { sender: { id: 8 } }),
);
assert(Exit.isFailure(exit));
const failure = Cause.findErrorOption(exit.cause);
assert(Option.isSome(failure));
const error = failure.value;
assert.equal((error as { readonly _tag: string })._tag, "SnapShotIpcUnauthorizedSenderError");
assert.equal((error as Error).message, "Snapshot request was rejected.");
}).pipe(
Effect.provideService(
ElectronWindow.ElectronWindow,
ElectronWindow.ElectronWindow.of({
main: Effect.succeed(Option.some({ webContents: { id: 7 } })),
} as ElectronWindow.ElectronWindow["Service"]),
),
Effect.provideService(DesktopSnapShot.DesktopSnapShot, null as never),
),
);
it.effect("allows capture setup only from the trusted main renderer", () => {
const actions: string[] = [];
return Effect.gen(function* () {
yield* setupSnapShot.handler("install-extension", { sender: { id: 7 } });
assert.deepEqual(actions, ["install-extension"]);
const rejected = yield* Effect.exit(
setupSnapShot.handler("enable-extension", { sender: { id: 8 } }),
);
assert(Exit.isFailure(rejected));
assert.deepEqual(actions, ["install-extension"]);
}).pipe(
Effect.provide(
Layer.mergeAll(
Layer.succeed(ElectronWindow.ElectronWindow, {
main: Effect.succeed(Option.some({ webContents: { id: 7 } })),
} as ElectronWindow.ElectronWindow["Service"]),
Layer.succeed(DesktopSnapShot.DesktopSnapShot, {
setup: (action: string) =>
Effect.sync(() => {
actions.push(action);
}),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
),
),
);
});
it.effect("checks shortcut availability for a trusted renderer", () => {
const layer = Layer.mergeAll(
Layer.succeed(
ElectronWindow.ElectronWindow,
ElectronWindow.ElectronWindow.of({
main: Effect.succeed(Option.some({ webContents: { id: 7 } })),
} as ElectronWindow.ElectronWindow["Service"]),
),
Layer.succeed(
DesktopSnapShot.DesktopSnapShot,
DesktopSnapShot.DesktopSnapShot.of({
checkShortcut: () => Effect.succeed({ available: true, message: null }),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
),
);
return Effect.gen(function* () {
const result = yield* checkSnapShotShortcut.handler(
{ kind: "both-shift-keys" },
{ sender: { id: 7 } },
);
assert.deepEqual(result, { available: true, message: null });
}).pipe(Effect.provide(layer));
});
it.effect("suppresses the active shortcut for a trusted renderer", () => {
let suppressed = false;
const layer = Layer.mergeAll(
Layer.succeed(
ElectronWindow.ElectronWindow,
ElectronWindow.ElectronWindow.of({
main: Effect.succeed(Option.some({ webContents: { id: 7 } })),
} as ElectronWindow.ElectronWindow["Service"]),
),
Layer.succeed(
DesktopSnapShot.DesktopSnapShot,
DesktopSnapShot.DesktopSnapShot.of({
setShortcutSuppressed: (next: boolean) =>
Effect.sync(() => {
suppressed = next;
}),
} as unknown as DesktopSnapShot.DesktopSnapShot["Service"]),
),
);
return Effect.gen(function* () {
yield* setSnapShotShortcutSuppressed.handler(true, { sender: { id: 7 } });
assert.isTrue(suppressed);
}).pipe(Effect.provide(layer));
});
});
it("normalizes Wayland attachment coordinates without trusting Electron's screen origin", () => {
const frame = { x: 100, y: 300, width: 200, height: 100 };
const bounds = { x: 0, y: 0, width: 1000, height: 600 };
assert.deepEqual(snapShotRelativeFrame(frame, bounds, 1.25), {
x: 0.125,
y: 0.625,
width: 0.25,
height: 125 / 600,
});
assert.deepEqual(
snapShotRelativeFrame(frame, { ...bounds, x: -3840, y: 900 }, 1.25),
snapShotRelativeFrame(frame, bounds, 1.25),
);
assert.isUndefined(snapShotRelativeFrame(frame, { ...bounds, width: 0 }, 1));
});