Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
180620e
Fix Relay Worker RuntimeContext wiring (#4653)
juliusmarminge Jul 27, 2026
da11342
Fix live sidebar resize limits and defer Alchemy runtime context (#4655)
juliusmarminge Jul 27, 2026
32843c2
fix(web): constrain branch toolbar context (#4657)
maxktz Jul 27, 2026
0e19c10
Keep MCP credentials alive across provider turns (#4659)
juliusmarminge Jul 27, 2026
f0121f3
fix: close actions dropdown when editing (#4660)
0x4bs3nt Jul 27, 2026
32af2f0
fix(preview): stabilize PiP viewport identity (#4661)
juliusmarminge Jul 27, 2026
80ead5f
Add glass styling for thread tooltips and simplify preview tab handli…
juliusmarminge Jul 27, 2026
8585ac5
feat(tim): import tim-smart/t3code#4
tim-smart Jul 26, 2026
dde30a7
feat(tim): import tim-smart/t3code#5
tim-smart Jul 26, 2026
1ef5fdb
feat(tim): import tim-smart/t3code#6
tim-smart Jul 26, 2026
45d70bc
feat(tim): import tim-smart/t3code#7
tim-smart Jul 26, 2026
e26acd2
feat(tim): import tim-smart/t3code#8
tim-smart Jul 26, 2026
35986cb
feat(tim): import tim-smart/t3code#9
tim-smart Jul 26, 2026
db880dc
feat(tim): import tim-smart/t3code#10
tim-smart Jul 26, 2026
73d3f34
feat(tim): import tim-smart/t3code#11
tim-smart Jul 26, 2026
75d4787
feat(tim): import tim-smart/t3code#12
tim-smart Jul 26, 2026
ba3826f
feat(tim): import tim-smart/t3code#13
tim-smart Jul 26, 2026
3827c2a
feat(tim): import tim-smart/t3code#14
tim-smart Jul 26, 2026
3601472
feat(tim): import tim-smart/t3code#15
tim-smart Jul 26, 2026
ac20abd
feat(tim): import tim-smart/t3code#16
tim-smart Jul 26, 2026
fc81dc3
feat(tim): import tim-smart/t3code#17
tim-smart Jul 26, 2026
87725ed
test(tim-compat): gate macOS bundle fixture to Darwin
patroza Jul 26, 2026
cfd99d3
fix(tim-compat): avoid plutil for custom open-with apps
patroza Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion apps/desktop/src/backend/DesktopBackendPool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ChildProcessSpawner } from "effect/unstable/process";
import * as DesktopObservability from "../app/DesktopObservability.ts";
import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts";
import * as ElectronDialog from "../electron/ElectronDialog.ts";
import * as MacApplicationIcon from "../electron/MacApplicationIcon.ts";
import * as DesktopWindow from "../window/DesktopWindow.ts";
import * as DesktopBackendConfiguration from "./DesktopBackendConfiguration.ts";
import * as DesktopBackendPool from "./DesktopBackendPool.ts";
Expand Down Expand Up @@ -66,7 +67,13 @@ function makePoolLayer(
resolveWsl: () => Effect.die("unexpected WSL config resolve"),
} satisfies DesktopBackendConfiguration.DesktopBackendConfiguration["Service"]),
DesktopAppSettings.layerTest(),
ElectronDialog.layer,
ElectronDialog.layer.pipe(
Layer.provide(
Layer.succeed(MacApplicationIcon.MacApplicationIcon, {
resolveDataUrl: () => Effect.die("unexpected application icon resolution"),
} satisfies MacApplicationIcon.MacApplicationIcon["Service"]),
),
),
Layer.succeed(DesktopWindow.DesktopWindow, {
createMain: Effect.die("unexpected window create"),
ensureMain: Effect.die("unexpected window ensure"),
Expand Down
95 changes: 83 additions & 12 deletions apps/desktop/src/electron/ElectronDialog.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import { assert, describe, it } from "@effect/vitest";
import * as Cause from "effect/Cause";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import type { BrowserWindow } from "electron";
import { beforeEach, vi } from "vite-plus/test";

import * as ElectronDialog from "./ElectronDialog.ts";
import * as MacApplicationIcon from "./MacApplicationIcon.ts";

const { showMessageBoxMock, showOpenDialogMock, showErrorBoxMock } = vi.hoisted(() => ({
showMessageBoxMock: vi.fn(),
showOpenDialogMock: vi.fn(),
showErrorBoxMock: vi.fn(),
}));
const { showMessageBoxMock, showOpenDialogMock, showErrorBoxMock, resolveDataUrlMock } = vi.hoisted(
() => ({
showMessageBoxMock: vi.fn(),
showOpenDialogMock: vi.fn(),
showErrorBoxMock: vi.fn(),
resolveDataUrlMock: vi.fn(),
}),
);

vi.mock("electron", () => ({
dialog: {
Expand All @@ -21,13 +26,79 @@ vi.mock("electron", () => ({
},
}));

const applicationIconLayer = Layer.succeed(MacApplicationIcon.MacApplicationIcon, {
resolveDataUrl: (applicationPath) =>
Effect.tryPromise({
try: () => resolveDataUrlMock(applicationPath),
catch: (cause) =>
new MacApplicationIcon.MacApplicationIconResolutionError({ applicationPath, cause }),
}),
} satisfies MacApplicationIcon.MacApplicationIcon["Service"]);
const dialogLayer = ElectronDialog.layer.pipe(Layer.provide(applicationIconLayer));

describe("ElectronDialog", () => {
beforeEach(() => {
showMessageBoxMock.mockReset();
showOpenDialogMock.mockReset();
showErrorBoxMock.mockReset();
resolveDataUrlMock.mockReset();
});

it.effect("selects a macOS application and resolves its system icon", () =>
Effect.gen(function* () {
const owner = { id: 12 } as BrowserWindow;
showOpenDialogMock.mockResolvedValue({
canceled: false,
filePaths: ["/Applications/Terminal.app"],
});
resolveDataUrlMock.mockResolvedValue("data:image/png;base64,icon");
const dialog = yield* ElectronDialog.ElectronDialog;

const result = yield* dialog.pickApplication({ owner: Option.some(owner) });

assert.deepEqual(Option.getOrNull(result), {
applicationPath: "/Applications/Terminal.app",
suggestedName: "Terminal",
iconDataUrl: "data:image/png;base64,icon",
});
assert.deepEqual(showOpenDialogMock.mock.calls[0], [
owner,
{
defaultPath: "/Applications",
properties: ["openFile"],
filters: [{ name: "Applications", extensions: ["app"] }],
},
]);
assert.deepEqual(resolveDataUrlMock.mock.calls[0], ["/Applications/Terminal.app"]);
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("returns none when application selection is cancelled", () =>
Effect.gen(function* () {
showOpenDialogMock.mockResolvedValue({ canceled: true, filePaths: [] });
const dialog = yield* ElectronDialog.ElectronDialog;
assert.isTrue(Option.isNone(yield* dialog.pickApplication({ owner: Option.none() })));
assert.equal(resolveDataUrlMock.mock.calls.length, 0);
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("keeps a valid selection when icon extraction fails", () =>
Effect.gen(function* () {
showOpenDialogMock.mockResolvedValue({
canceled: false,
filePaths: ["/Users/test/Applications/My Tool.app"],
});
resolveDataUrlMock.mockRejectedValue(new Error("icon unavailable"));
const dialog = yield* ElectronDialog.ElectronDialog;

assert.deepEqual(Option.getOrNull(yield* dialog.pickApplication({ owner: Option.none() })), {
applicationPath: "/Users/test/Applications/My Tool.app",
suggestedName: "My Tool",
iconDataUrl: null,
});
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("returns false without opening a confirm dialog for empty messages", () =>
Effect.gen(function* () {
const dialog = yield* ElectronDialog.ElectronDialog;
Expand All @@ -39,7 +110,7 @@ describe("ElectronDialog", () => {

assert.isFalse(result);
assert.equal(showMessageBoxMock.mock.calls.length, 0);
}).pipe(Effect.provide(ElectronDialog.layer)),
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("opens a confirm dialog for the owner window", () =>
Expand All @@ -65,7 +136,7 @@ describe("ElectronDialog", () => {
message: "Delete worktree?",
},
]);
}).pipe(Effect.provide(ElectronDialog.layer)),
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("opens an app-level confirm dialog when there is no owner window", () =>
Expand All @@ -89,7 +160,7 @@ describe("ElectronDialog", () => {
message: "Delete worktree?",
},
]);
}).pipe(Effect.provide(ElectronDialog.layer)),
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("preserves folder picker request context and cause", () =>
Expand All @@ -114,7 +185,7 @@ describe("ElectronDialog", () => {
assert.include(error.message, "window 7");
assert.include(error.message, "/workspace");
assert.notInclude(error.message, cause.message);
}).pipe(Effect.provide(ElectronDialog.layer)),
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("preserves confirmation request context and cause", () =>
Expand All @@ -139,7 +210,7 @@ describe("ElectronDialog", () => {
assert.include(error.message, "window 9");
assert.notInclude(error.message, "Confirm removal?");
assert.notInclude(error.message, cause.message);
}).pipe(Effect.provide(ElectronDialog.layer)),
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("preserves message box request context and cause", () =>
Expand Down Expand Up @@ -176,7 +247,7 @@ describe("ElectronDialog", () => {
assert.notInclude(error.message, "Cancel");
assert.notInclude(error.message, "Discard");
assert.notInclude(error.message, cause.message);
}).pipe(Effect.provide(ElectronDialog.layer)),
}).pipe(Effect.provide(dialogLayer)),
);

it.effect("preserves error box request context and cause in the defect", () =>
Expand All @@ -201,6 +272,6 @@ describe("ElectronDialog", () => {
assert.notInclude(error.message, "Startup failed");
assert.notInclude(error.message, "Could not start.");
assert.notInclude(error.message, cause.message);
}).pipe(Effect.provide(ElectronDialog.layer)),
}).pipe(Effect.provide(dialogLayer)),
);
});
Loading