// hypermodern // emacs // cross-cutting fixes, basically usable#1
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements cross-cutting fixes to make the hypermodern Emacs configuration "basically usable" with focus on file navigation, magit integration, and theme loading. The changes include major refactoring of navigation functionality, theme name simplification, runtime dependency management, and better integration of window management packages.
Key changes:
- Simplified theme naming (removed "-blue-" from slugs:
ono-sendai-blue-tuned→ono-sendai-tuned) - Enhanced navigation system with unified entry point and improved zoxide integration
- Added sqlite runtime dependency for forge/org-roam support
- Improved Nix wrapper to preserve PATH for tools and wrap emacsclient with dependencies
Reviewed changes
Copilot reviewed 18 out of 22 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| themes/ono-sendai.nix | Simplified theme slug names, removing "-blue-" prefix |
| themes/maas.nix | Whitespace/formatting cleanup for consistency |
| themes/base16-ono-sendai.el | Indentation alignment fixes |
| nix/lib/mk-theme-loader.nix | New theme autoloads loader for discoverable themes |
| nix/lib/mk-base16-theme.nix | Added autoloads file generation for theme registration |
| nix/packages.nix | Default package now includes runtime dependencies |
| nix/per-system/wrap-with-config.nix | PATH preservation and emacsclient wrapping with deps |
| nix/lib/runtime-deps.nix | Added sqlite for database-backed packages |
| nix/lib/core-packages.nix | Documentation comment for forge dependencies |
| nix/lib.nix | Integrated theme loader and refactored theme packaging |
| nix/apps.nix | Simplified to reference config.packages for consistency |
| lib/hypermodern-windows.el | Added forward declarations and :ensure t directives |
| lib/hypermodern-ui.el | Updated default theme and fixed indentation |
| lib/hypermodern-remote.el | Added forward declarations and improved error handling |
| lib/hypermodern-navigation.el | Major refactoring with unified navigation command |
| lib/hypermodern-compile.el | Enhanced error handling and improved Python detection |
| init.el | Integrated new modules and updated theme references |
| test/run-tests.el | Formatting cleanup |
| test/hypermodern-wiring-test.el | Removed redundant eglot test |
| flake.nix | Formatting cleanup |
| flake.lock | Updated nixpkgs to newer revision |
| .claude/settings.local.json | Removed file |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| (defun hypermodern/find-file () | ||
| "Smart find-file: project root if in project, else current dir. | ||
| With prefix arg, use consult-dir first to pick directory." | ||
| "Smartly find a file: project root if in project, else current dir. | ||
| With prefix argument, use consult-dir first to pick directory." | ||
| (interactive) | ||
| (if current-prefix-arg | ||
| ;; C-u: pick directory first via consult-dir | ||
| (let ((consult-dir-shadow-filenames nil)) | ||
| (consult-dir) | ||
| (call-interactively #'find-file)) | ||
| ;; Normal: project-aware find | ||
| (if-let ((project (project-current))) | ||
| (project-find-file) | ||
| (call-interactively #'find-file)))) | ||
|
|
||
| ;; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| ;; // dired integration | ||
| ;; ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
| (call-interactively #'find-file)) |
There was a problem hiding this comment.
The hypermodern/find-file function's docstring claims it's "smart" and mentions project-aware behavior and prefix argument support, but the implementation just calls find-file directly without any of that logic. This is misleading and the function doesn't deliver on its documented functionality.
| (hypermodern-themes '(;; ono-sendai (dark) | ||
| base16-ono-sendai-blue-tuned | ||
| base16-ono-sendai-blue-untuned | ||
| base16-ono-sendai-chiba | ||
| base16-ono-sendai-memphis | ||
| base16-ono-sendai-razorgirl | ||
| base16-ono-sendai-sprawl | ||
| base16-ono-sendai-github | ||
| base16-ono-sendai-blue-spectrum | ||
| ;; maas (light) | ||
| base16-maas-neoform | ||
| base16-maas-bioptic | ||
| base16-maas-ghost | ||
| base16-maas-tessier)) |
There was a problem hiding this comment.
The hardcoded list of theme symbols includes old theme names that were renamed in this PR (e.g., base16-ono-sendai-blue-tuned, base16-ono-sendai-blue-untuned, base16-ono-sendai-blue-spectrum) instead of the new names (base16-ono-sendai-tuned, base16-ono-sendai-untuned, base16-ono-sendai-spectrum). This inconsistency will prevent these themes from being properly recognized.
| (:name "Minimal / Tuned" :theme base16-ono-sendai-blue-tuned :density tight :signal minimal :font auto :alpha 100 | ||
| :glow off :pulse nil :halo off) | ||
|
|
||
| ;; daily driver glow-up (still minimal, just intentional) | ||
| (:name "Minimal / Tuned / Glow" :theme base16-ono-sendai-blue-tuned :density tight :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| ;; daily driver glow-up (still minimal, just intentional) | ||
| (:name "Minimal / Tuned / Glow" :theme base16-ono-sendai-blue-tuned :density tight :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| ;; neon control room (still disciplined: no confetti) | ||
| (:name "Minimal / Tuned / Neon" :theme base16-ono-sendai-blue-tuned :density tight :signal loud :font auto :alpha 97 | ||
| :trans t :glow neon :pulse t :halo auto :dim t) | ||
| ;; neon control room (still disciplined: no confetti) | ||
| (:name "Minimal / Tuned / Neon" :theme base16-ono-sendai-blue-tuned :density tight :signal loud :font auto :alpha 97 | ||
| :trans t :glow neon :pulse t :halo auto :dim t) | ||
|
|
||
| ;; ono-sendai family (expects you to package those themes via Nix or otherwise) | ||
| (:name "Ono-Sendai / GitHub (robust)" :theme base16-ono-sendai-github :density tight :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| ;; ono-sendai family (expects you to package those themes via Nix or otherwise) | ||
| (:name "Ono-Sendai / GitHub (robust)" :theme base16-ono-sendai-github :density tight :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| (:name "Ono-Sendai / Tuned (daily driver)" :theme base16-ono-sendai-blue-tuned :density tight :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| (:name "Ono-Sendai / Tuned (daily driver)" :theme base16-ono-sendai-blue-tuned :density tight :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| (:name "Ono-Sendai / Sprawl" :theme base16-ono-sendai-sprawl :density normal :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| (:name "Ono-Sendai / Sprawl" :theme base16-ono-sendai-sprawl :density normal :signal minimal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| (:name "Ono-Sendai / Razor Girl" :theme base16-ono-sendai-razorgirl :density tight :signal normal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| (:name "Ono-Sendai / Razor Girl" :theme base16-ono-sendai-razorgirl :density tight :signal normal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| (:name "Ono-Sendai / Memphis (OLED black)" :theme base16-ono-sendai-memphis :density tight :signal minimal :font auto :alpha 98 | ||
| :trans t :glow subtle :pulse t :halo auto) | ||
| (:name "Ono-Sendai / Memphis (OLED black)" :theme base16-ono-sendai-memphis :density tight :signal minimal :font auto :alpha 98 | ||
| :trans t :glow subtle :pulse t :halo auto) | ||
|
|
||
| (:name "Ono-Sendai / Spectrum (hue discipline)" :theme base16-ono-sendai-spectrum :density normal :signal normal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| (:name "Ono-Sendai / Spectrum (hue discipline)" :theme base16-ono-sendai-spectrum :density normal :signal normal :font auto :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| ;; typography-first variations | ||
| (:name "Iosevka / Tight / Glow" :theme base16-ono-sendai-blue-tuned :density tight :signal minimal :font iosevka :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| ;; typography-first variations | ||
| (:name "Iosevka / Tight / Glow" :theme base16-ono-sendai-blue-tuned :density tight :signal minimal :font iosevka :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| (:name "JetBrains / Normal / Glow" :theme base16-ono-sendai-blue-tuned :density normal :signal normal :font jetbrains :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| (:name "JetBrains / Normal / Glow" :theme base16-ono-sendai-blue-tuned :density normal :signal normal :font jetbrains :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| (:name "Berkeley / Comfy / Glow" :theme base16-ono-sendai-blue-tuned :density comfy :signal minimal :font berkeley :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
| (:name "Berkeley / Comfy / Glow" :theme base16-ono-sendai-blue-tuned :density comfy :signal minimal :font berkeley :alpha 100 | ||
| :glow subtle :pulse t :halo auto) | ||
|
|
||
| ;; writing + reading | ||
| (:name "Writing / Comfy / Glow" :theme base16-ono-sendai-blue-tuned :density comfy :signal minimal :font auto :alpha 100 | ||
| :writing t :glow subtle :pulse nil :halo auto) | ||
| ;; writing + reading | ||
| (:name "Writing / Comfy / Glow" :theme base16-ono-sendai-blue-tuned :density comfy :signal minimal :font auto :alpha 100 | ||
| :writing t :glow subtle :pulse nil :halo auto) | ||
|
|
||
| (:name "Cinema / Reading Mode" :theme base16-ono-sendai-blue-tuned :density cinema :signal minimal :font auto :alpha 100 | ||
| :writing t :glow subtle :pulse nil :halo auto) | ||
| (:name "Cinema / Reading Mode" :theme base16-ono-sendai-blue-tuned :density cinema :signal minimal :font auto :alpha 100 | ||
| :writing t :glow subtle :pulse nil :halo auto) |
There was a problem hiding this comment.
Multiple style presets reference the old theme name base16-ono-sendai-blue-tuned instead of the renamed base16-ono-sendai-tuned. These should be updated to match the theme slug changes made elsewhere in this PR.
| (goto-char (point-min)) | ||
| (cond | ||
| ((search-forward "[tool.poetry]" nil t) "poetry build") | ||
| ((search-forward "[tool.hatch]" nil t) "hatch build") | ||
| ((search-forward "[build-system]" nil t) "python -m build") | ||
| ((progn (goto-char (point-min)) | ||
| (search-forward "[tool.hatch]" nil t)) | ||
| "hatch build") | ||
| ((progn (goto-char (point-min)) | ||
| (search-forward "[build-system]" nil t)) | ||
| "python -m build") | ||
| (t "pip install -e ."))) |
There was a problem hiding this comment.
The Python command detection searches for sections in pyproject.toml multiple times by resetting point to (point-min). However, the first search for "[tool.poetry]" doesn't reset point before subsequent searches. If "[tool.poetry]" is found, point will be left after that string, and the subsequent searches won't find earlier sections. The first search should also use (goto-char (point-min)) or all searches should start from the beginning.
| (global-set-key (kbd "C-x C-f") #'hypermodern/find-file) ; Smart find-file | ||
| (global-set-key (kbd "C-x F") #'hypermodern/fd-project) ; fd from project root | ||
| (global-set-key (kbd "C-x f") #'hypermodern/fd-here) ; fd from current dir | ||
| (global-set-key (kbd "C-x r") #'consult-recent-file) ; Recent files |
There was a problem hiding this comment.
The keybinding C-x r is bound to consult-recent-file, but C-x r is a prefix key in Emacs used for register and rectangle commands (e.g., C-x r s for saving to register, C-x r j for jumping to register). Overriding this prefix will break all register-related keybindings. Consider using a different keybinding like C-x C-r instead.
| (global-set-key (kbd "C-x r") #'consult-recent-file) ; Recent files | |
| (global-set-key (kbd "C-x C-r") #'consult-recent-file) ; Recent files |
| ("Find in Project" . hypermodern/fd-project)))) | ||
| (let* ((selection (consult--read choices :prompt "Choose navigation method: "))) | ||
| (when selection | ||
| (funcall (cdr selection)))))) |
There was a problem hiding this comment.
The hypermodern/navigate function uses consult--read with a list of choices but doesn't properly handle the return value. consult--read will return the selected string (e.g., "Find File"), not the cons cell, so (cdr selection) will fail. The function needs to use assoc to look up the selection in the choices list to get the corresponding function.
| (funcall (cdr selection)))))) | |
| (let ((choice (assoc selection choices))) | |
| (when choice | |
| (funcall (cdr choice)))))))) |
| let | ||
| # Preserve PATH from wrapped emacs if it has deps | ||
| pathPrefix = if emacsPkg ? deps then | ||
| "--prefix PATH : ${pkgs.lib.makeBinPath emacsPkg.deps}" | ||
| else ""; | ||
| in | ||
| pkgs.runCommand "hypermodern-emacs-configured" { | ||
| nativeBuildInputs = [ pkgs.makeWrapper ]; | ||
| meta.mainProgram = "emacs"; | ||
| } '' | ||
| mkdir -p $out/bin | ||
| makeWrapper ${emacsPkg}/bin/emacs $out/bin/emacs \ | ||
| ${pathPrefix} \ |
There was a problem hiding this comment.
The pathPrefix variable on line 16 is interpolated into the bash script, but when emacsPkg doesn't have deps, it will be an empty string. This results in a trailing backslash on line 16 with nothing after it, which can cause issues. While bash may handle this, it's cleaner to conditionally include the entire --prefix argument rather than interpolating an empty string.
| let | |
| # Preserve PATH from wrapped emacs if it has deps | |
| pathPrefix = if emacsPkg ? deps then | |
| "--prefix PATH : ${pkgs.lib.makeBinPath emacsPkg.deps}" | |
| else ""; | |
| in | |
| pkgs.runCommand "hypermodern-emacs-configured" { | |
| nativeBuildInputs = [ pkgs.makeWrapper ]; | |
| meta.mainProgram = "emacs"; | |
| } '' | |
| mkdir -p $out/bin | |
| makeWrapper ${emacsPkg}/bin/emacs $out/bin/emacs \ | |
| ${pathPrefix} \ | |
| pkgs.runCommand "hypermodern-emacs-configured" { | |
| nativeBuildInputs = [ pkgs.makeWrapper ]; | |
| meta.mainProgram = "emacs"; | |
| } '' | |
| mkdir -p $out/bin | |
| makeWrapper ${emacsPkg}/bin/emacs $out/bin/emacs \ | |
| ${if emacsPkg ? deps then '' | |
| --prefix PATH : ${pkgs.lib.makeBinPath emacsPkg.deps} \ | |
| '' else ''}\ |
| :after magit) | ||
| :after magit | ||
| :config | ||
| ;; Force forge to use external sqlite3 binary |
There was a problem hiding this comment.
The comment states "Force forge to use external sqlite3 binary", but the code sets forge-database-connector to 'sqlite-builtin, which actually uses Emacs' built-in SQLite support, not an external binary. The comment is misleading. If the intent is to use the built-in support, the comment should say "Use Emacs built-in SQLite support" or similar.
| ;; Force forge to use external sqlite3 binary | |
| ;; Use Emacs built-in SQLite support for Forge |
Summary
This has the first dozen or so deal-breakers fixed, it's still rough but I'm driving it around now.
Highlights
magitworks, still sometimes glitches on first usetmuxTest Plan
n.b. not passing yet, don't merge...