- Bump a terminal epoch when the deferred WASM setup finishes so the session sync effect re-runs against the live terminal even when no session field changed during the load, completing the exited-at-mount handling - Read config.ghostty as an alternate config file name and merge the XDG and macOS Application Support configs in Ghostty's load order instead of stopping at the first existing file
Unified · Split
| @@ -207,13 +207,25 @@ export const loadGhosttyTerminalStyle: Effect.Effect< · 206 unmodified lines | ||
| 207 | 207 | return undefined; |
| 208 | 208 | }); |
| 209 | 209 | |
| 210 | − const configSource = yield* readFirstExisting([ | |
| 211 | − path.join(xdgConfigHome, "ghostty", "config"), | |
| 212 | − path.join(home, "Library", "Application Support", "com.mitchellh.ghostty", "config"), | |
| 213 | − ]); | |
| 214 | − if (configSource === undefined) return undefined; | |
| 210 | + // Ghostty reads the XDG config first and the macOS Application Support | |
| 211 | + // config after it, so later files override earlier values. It also accepts | |
| 212 | + // config.ghostty as the file name. Merge every location that exists instead | |
| 213 | + // of stopping at the first match. | |
| 214 | + const configDirs = [ | |
| 215 | + path.join(xdgConfigHome, "ghostty"), | |
| 216 | + path.join(home, "Library", "Application Support", "com.mitchellh.ghostty"), | |
| 217 | + ]; | |
| 218 | + const configSources: Array<string> = []; | |
| 219 | + for (const dir of configDirs) { | |
| 220 | + const source = yield* readFirstExisting([ | |
| 221 | + path.join(dir, "config"), | |
| 222 | + path.join(dir, "config.ghostty"), | |
| 223 | + ]); | |
| 224 | + if (source !== undefined) configSources.push(source); | |
| 225 | + } | |
| 226 | + if (configSources.length === 0) return undefined; | |
| 215 | 227 | |
| 216 | − const config = parseGhosttyConfig(configSource); | |
| 228 | + const config = parseGhosttyConfig(configSources.join("\n")); | |
| 217 | 229 | |
| 218 | 230 | const loadThemeColors = (themeName: string) => |
| 219 | 231 | Effect.gen(function* () { |
| @@ -434,6 +434,10 @@ export function TerminalViewport({ · 433 unmodified lines | ||
| 434 | 434 | reportFailure: false, |
| 435 | 435 | }); |
| 436 | 436 | const hasHandledExitRef = useRef(false); |
| 437 | + // Bumped when the async WASM gate finishes creating a terminal, so the | |
| 438 | + // session sync effect re-runs against the live terminal even when no | |
| 439 | + // session field changed while the WASM module was loading. | |
| 440 | + const [terminalEpoch, setTerminalEpoch] = useState(0); | |
| 437 | 441 | const selectionPointerRef = useRef<{ x: number; y: number } | null>(null); |
| 438 | 442 | const selectionGestureActiveRef = useRef(false); |
| 439 | 443 | const selectionActionRequestIdRef = useRef(0); |
| @@ -549,6 +553,7 @@ export function TerminalViewport({ · 109 unmodified lines | ||
| 549 | 553 | status: "closed", |
| 550 | 554 | version: 0, |
| 551 | 555 | }; |
| 556 | + setTerminalEpoch((value) => value + 1); | |
| 552 | 557 | |
| 553 | 558 | const clearSelectionAction = () => { |
| 554 | 559 | selectionActionRequestIdRef.current += 1; |
| @@ -970,7 +975,7 @@ export function TerminalViewport({ · 415 unmodified lines | ||
| 970 | 975 | }); |
| 971 | 976 | } |
| 972 | 977 | previousSessionRef.current = current; |
| 973 | − }, [autoFocus, terminalBuffer, terminalError, terminalStatus, terminalVersion]); | |
| 978 | + }, [autoFocus, terminalBuffer, terminalEpoch, terminalError, terminalStatus, terminalVersion]); | |
| 974 | 979 | |
| 975 | 980 | useEffect(() => { |
| 976 | 981 | if (!autoFocus) return; |