100 lines · 3.4 KB · TypeScript
RawDownload
import {  REMOTE_CAPABLE_EDITOR_IDS,  remoteSchemeForEditor,  type SystemSettingsPane,} from "@t3tools/contracts";import * as Context from "effect/Context";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";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.const SAFE_WEB_PROTOCOLS = new Set(["http:", "https:"]);const REMOTE_EDITOR_PROTOCOLS = new Set(  REMOTE_CAPABLE_EDITOR_IDS.flatMap((id) => {    const scheme = remoteSchemeForEditor(id);    return scheme === undefined ? [] : [`${scheme}:`];  }),); // Zed's host sits in the first path segment, so it needs its own userinfo ban.const ZED_SSH_PATHNAME = /^\/[^/@:]+\/.*$/; const isRemoteEditorUrl = (url: URL) =>  REMOTE_EDITOR_PROTOCOLS.has(url.protocol) &&  url.username.length === 0 &&  url.password.length === 0 &&  (url.protocol === "zed:"    ? url.host === "ssh" && ZED_SSH_PATHNAME.test(url.pathname)    : url.host === "vscode-remote" &&      url.pathname.startsWith("/ssh-remote+") &&      url.pathname.length > "/ssh-remote+".length); export function parseSafeExternalUrl(rawUrl: unknown): Option.Option<string> {  if (typeof rawUrl !== "string") {    return Option.none();  }   try {    const url = new URL(rawUrl);    return SAFE_WEB_PROTOCOLS.has(url.protocol) || isRemoteEditorUrl(url)      ? Option.some(url.href)      : Option.none();  } catch {    return Option.none();  }} export class ElectronShell extends Context.Service<  ElectronShell,  {    readonly openExternal: (rawUrl: unknown) => Effect.Effect<boolean>;    /** Opens a known System Settings pane by identifier, not by URL. */    readonly openSystemSettings: (pane: SystemSettingsPane) => Effect.Effect<boolean>;    readonly copyText: (text: string) => Effect.Effect<void>;  }>()("@t3tools/desktop/electron/ElectronShell") {} /** @public Service construction is part of the canonical Effect module API. */export const make = ElectronShell.of({  openExternal: (rawUrl) =>    Option.match(parseSafeExternalUrl(rawUrl), {      onNone: () => Effect.succeed(false),      onSome: (externalUrl) =>        Effect.promise(() =>          Electron.shell.openExternal(externalUrl).then(            () => true,            () => false,          ),        ),    }),  openSystemSettings: (pane) =>    Effect.promise(() =>      Electron.shell.openExternal(SYSTEM_SETTINGS_URLS[pane]).then(        () => true,        () => false,      ),    ),  copyText: (text) =>    Effect.promise(() => Electron.clipboard.writeText(text).catch(() => undefined)),}); export const layer = Layer.succeed(ElectronShell, make);