Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
095d575
fix(desktop): hold-to-quit no longer strands the quit (#11016)
maria-rcks Sep 11, 2026
4a8ab1b
feat(web): mark projects on another machine in project pickers (#11323)
maria-rcks Sep 11, 2026
8711929
fix(web): show pointer cursors on pull request controls (#11283)
shivamhwp Sep 11, 2026
867eb9b
fix(web): themed panel toggles show their disabled state (#11188)
flamboh Sep 11, 2026
50791a0
fix(web): use branch wording in commit dialogs (#11281)
shivamhwp Sep 11, 2026
cd64ad3
fix(mobile): keep Android file icons on the line with wrapped filenam…
SunkenInTime Sep 11, 2026
a92161a
fix(codex): preserve qualified model ids in selection and generation …
maria-rcks Sep 11, 2026
36668db
feat(desktop): share macOS permission onboarding (#11289)
juliusmarminge Sep 11, 2026
8a2d5f5
fix(test): drain worker broadcasts before restoring browser globals (…
maria-rcks Sep 12, 2026
8461c25
fix(web): disable linked pull requests when none are linked (#11348)
maria-rcks Sep 12, 2026
1f73a89
fix(models): default to astra medium and fable 5.1 medium (#11347)
maria-rcks Sep 12, 2026
4d16e6b
fix(web): align provider settings with shared settings rows (#10571)
maria-rcks Sep 12, 2026
cf1ba3d
feat(settings): configure default permissions for new threads (#11346)
maria-rcks Sep 12, 2026
fd5553f
fix: restore provider history and prompts when rewinding (#11338)
maria-rcks Sep 12, 2026
25da986
fix(web): keep comment actions visible when pr comments are folded (#…
maria-rcks Sep 12, 2026
efccda9
feat: rewind conversations while keeping file changes (#11358)
maria-rcks Sep 12, 2026
3882778
fix(web): keep sidebar scroll position when pinning threads (#10757)
saphid Sep 12, 2026
8eec78c
fix(web): remove pr description reactions (#11361)
maria-rcks Sep 12, 2026
e816064
fix(desktop): keep preview keystrokes out of the composer (#11354)
maria-rcks Sep 12, 2026
74f4fe0
chore: sync upstream main
matheustimbo Sep 12, 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
17 changes: 2 additions & 15 deletions apps/desktop/src/electron/ElectronShell.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MAC_PERMISSION_SETTINGS_URLS } from "../permissions/MacPermission.ts";
import {
REMOTE_CAPABLE_EDITOR_IDS,
remoteSchemeForEditor,
Expand All @@ -10,20 +11,6 @@ import * as Option from "effect/Option";

import * as Electron from "electron";

/**
* Deep links to individual System Settings panes. These are app-fixed, not
* renderer-supplied, so they skip `parseSafeExternalUrl` — which exists to keep
* arbitrary link schemes from reaching the OS handler — and open through their
* own path below. The pane rather than the URL crosses the IPC boundary, so a
* renderer can only ask for one of these known destinations.
*
* Full Disk Access uses the post-Ventura `PrivacySecurity.extension` anchor.
*/
const SYSTEM_SETTINGS_URLS: Record<SystemSettingsPane, string> = {
"full-disk-access":
"x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_AllFiles",
};

// Remote open-in-editor deep links (`vscode://vscode-remote/ssh-remote+…`,
// `zed://ssh/<host>/<path>`) must reach the OS handler; every other non-web
// scheme stays blocked.
Expand Down Expand Up @@ -88,7 +75,7 @@ export const make = ElectronShell.of({
}),
openSystemSettings: (pane) =>
Effect.promise(() =>
Electron.shell.openExternal(SYSTEM_SETTINGS_URLS[pane]).then(
Electron.shell.openExternal(MAC_PERMISSION_SETTINGS_URLS[pane]).then(
() => true,
() => false,
),
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/ipc/DesktopIpcHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
getWindowFullscreenState,
openExternal,
openSystemSettings,
checkSystemPermission,
probeRemoteEditors,
pickFolder,
pickProjectFavicon,
Expand Down Expand Up @@ -122,6 +123,7 @@ export const installDesktopIpcHandlers = Effect.fn("desktop.ipc.installHandlers"
yield* ipc.handle(showContextMenu);
yield* ipc.handle(openExternal);
yield* ipc.handle(openSystemSettings);
yield* ipc.handle(checkSystemPermission);
yield* ipc.handle(probeRemoteEditors);
yield* ipc.handle(getUpdateState);
yield* ipc.handle(setUpdateChannel);
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/ipc/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,7 @@ export const PREVIEW_RECORDING_SAVE_CHANNEL = "desktop:preview-recording-save";
export const PREVIEW_RECORDING_FRAME_CHANNEL = "desktop:preview-recording-frame";
export const PREVIEW_STATE_CHANGE_CHANNEL = "desktop:preview-state-change";
export const PREVIEW_POINTER_EVENT_CHANNEL = "desktop:preview-pointer-event";

export const MAC_PERMISSION_HELPER_CHANNEL = "desktop:mac-permission-helper";

export const CHECK_SYSTEM_PERMISSION_CHANNEL = "desktop:check-system-permission";
26 changes: 25 additions & 1 deletion apps/desktop/src/ipc/methods/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ import * as ElectronMenu from "../../electron/ElectronMenu.ts";
import * as ElectronShell from "../../electron/ElectronShell.ts";
import * as ElectronTheme from "../../electron/ElectronTheme.ts";
import * as ElectronWindow from "../../electron/ElectronWindow.ts";
import * as Electron from "electron";
import * as MacPermissions from "../../permissions/MacPermissions.ts";
import { safariPermissionCheck } from "../../preview/BrowserImport/SafariPermission.ts";
import * as IpcChannels from "../channels.ts";
import * as DesktopIpc from "../DesktopIpc.ts";
import {
Expand Down Expand Up @@ -322,7 +325,16 @@ export const openSystemSettings = DesktopIpc.makeIpcMethod({
result: Schema.Boolean,
handler: Effect.fn("desktop.ipc.window.openSystemSettings")(function* (pane) {
const shell = yield* ElectronShell.ElectronShell;
return yield* shell.openSystemSettings(pane);
const environment = yield* DesktopEnvironment.DesktopEnvironment;
if (environment.platform !== "darwin") return false;
const owner = Electron.BrowserWindow.getFocusedWindow();
const opened = yield* shell.openSystemSettings(pane);
if (opened && environment.isPackaged) {
const permissions = yield* MacPermissions.MacPermissions;
const isGranted = yield* safariPermissionCheck;
yield* permissions.showHelper(pane, owner, isGranted);
}
return opened;
}),
});

Expand Down Expand Up @@ -396,3 +408,15 @@ export const pickThemeFiles = DesktopIpc.makeIpcMethod({
});
}),
});

export const checkSystemPermission = DesktopIpc.makeIpcMethod({
channel: IpcChannels.CHECK_SYSTEM_PERMISSION_CHANNEL,
payload: SystemSettingsPaneSchema,
result: Schema.Boolean,
handler: Effect.fn("desktop.ipc.window.checkSystemPermission")(function* () {
const environment = yield* DesktopEnvironment.DesktopEnvironment;
if (environment.platform !== "darwin") return false;
const check = yield* safariPermissionCheck;
return yield* Effect.promise(check);
}),
});
17 changes: 17 additions & 0 deletions apps/desktop/src/mac-permission-preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ipcRenderer } from "electron";
import { MAC_PERMISSION_HELPER_CHANNEL } from "./ipc/channels.ts";

// This preload belongs only to the static permission panel. No general desktop bridge is exposed.
window.addEventListener("DOMContentLoaded", () => {
const send = (action: "drag" | "finder" | "close") =>
ipcRenderer.send(MAC_PERMISSION_HELPER_CHANNEL, action);
document.getElementById("app")?.addEventListener("dragstart", (event) => {
event.preventDefault();
send("drag");
});
document.getElementById("app")?.addEventListener("click", () => send("finder"));
document.getElementById("close")?.addEventListener("click", () => send("close"));
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") send("close");
});
});
2 changes: 2 additions & 0 deletions apps/desktop/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as MacPermissions from "./permissions/MacPermissions.ts";
for (const stream of [process.stdout, process.stderr]) {
stream.on("error", (err: NodeJS.ErrnoException) => {
if (err.code !== "EPIPE") throw err;
Expand Down Expand Up @@ -133,6 +134,7 @@ const electronLayer = Layer.mergeAll(
);

const desktopFoundationLayer = Layer.mergeAll(
MacPermissions.layer,
DesktopState.layer,
DesktopShutdown.layer,
DesktopAppSettings.layer,
Expand Down
15 changes: 15 additions & 0 deletions apps/desktop/src/permissions/MacPermission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const MAC_PERMISSION_SETTINGS_URLS = {
"screen-recording":
"x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture",
accessibility: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility",
"full-disk-access":
"x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_AllFiles",
};

export type MacPermission = keyof typeof MAC_PERMISSION_SETTINGS_URLS;

export const MAC_PERMISSION_TITLES: Record<MacPermission, string> = {
"screen-recording": "Screen Recording",
accessibility: "Accessibility",
"full-disk-access": "Full Disk Access",
};
Loading
Loading