| ... | @@ -50,6 +50,8 @@ const ROOT_DIR = Path.resolve(__dirname, "../../.."); | ... | @@ -50,6 +50,8 @@ const ROOT_DIR = Path.resolve(__dirname, "../../.."); |
| 50 | const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL); | 50 | const isDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL); |
| 51 | const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; | 51 | const APP_DISPLAY_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; |
| 52 | const APP_USER_MODEL_ID = "com.t3tools.t3code"; | 52 | const APP_USER_MODEL_ID = "com.t3tools.t3code"; |
| | 53 | const USER_DATA_DIR_NAME = isDevelopment ? "t3code-dev" : "t3code"; |
| | 54 | const LEGACY_USER_DATA_DIR_NAME = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; |
| 53 | const COMMIT_HASH_PATTERN = /^[0-9a-f]{7,40}$/i; | 55 | const COMMIT_HASH_PATTERN = /^[0-9a-f]{7,40}$/i; |
| 54 | const COMMIT_HASH_DISPLAY_LENGTH = 12; | 56 | const COMMIT_HASH_DISPLAY_LENGTH = 12; |
| 55 | const LOG_DIR = Path.join(STATE_DIR, "logs"); | 57 | const LOG_DIR = Path.join(STATE_DIR, "logs"); |
| ... | @@ -593,6 +595,34 @@ function resolveIconPath(ext: "ico" | "icns" | "png"): string | null { | ... | @@ -593,6 +595,34 @@ function resolveIconPath(ext: "ico" | "icns" | "png"): string | null { |
| 593 | return resolveResourcePath(`icon.${ext}`); | 595 | return resolveResourcePath(`icon.${ext}`); |
| 594 | } | 596 | } |
| 595 | | 597 | |
| | 598 | /** |
| | 599 | * Resolve the Electron userData directory path. |
| | 600 | * |
| | 601 | * Electron derives the default userData path from `productName` in |
| | 602 | * package.json, which currently produces directories with spaces and |
| | 603 | * parentheses (e.g. `~/.config/T3 Code (Alpha)` on Linux). This is |
| | 604 | * unfriendly for shell usage and violates Linux naming conventions. |
| | 605 | * |
| | 606 | * We override it to a clean lowercase name (`t3code`). If the legacy |
| | 607 | * directory already exists we keep using it so existing users don't |
| | 608 | * lose their Chromium profile data (localStorage, cookies, sessions). |
| | 609 | */ |
| | 610 | function resolveUserDataPath(): string { |
| | 611 | const appDataBase = |
| | 612 | process.platform === "win32" |
| | 613 | ? process.env.APPDATA || Path.join(OS.homedir(), "AppData", "Roaming") |
| | 614 | : process.platform === "darwin" |
| | 615 | ? Path.join(OS.homedir(), "Library", "Application Support") |
| | 616 | : process.env.XDG_CONFIG_HOME || Path.join(OS.homedir(), ".config"); |
| | 617 | |
| | 618 | const legacyPath = Path.join(appDataBase, LEGACY_USER_DATA_DIR_NAME); |
| | 619 | if (FS.existsSync(legacyPath)) { |
| | 620 | return legacyPath; |
| | 621 | } |
| | 622 | |
| | 623 | return Path.join(appDataBase, USER_DATA_DIR_NAME); |
| | 624 | } |
| | 625 | |
| 596 | function configureAppIdentity(): void { | 626 | function configureAppIdentity(): void { |
| 597 | app.setName(APP_DISPLAY_NAME); | 627 | app.setName(APP_DISPLAY_NAME); |
| 598 | const commitHash = resolveAboutCommitHash(); | 628 | const commitHash = resolveAboutCommitHash(); |
| ... | @@ -1187,6 +1217,11 @@ function createWindow(): BrowserWindow { | ... | @@ -1187,6 +1217,11 @@ function createWindow(): BrowserWindow { |
| 1187 | return window; | 1217 | return window; |
| 1188 | } | 1218 | } |
| 1189 | | 1219 | |
| | 1220 | // Override Electron's userData path before the `ready` event so that |
| | 1221 | // Chromium session data uses a filesystem-friendly directory name. |
| | 1222 | // Must be called synchronously at the top level — before `app.whenReady()`. |
| | 1223 | app.setPath("userData", resolveUserDataPath()); |
| | 1224 | |
| 1190 | configureAppIdentity(); | 1225 | configureAppIdentity(); |
| 1191 | | 1226 | |
| 1192 | async function bootstrap(): Promise<void> { | 1227 | async function bootstrap(): Promise<void> { |