History

Re-run session sync after WASM init and merge Ghostty config files

- 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

Pixel Perfect committed 4 months ago

Commit 36d1cc61 parent c1caaa32 files · +24 −7Browse files

Unified · Split

2 files
  1. apps/server/src/terminal/ghosttyStyle.ts +18 −6
  2. apps/web/src/components/ThreadTerminalDrawer.tsx +6 −1
apps/server/src/terminal/ghosttyStyle.ts+18−6
@@ -207,13 +207,25 @@ export const loadGhosttyTerminalStyle: Effect.Effect< · 206 unmodified lines
207 return undefined;207 return undefined;
208 });208 });
209 209
210 const configSource = yield* readFirstExisting([210 // Ghostty reads the XDG config first and the macOS Application Support
211 path.join(xdgConfigHome, "ghostty", "config"),211 // config after it, so later files override earlier values. It also accepts
212 path.join(home, "Library", "Application Support", "com.mitchellh.ghostty", "config"),212 // config.ghostty as the file name. Merge every location that exists instead
213 ]);213 // of stopping at the first match.
214 if (configSource === undefined) return undefined;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 const loadThemeColors = (themeName: string) =>230 const loadThemeColors = (themeName: string) =>
219 Effect.gen(function* () {231 Effect.gen(function* () {
apps/web/src/components/ThreadTerminalDrawer.tsx+6−1
@@ -434,6 +434,10 @@ export function TerminalViewport({ · 433 unmodified lines
434 reportFailure: false,434 reportFailure: false,
435 });435 });
436 const hasHandledExitRef = useRef(false);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 const selectionPointerRef = useRef<{ x: number; y: number } | null>(null);441 const selectionPointerRef = useRef<{ x: number; y: number } | null>(null);
438 const selectionGestureActiveRef = useRef(false);442 const selectionGestureActiveRef = useRef(false);
439 const selectionActionRequestIdRef = useRef(0);443 const selectionActionRequestIdRef = useRef(0);
@@ -549,6 +553,7 @@ export function TerminalViewport({ · 109 unmodified lines
549 status: "closed",553 status: "closed",
550 version: 0,554 version: 0,
551 };555 };
556 setTerminalEpoch((value) => value + 1);
552 557
553 const clearSelectionAction = () => {558 const clearSelectionAction = () => {
554 selectionActionRequestIdRef.current += 1;559 selectionActionRequestIdRef.current += 1;
@@ -970,7 +975,7 @@ export function TerminalViewport({ · 415 unmodified lines
970 });975 });
971 }976 }
972 previousSessionRef.current = current;977 previousSessionRef.current = current;
973 }, [autoFocus, terminalBuffer, terminalError, terminalStatus, terminalVersion]);978 }, [autoFocus, terminalBuffer, terminalEpoch, terminalError, terminalStatus, terminalVersion]);
974 979
975 useEffect(() => {980 useEffect(() => {
976 if (!autoFocus) return;981 if (!autoFocus) return;