feat: light mode and customizable shortcuts#3
Conversation
- Follow system appearance by default (light mode users get light Chromeless) - Allow override via `defaults write com.chromeless.app appearance dark|light|system` - Start page HTML uses `@media (prefers-color-scheme: dark)` for dual mode - Window/webview backgrounds use system-adaptive .windowBackgroundColor - All chromeless-specific shortcuts now customizable via UserDefaults plist - Standard Cocoa shortcuts (Cmd-C, Cmd-V, Cmd-Q) remain non-customizable - Shortcut changes take effect on app relaunch - no live rebinding Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Greptile SummaryThis PR adds light-mode support and customizable app shortcuts. The main changes are:
Confidence Score: 4/5This is close, but the shortcut collision cases should be fixed before merging.
main.swift Important Files Changed
Reviews (2): Last reviewed commit: "fix: improve shortcut modifier parsing, ..." | Re-trigger Greptile |
| let (snapKey, snapMods) = resolveShortcut("saveSnapshot", defaultKey: "s", defaultModifiers: [.command, .shift]) | ||
| let snap = fileMenu.addItem(withTitle: "Save Snapshot to Desktop", |
There was a problem hiding this comment.
When saveSnapshot is set to cmd+v, this File-menu item is registered before Edit > Paste with the same ⌘V equivalent. Pressing ⌘V in a page or text field can run saveSnapshot(_:) instead of paste(_:), even though the standard Cocoa shortcut is meant to remain non-customizable.
| let (hrKey, hrMods) = resolveShortcut("hardReload", defaultKey: "r", defaultModifiers: [.command, .shift]) | ||
| let hardReload = viewMenu.addItem(withTitle: "Reload Ignoring Cache", | ||
| action: #selector(BrowserWindowController.hardReloadPage(_:)), keyEquivalent: "r") | ||
| hardReload.keyEquivalentModifierMask = [.command, .shift] | ||
| action: #selector(BrowserWindowController.hardReloadPage(_:)), keyEquivalent: hrKey) | ||
| hardReload.keyEquivalentModifierMask = hrMods |
There was a problem hiding this comment.
When hardReload is configured as cmd+r, this item gets the same ⌘R equivalent as Reload Page, which is added just above it in the same menu. AppKit will match the first enabled item, so the keyboard shortcut performs a normal reload and hard reload becomes unreachable from the keyboard.
…cuts in README - Fix resolveShortcut() compound modifier parsing with while-loop re-scan - Start modifiers as empty set so non-Cmd shortcuts are possible - Guard against empty key after modifier-only input - Add README sections for appearance override and customizable shortcuts Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
| } | ||
| if key.isEmpty { return (defaultKey, defaultModifiers) } | ||
| if modifiers.isEmpty { modifiers = defaultModifiers } | ||
| return (key, modifiers) |
There was a problem hiding this comment.
Reserved shortcuts still bind The parser returns user-provided bindings even when they match standard Cocoa shortcuts. For example,
defaults write com.chromeless.app shortcuts -dict openLocation "cmd+q" resolves to q with the command modifier, and the menu then installs Open Location on the same shortcut as Quit. The same path can take cmd+w, cmd+h, or cmd+m, so core app and window commands can stop working from the keyboard even though those shortcuts are meant to stay reserved.
| let (hrKey, hrMods) = resolveShortcut("hardReload", defaultKey: "r", defaultModifiers: [.command, .shift]) | ||
| let hardReload = viewMenu.addItem(withTitle: "Reload Ignoring Cache", | ||
| action: #selector(BrowserWindowController.hardReloadPage(_:)), keyEquivalent: "r") | ||
| hardReload.keyEquivalentModifierMask = [.command, .shift] | ||
| action: #selector(BrowserWindowController.hardReloadPage(_:)), keyEquivalent: hrKey) | ||
| hardReload.keyEquivalentModifierMask = hrMods |
There was a problem hiding this comment.
Hard reload can collide
reloadPage can still be configured to the hard-reload shortcut. If the user sets reloadPage to shift+cmd+r, this menu item is added before Reload Ignoring Cache with the same key and modifier mask, so the normal reload item handles the shortcut first and hard reload is no longer reachable from the keyboard. The reload pair needs duplicate-binding handling before assigning both menu items.
| let (hrKey, hrMods) = resolveShortcut("hardReload", defaultKey: "r", defaultModifiers: [.command, .shift]) | |
| let hardReload = viewMenu.addItem(withTitle: "Reload Ignoring Cache", | |
| action: #selector(BrowserWindowController.hardReloadPage(_:)), keyEquivalent: "r") | |
| hardReload.keyEquivalentModifierMask = [.command, .shift] | |
| action: #selector(BrowserWindowController.hardReloadPage(_:)), keyEquivalent: hrKey) | |
| hardReload.keyEquivalentModifierMask = hrMods | |
| var (hrKey, hrMods) = resolveShortcut("hardReload", defaultKey: "r", defaultModifiers: [.command, .shift]) | |
| if hrKey == rlKey && hrMods == rlMods { | |
| (hrKey, hrMods) = ("r", [.command, .shift]) | |
| } | |
| let hardReload = viewMenu.addItem(withTitle: "Reload Ignoring Cache", | |
| action: #selector(BrowserWindowController.hardReloadPage(_:)), keyEquivalent: hrKey) | |
| hardReload.keyEquivalentModifierMask = hrMods |
Two features, one commit (+122/−36 lines in main.swift):
Light mode — Chromeless now follows the system appearance by default. Light-mode Mac users get light Chromeless. Users can override with:
The start page HTML uses
@media (prefers-color-scheme: dark)with light colors as the default. Window and webview backgrounds use system-adaptive.windowBackgroundColorinstead of hardcoded near-black.Customizable shortcuts — every chromeless-specific shortcut is now stored in a UserDefaults plist dictionary. Customize with:
Customizable actions: openLocation, saveSnapshot, reloadPage, hardReload, zoomIn, zoomOut, actualSize, goBack, goForward, togglePin, copyURL, showHelp. Standard Cocoa shortcuts (Cmd-C, Cmd-V, Cmd-Q, Cmd-W, Cmd-M, Cmd-H) are not customizable. Changes take effect on app relaunch.
Both features stay within the zero-dependency, single-file philosophy.
Validation
Built with
./build.shon macOS 26 (arm64):defaults write com.chromeless.app appearance dark→ relaunch → dark Chromeless--snap /tmp/test.png --size 800x600→ saved successfullyopen Chromeless.app→ Cmd-L HUD works, Esc returns to start page