-
Notifications
You must be signed in to change notification settings - Fork 2.1k
fix(desktop): improve AppImage icons and remote environment #2538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 11 commits
d5fce53
bfc4a7c
5cce063
8ad0cc9
b133f59
23c7528
58ada8b
afc9110
b7c2e20
5f1fe13
1676dd3
6fd6ad1
46d0228
2489efc
a0a2a94
4e4cc84
27eadcd
6c34906
0f7c19d
749bc07
4d69158
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { assert, describe, it } from "@effect/vitest"; | ||
|
|
||
| import { | ||
| resolveEarlyLinuxElectronOptions, | ||
| resolveEarlyLinuxPasswordStorePreference, | ||
| } from "./DesktopEarlyElectronStartup.ts"; | ||
|
|
||
| describe("DesktopEarlyElectronStartup", () => { | ||
| it("reads the persisted linux password-store preference before Electron is ready", () => { | ||
| const preference = resolveEarlyLinuxPasswordStorePreference({ | ||
| env: { T3CODE_HOME: "/home/user/.t3-test" }, | ||
| homeDirectory: "/home/user", | ||
| readFileString: (path) => { | ||
| assert.equal(path, "/home/user/.t3-test/userdata/desktop-settings.json"); | ||
| return JSON.stringify({ linuxPasswordStore: "kwallet6" }); | ||
| }, | ||
| }); | ||
|
|
||
| assert.equal(preference, "kwallet6"); | ||
| }); | ||
|
|
||
| it("falls back to auto when the early settings document is missing or invalid", () => { | ||
| const preference = resolveEarlyLinuxPasswordStorePreference({ | ||
| env: {}, | ||
| homeDirectory: "/home/user", | ||
| readFileString: () => { | ||
| throw new Error("missing"); | ||
| }, | ||
| }); | ||
|
|
||
| assert.equal(preference, "auto"); | ||
| }); | ||
|
|
||
| it("resolves the early linux Electron switches and DBus fallback", () => { | ||
| const options = resolveEarlyLinuxElectronOptions({ | ||
| env: { | ||
| T3CODE_HOME: "/home/user/.t3-test", | ||
| XDG_CURRENT_DESKTOP: "niri", | ||
| XDG_RUNTIME_DIR: "/run/user/1000", | ||
| VITE_DEV_SERVER_URL: "http://127.0.0.1:5173", | ||
| }, | ||
| exists: (path) => path === "/run/user/1000/bus", | ||
| homeDirectory: "/home/user", | ||
| readFileString: (path) => { | ||
| assert.equal(path, "/home/user/.t3-test/dev/desktop-settings.json"); | ||
| return JSON.stringify({ linuxPasswordStore: "auto" }); | ||
| }, | ||
| uid: 1000, | ||
| }); | ||
|
|
||
| assert.deepEqual(options, { | ||
| dbusSessionBusAddress: "unix:path=/run/user/1000/bus", | ||
| linuxWmClass: "t3code-dev", | ||
| passwordStore: "gnome-libsecret", | ||
| }); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| // @effect-diagnostics nodeBuiltinImport:off | ||
| import * as Fs from "node:fs"; | ||
| import * as NodeOS from "node:os"; | ||
|
|
||
| import * as Electron from "electron"; | ||
|
|
||
| import * as ElectronProtocol from "../electron/ElectronProtocol.ts"; | ||
| import { | ||
| DEFAULT_LINUX_PASSWORD_STORE, | ||
| normalizeLinuxPasswordStorePreference, | ||
| resolveLinuxPasswordStoreSwitch, | ||
| type LinuxPasswordStoreSwitch, | ||
| type LinuxPasswordStorePreference, | ||
| } from "../linuxSecretStorage.ts"; | ||
| import { resolveDefaultLinuxDbusSessionBusAddress } from "../shell/DesktopShellEnvironment.ts"; | ||
| import { resolveDesktopBaseDir, resolveDesktopStateDir } from "./DesktopStatePaths.ts"; | ||
|
|
||
| interface EarlyDesktopSettingsInput { | ||
| readonly env: NodeJS.ProcessEnv; | ||
| readonly homeDirectory: string; | ||
| readonly readFileString: (path: string) => string; | ||
| } | ||
|
|
||
| interface EarlyLinuxElectronOptionsInput extends EarlyDesktopSettingsInput { | ||
| readonly exists: (path: string) => boolean; | ||
| readonly uid: number | undefined; | ||
| } | ||
|
|
||
| export interface EarlyLinuxElectronOptions { | ||
| readonly dbusSessionBusAddress: string | null; | ||
| readonly linuxWmClass: string; | ||
| readonly passwordStore: LinuxPasswordStoreSwitch | null; | ||
| } | ||
|
|
||
| const trimNonEmpty = (value: string | undefined): string | null => { | ||
| const trimmed = value?.trim(); | ||
| return trimmed && trimmed.length > 0 ? trimmed : null; | ||
| }; | ||
|
|
||
| const isDevelopmentEnvironment = (env: NodeJS.ProcessEnv): boolean => | ||
| trimNonEmpty(env.VITE_DEV_SERVER_URL) !== null; | ||
|
|
||
| const joinLinuxPath = (first: string, ...segments: string[]): string => { | ||
| const normalizedFirst = first.replace(/\/+$/u, ""); | ||
| const normalizedSegments = segments.map((segment) => segment.replace(/^\/+|\/+$/gu, "")); | ||
| return [normalizedFirst, ...normalizedSegments].filter((segment) => segment.length > 0).join("/"); | ||
| }; | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| function resolveEarlyDesktopSettingsPath(input: { | ||
| readonly env: NodeJS.ProcessEnv; | ||
| readonly homeDirectory: string; | ||
| }): string { | ||
| const baseDir = resolveDesktopBaseDir({ | ||
| homeDirectory: input.homeDirectory, | ||
| joinPath: joinLinuxPath, | ||
| t3Home: input.env.T3CODE_HOME, | ||
| }); | ||
| const stateDir = resolveDesktopStateDir({ | ||
| baseDir, | ||
| isDevelopment: isDevelopmentEnvironment(input.env), | ||
| joinPath: joinLinuxPath, | ||
| }); | ||
| return joinLinuxPath(stateDir, "desktop-settings.json"); | ||
| } | ||
|
|
||
| export function resolveEarlyLinuxPasswordStorePreference( | ||
| input: EarlyDesktopSettingsInput, | ||
| ): LinuxPasswordStorePreference { | ||
| const settingsPath = resolveEarlyDesktopSettingsPath(input); | ||
| try { | ||
| const parsed = JSON.parse(input.readFileString(settingsPath)) as unknown; | ||
|
cursor[bot] marked this conversation as resolved.
Outdated
|
||
| if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { | ||
| return DEFAULT_LINUX_PASSWORD_STORE; | ||
| } | ||
| return normalizeLinuxPasswordStorePreference( | ||
| (parsed as { readonly linuxPasswordStore?: unknown }).linuxPasswordStore, | ||
| ); | ||
| } catch { | ||
| return DEFAULT_LINUX_PASSWORD_STORE; | ||
| } | ||
| } | ||
|
|
||
| export function resolveEarlyLinuxElectronOptions( | ||
| input: EarlyLinuxElectronOptionsInput, | ||
| ): EarlyLinuxElectronOptions { | ||
| const preference = resolveEarlyLinuxPasswordStorePreference(input); | ||
| return { | ||
| dbusSessionBusAddress: | ||
| trimNonEmpty(input.env.DBUS_SESSION_BUS_ADDRESS) === null | ||
| ? resolveDefaultLinuxDbusSessionBusAddress({ | ||
| env: input.env, | ||
| exists: input.exists, | ||
| uid: input.uid, | ||
| }) | ||
| : null, | ||
| linuxWmClass: isDevelopmentEnvironment(input.env) ? "t3code-dev" : "t3code", | ||
| passwordStore: resolveLinuxPasswordStoreSwitch({ | ||
| preference, | ||
| env: input.env, | ||
| }), | ||
| }; | ||
| } | ||
|
|
||
| export function configureElectronBeforeReady(): void { | ||
| if (process.platform === "linux") { | ||
| const options = resolveEarlyLinuxElectronOptions({ | ||
| env: process.env, | ||
| exists: Fs.existsSync, | ||
| homeDirectory: NodeOS.homedir(), | ||
| readFileString: (path) => Fs.readFileSync(path, "utf8"), | ||
| uid: process.getuid?.(), | ||
| }); | ||
| if (options.dbusSessionBusAddress !== null) { | ||
| process.env.DBUS_SESSION_BUS_ADDRESS = options.dbusSessionBusAddress; | ||
| } | ||
| if (options.passwordStore !== null) { | ||
| Electron.app.commandLine.appendSwitch("password-store", options.passwordStore); | ||
| } | ||
|
|
||
| Electron.app.commandLine.appendSwitch("class", options.linuxWmClass); | ||
| } | ||
|
|
||
| ElectronProtocol.registerDesktopSchemePrivilegesSync(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| export type JoinPath = (first: string, ...segments: string[]) => string; | ||
|
|
||
| export function resolveDesktopBaseDir(input: { | ||
| readonly homeDirectory: string; | ||
| readonly joinPath: JoinPath; | ||
| readonly t3Home: string | null | undefined; | ||
| }): string { | ||
| if (typeof input.t3Home === "string") { | ||
| const trimmed = input.t3Home.trim(); | ||
| if (trimmed.length > 0) { | ||
| return trimmed; | ||
| } | ||
| } | ||
|
|
||
| return input.joinPath(input.homeDirectory, ".t3"); | ||
| } | ||
|
|
||
| export function resolveDesktopStateDir(input: { | ||
| readonly baseDir: string; | ||
| readonly isDevelopment: boolean; | ||
| readonly joinPath: JoinPath; | ||
| }): string { | ||
| return input.joinPath(input.baseDir, input.isDevelopment ? "dev" : "userdata"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,7 +69,10 @@ export function normalizeDesktopProtocolPathname(rawPath: string): Option.Option | |
| return Option.some(segments.join("/")); | ||
| } | ||
|
|
||
| const registerDesktopSchemePrivileges = Effect.sync(() => { | ||
| /** | ||
| * Must run synchronously during process bootstrap, before Electron emits `ready`. | ||
| */ | ||
| export function registerDesktopSchemePrivilegesSync(): void { | ||
| Electron.protocol.registerSchemesAsPrivileged([ | ||
| { | ||
| scheme: DESKTOP_SCHEME, | ||
|
|
@@ -81,7 +84,11 @@ const registerDesktopSchemePrivileges = Effect.sync(() => { | |
| }, | ||
| }, | ||
| ]); | ||
| }).pipe(Effect.withSpan("desktop.electron.protocol.registerSchemePrivileges")); | ||
| } | ||
|
|
||
| export const registerDesktopSchemePrivileges = Effect.sync( | ||
| registerDesktopSchemePrivilegesSync, | ||
| ).pipe(Effect.withSpan("desktop.electron.protocol.registerSchemePrivileges")); | ||
|
|
||
| export const layerSchemePrivileges = Layer.effectDiscard(registerDesktopSchemePrivileges); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the way the layers were setup this ran before electron setup. not sure if you changed some layer structure so that's no longer the case?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, that ordering is still preserved. I kept only the pre- |
||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.