Repository navigation
Conversation
|
@lcamargof is attempting to deploy a commit to the Manaflow Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review infoConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds terminal theme selection support: enumerates available CMUX theme directories, exposes them to Settings (picker + AppStorage), and applies a CMUX theme override by writing a temporary theme file and loading it via the Ghostty C config loader during startup and when the selected theme changes. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Settings as Settings UI
participant App as GhosttyApp
participant Config as GhosttyConfig
participant CAPI as Ghostty C API
User->>Settings: select terminal theme
Settings->>Settings: update `terminalTheme` (AppStorage)
Settings->>Config: invalidate load cache
Settings->>App: request config reload (source: settings.terminalTheme)
App->>Config: call availableThemeNames()
Config-->>App: return theme list
App->>App: loadCmuxThemeOverrideIfNeeded(selectedTheme)
App->>App: write temp CMUX theme file (/tmp)
App->>CAPI: ghostty_config_load_file(temp_path)
CAPI-->>App: config loaded
App->>App: delete temp file
App-->>User: theme applied
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
Sources/GhosttyConfig.swift (1)
412-449: Consider filtering out directories from theme enumeration.
FileManager.contentsOfDirectory(atPath:)returns both files and subdirectories. If a user has a subdirectory in their themes folder (e.g., for organization or backups), it will appear as a selectable theme option but fail to load.🔧 Optional fix to filter directories
for dir in themeDirs { guard let entries = try? fm.contentsOfDirectory(atPath: dir) else { continue } for entry in entries { guard !entry.hasPrefix("."), !seen.contains(entry) else { continue } + let fullPath = (dir as NSString).appendingPathComponent(entry) + var isDirectory: ObjCBool = false + guard fm.fileExists(atPath: fullPath, isDirectory: &isDirectory), !isDirectory.boolValue else { continue } seen.insert(entry) names.append(entry) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/GhosttyConfig.swift` around lines 412 - 449, In availableThemeNames, when iterating entries from FileManager.contentsOfDirectory(atPath:), filter out non-directory entries so only directories are treated as themes; for each entry build the full path (e.g., dir + "/" + entry) and use FileManager.fileExists(atPath:isDirectory:) or attributesOfItem to check isDirectory, skipping entries that are not directories or are symbolic links to non-directories; keep the existing deduplication with seen and sorting logic intact (change only the loop that processes entries).Sources/GhosttyTerminalView.swift (1)
623-634: Consider improving error visibility and theme name validation.Two observations:
Silent error swallowing: The empty
catch {}makes it difficult to diagnose failures when the theme override doesn't apply. Consider at minimum logging the error in DEBUG builds.Theme name interpolation: The theme name is directly interpolated into the config content. If a malicious or malformed value is stored in UserDefaults (e.g., containing newlines), it could inject additional config directives.
♻️ Suggested improvements
private func loadCmuxThemeOverrideIfNeeded(_ config: ghostty_config_t) { guard let themeName = TerminalThemeSettings.effectiveThemeName() else { return } + // Validate theme name doesn't contain characters that could break config format + guard !themeName.contains(where: { $0.isNewline || $0 == "\"" || $0 == "\\" }) else { + `#if` DEBUG + Self.initLog("loadCmuxThemeOverrideIfNeeded: invalid theme name characters") + `#endif` + return + } let tmpPath = "/tmp/cmux-theme-override-\(UUID().uuidString).conf" let content = "theme = \(themeName)\n" do { try content.write(toFile: tmpPath, atomically: true, encoding: .utf8) tmpPath.withCString { path in ghostty_config_load_file(config, path) } try? FileManager.default.removeItem(atPath: tmpPath) - } catch {} + } catch { + `#if` DEBUG + Self.initLog("loadCmuxThemeOverrideIfNeeded: failed to write temp config: \(error)") + `#endif` + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/GhosttyTerminalView.swift` around lines 623 - 634, The loadCmuxThemeOverrideIfNeeded function silently swallows errors and injects themeName without validation; update it to validate/sanitize TerminalThemeSettings.effectiveThemeName() (e.g., reject or escape newlines and other control characters) before composing content, and replace the empty catch with a debug-only log that records the caught error and tmpPath (use `#if` DEBUG and a logger/NSLog) while still attempting to remove the tmp file via FileManager.default.removeItem(atPath:). Also ensure ghostty_config_load_file is only called with the sanitized tmpPath/content.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@Sources/GhosttyConfig.swift`:
- Around line 412-449: In availableThemeNames, when iterating entries from
FileManager.contentsOfDirectory(atPath:), filter out non-directory entries so
only directories are treated as themes; for each entry build the full path
(e.g., dir + "/" + entry) and use FileManager.fileExists(atPath:isDirectory:) or
attributesOfItem to check isDirectory, skipping entries that are not directories
or are symbolic links to non-directories; keep the existing deduplication with
seen and sorting logic intact (change only the loop that processes entries).
In `@Sources/GhosttyTerminalView.swift`:
- Around line 623-634: The loadCmuxThemeOverrideIfNeeded function silently
swallows errors and injects themeName without validation; update it to
validate/sanitize TerminalThemeSettings.effectiveThemeName() (e.g., reject or
escape newlines and other control characters) before composing content, and
replace the empty catch with a debug-only log that records the caught error and
tmpPath (use `#if` DEBUG and a logger/NSLog) while still attempting to remove the
tmp file via FileManager.default.removeItem(atPath:). Also ensure
ghostty_config_load_file is only called with the sanitized tmpPath/content.
Greptile SummaryAdds a "Terminal Theme" picker to Settings that overrides the user's Ghostty config theme setting.
Confidence Score: 5/5
Important Files Changed
Last reviewed commit: 38c5c1d |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/cmuxApp.swift`:
- Around line 2661-2665: The effectiveThemeName(defaults: UserDefaults =
.standard) function currently returns any non-empty string stored under
themeKey; change it to validate and sanitize the persisted value before treating
it as an override: read the string for themeKey, trim whitespace and control
characters, then verify it matches the set of allowed theme identifiers (or a
safe pattern such as /^[A-Za-z0-9-_]+$/) and only return it if valid; otherwise
return nil so stale/invalid/unsafe values are ignored. Ensure the validation
happens inside effectiveThemeName and uses themeKey to locate the stored value.
In `@Sources/GhosttyConfig.swift`:
- Around line 417-457: availableThemeNames currently omits XDG_DATA_DIRS so
themes present in those locations never show up; update availableThemeNames to
mirror themeSearchPaths by reading the XDG_DATA_DIRS environment variable,
splitting on ":" and for each non-empty entry appending the "ghostty/themes"
component (and expanding tildes), ensuring each resulting path is appended via
appendDir to preserve deduplication/ordering; reference the availableThemeNames
function and the existing themeSearchPaths behavior when implementing this
parity change.
- Around line 107-110: Guard the unconditional override by verifying the
override actually exists before assigning to config.theme: retrieve the optional
via TerminalThemeSettings.effectiveThemeName(), then only set config.theme =
override if that theme is present in your theme registry (e.g.
ThemeManager.shared.themeExists(named:), ThemeStore.hasTheme(_:), or similar
lookup in your theme list); otherwise leave config.theme untouched (or
explicitly fall back to a default) so a missing previously-selected theme
doesn't overwrite Ghostty's configured theme.
In `@Sources/GhosttyTerminalView.swift`:
- Around line 633-644: In loadCmuxThemeOverrideIfNeeded, validate/sanitize the
persisted theme string from TerminalThemeSettings.effectiveThemeName() before
constructing the override file: reject or normalize values containing newlines,
null bytes, or path/control characters (e.g., if themeName.contains("\n") ||
themeName.contains("\0") ...) and skip creating tmpPath if invalid; if valid,
escape or strictly whitelist allowed characters when building content = "theme =
\(themeName)\n"; also stop swallowing errors—surface or log failures from
writing, ghostty_config_load_file, or file removal (so callers can detect
issues) rather than leaving the catch empty.
Summary
~/.config/ghostty/themes)themesetting; "Default" falls back to user's ghostty configHow it works
TerminalThemeSettingsstores a single theme name in UserDefaults. On config load, if a theme is set, it writes a temptheme = <name>config file and loads it viaghostty_config_load_file— overriding whatever the user's ghostty config specifies.Test plan
Test
Summary by CodeRabbit