diff options
| author | user <user@clank> | 2026-08-12 19:35:04 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-12 19:35:04 +0200 |
| commit | cd2e81d50569271f69d7f3c99aaf8a0295087f33 (patch) | |
| tree | d7e286625611ebaf298bd2e2327d37f66a751d19 | |
| parent | installers: sync scripts/ CLI helpers into ~/bin (diff) | |
| download | sl0ppi-cd2e81d50569271f69d7f3c99aaf8a0295087f33.tar.gz sl0ppi-cd2e81d50569271f69d7f3c99aaf8a0295087f33.tar.xz sl0ppi-cd2e81d50569271f69d7f3c99aaf8a0295087f33.zip | |
config: snapshot the live environment as source of truth
The most opinionated part of sl0ppi was never in any repo: it lived in
~/.config/zellij as dotfiles, versioned by ~20 hand-made `config.kdl.pre-*`
backups. Imported verbatim so the packaging work has something to install.
Deliberately not imported:
- config.kdl.pre-* / layouts/*.pre-* / themes/*.pre-* backups (that is what
git is for now)
- plugins/pane-finder.wasm (a build output of sl0ppij/sl0p/zj-pane-finder)
- plugins/zjstatus.wasm (a third-party blob no longer referenced by
config.kdl -- dropped as dead weight)
The snapshot is faithful, so it still hardcodes development-machine paths;
config/README.md lists what the rename work has to parameterise.
| -rw-r--r-- | config/README.md | 28 | ||||
| -rw-r--r-- | config/answers.example.md | 14 | ||||
| -rw-r--r-- | config/config.kdl | 380 | ||||
| -rw-r--r-- | config/layouts/default.kdl | 137 | ||||
| -rw-r--r-- | config/layouts/default.swap.kdl | 131 | ||||
| -rw-r--r-- | config/themes/gruber-darker-pop.kdl | 159 | ||||
| -rw-r--r-- | config/themes/templeos.kdl | 133 |
7 files changed, 982 insertions, 0 deletions
diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..350384b --- /dev/null +++ b/config/README.md @@ -0,0 +1,28 @@ +# config/ — the opinionated default environment + +These files are the **source of truth** for sl0ppij's shipped configuration. +Until this snapshot they existed only as live dotfiles in `~/.config/zellij`, +versioned by hand-rolled `config.kdl.pre-<thing>` backups. + +| file | what it is | +|---|---| +| `config.kdl` | "unlock-first" keybinds (`Ctrl g` leader), pane frames, plugin bindings, sl0p fork options | +| `layouts/default.kdl` | the default tab layout | +| `layouts/default.swap.kdl` | responsive swap layouts (stacked-pane behaviour) | +| `themes/gruber-darker-pop.kdl` | the default theme | +| `themes/templeos.kdl` | alternate 16-color VGA theme | +| `answers.example.md` | seed for a project's `.pi-answers.md` (answer-picker buttons) | + +## Editing workflow + +Edit **here**, then `make install-config` to sync into your live config dir. +Do not edit the live dotfiles directly any more — they are now a build output. + +## Known rewrites still pending (see docs/rename.md) + +This is a *faithful* snapshot, so it still contains development-machine paths +that the rename + packaging work has to parameterise: + +- `file:~/.config/zellij/plugins/pane-finder.wasm` (x2) → sl0ppij's own config dir +- header comments referencing `~/.config/zellij/...` and `~/lol/zellij` +- `theme "gruber-darker-pop"` assumes `themes/` is installed alongside diff --git a/config/answers.example.md b/config/answers.example.md new file mode 100644 index 0000000..85dd160 --- /dev/null +++ b/config/answers.example.md @@ -0,0 +1,14 @@ +# pi answer buttons + +<!-- Clickable buttons in this pane's zellij frame row (OSC 5577), also alt+1..9. + Format: - `label` — text sent to the agent + This file is authoritative: it was seeded with answer-picker.ts defaults, + so deleting a line removes that button. `/answers reload` re-reads it. --> + +- `continue` — continue +- `yes` — yes, go ahead +- `no` — no — don't do that +- `explain` — explain what you just did, and why +- `tests` — run the tests and fix anything that fails +- `commit` — commit this with a clear message +- `push` — push to git diff --git a/config/config.kdl b/config/config.kdl new file mode 100644 index 0000000..7f15c8c --- /dev/null +++ b/config/config.kdl @@ -0,0 +1,380 @@ +// ~/.config/zellij/config.kdl +// "unlock-first" (non-colliding) setup: Zellij grabs almost no keys by default. +// Leader: Ctrl g -> unlocks into Normal mode, then one letter picks a mode: +// p pane t tab r resize m move s scroll/search o session +// Esc / Enter (or Ctrl g again) -> back to Locked. +// Everything else goes straight to your shell/vim/etc. + +default_mode "locked" + +keybinds clear-defaults=true { + normal { + } + locked { + bind "Ctrl g" { SwitchToMode "Normal"; } + } + resize { + bind "r" { SwitchToMode "Normal"; } + bind "h" "Left" { Resize "Increase Left"; } + bind "j" "Down" { Resize "Increase Down"; } + bind "k" "Up" { Resize "Increase Up"; } + bind "l" "Right" { Resize "Increase Right"; } + bind "H" { Resize "Decrease Left"; } + bind "J" { Resize "Decrease Down"; } + bind "K" { Resize "Decrease Up"; } + bind "L" { Resize "Decrease Right"; } + bind "=" "+" { Resize "Increase"; } + bind "-" { Resize "Decrease"; } + } + pane { + bind "p" { SwitchToMode "Normal"; } + bind "h" "Left" { MoveFocus "Left"; } + bind "l" "Right" { MoveFocus "Right"; } + bind "j" "Down" { MoveFocus "Down"; } + bind "k" "Up" { MoveFocus "Up"; } + bind "Tab" { SwitchFocus; } + bind "n" { NewPane; SwitchToMode "Locked"; } + bind "d" { NewPane "Down"; SwitchToMode "Locked"; } + bind "r" { NewPane "Right"; SwitchToMode "Locked"; } + bind "s" { NewPane "stacked"; SwitchToMode "Locked"; } + bind "x" { CloseFocus; SwitchToMode "Locked"; } + bind "f" { ToggleFocusFullscreen; SwitchToMode "Locked"; } + bind "Shift f" { ToggleFocusNoUiFullscreen; SwitchToMode "Locked"; } + bind "z" { TogglePaneFrames; SwitchToMode "Locked"; } + bind "w" { ToggleFloatingPanes; SwitchToMode "Locked"; } + bind "e" { TogglePaneEmbedOrFloating; SwitchToMode "Locked"; } + bind "c" { SwitchToMode "RenamePane"; PaneNameInput 0;} + bind "i" { TogglePanePinned; SwitchToMode "Locked"; } + } + move { + bind "m" { SwitchToMode "Normal"; } + bind "n" "Tab" { MovePane; } + bind "p" { MovePaneBackwards; } + bind "h" "Left" { MovePane "Left"; } + bind "j" "Down" { MovePane "Down"; } + bind "k" "Up" { MovePane "Up"; } + bind "l" "Right" { MovePane "Right"; } + } + tab { + bind "t" { SwitchToMode "Normal"; } + bind "r" { SwitchToMode "RenameTab"; TabNameInput 0; } + bind "h" "Left" "Up" "k" { GoToPreviousTab; } + bind "l" "Right" "Down" "j" { GoToNextTab; } + bind "n" { NewTab; SwitchToMode "Locked"; } + bind "x" { CloseTab; SwitchToMode "Locked"; } + bind "s" { ToggleActiveSyncTab; SwitchToMode "Locked"; } + bind "b" { BreakPane; SwitchToMode "Locked"; } + bind "]" { BreakPaneRight; SwitchToMode "Locked"; } + bind "[" { BreakPaneLeft; SwitchToMode "Locked"; } + bind "1" { GoToTab 1; SwitchToMode "Locked"; } + bind "2" { GoToTab 2; SwitchToMode "Locked"; } + bind "3" { GoToTab 3; SwitchToMode "Locked"; } + bind "4" { GoToTab 4; SwitchToMode "Locked"; } + bind "5" { GoToTab 5; SwitchToMode "Locked"; } + bind "6" { GoToTab 6; SwitchToMode "Locked"; } + bind "7" { GoToTab 7; SwitchToMode "Locked"; } + bind "8" { GoToTab 8; SwitchToMode "Locked"; } + bind "9" { GoToTab 9; SwitchToMode "Locked"; } + bind "Tab" { ToggleTab; } + } + scroll { + bind "s" { SwitchToMode "Normal"; } + bind "e" { EditScrollback; SwitchToMode "Locked"; } + bind "f" { SwitchToMode "EnterSearch"; SearchInput 0; } + bind "Ctrl c" { ScrollToBottom; SwitchToMode "Locked"; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "Alt left" { MoveFocusOrTab "left"; SwitchToMode "locked"; } + bind "Alt down" { MoveFocus "down"; SwitchToMode "locked"; } + bind "Alt up" { MoveFocus "up"; SwitchToMode "locked"; } + bind "Alt right" { MoveFocusOrTab "right"; SwitchToMode "locked"; } + bind "Alt h" { MoveFocusOrTab "left"; SwitchToMode "locked"; } + bind "Alt j" { MoveFocus "down"; SwitchToMode "locked"; } + bind "Alt k" { MoveFocus "up"; SwitchToMode "locked"; } + bind "Alt l" { MoveFocusOrTab "right"; SwitchToMode "locked"; } + } + search { + bind "Ctrl c" { ScrollToBottom; SwitchToMode "Locked"; } + bind "j" "Down" { ScrollDown; } + bind "k" "Up" { ScrollUp; } + bind "Ctrl f" "PageDown" "Right" "l" { PageScrollDown; } + bind "Ctrl b" "PageUp" "Left" "h" { PageScrollUp; } + bind "d" { HalfPageScrollDown; } + bind "u" { HalfPageScrollUp; } + bind "n" { Search "down"; } + bind "p" { Search "up"; } + bind "c" { SearchToggleOption "CaseSensitivity"; } + bind "w" { SearchToggleOption "Wrap"; } + bind "o" { SearchToggleOption "WholeWord"; } + } + entersearch { + bind "Ctrl c" "Esc" { SwitchToMode "Scroll"; } + bind "Enter" { SwitchToMode "Search"; } + } + renametab { + bind "Ctrl c" "Enter" { SwitchToMode "Locked"; } + bind "Esc" { UndoRenameTab; SwitchToMode "Tab"; } + } + renamepane { + bind "Ctrl c" "Enter" { SwitchToMode "Locked"; } + bind "Esc" { UndoRenamePane; SwitchToMode "Pane"; } + } + session { + bind "o" { SwitchToMode "Normal"; } + // pane finder (fuzzy find any pane across tabs, with live preview). + // skip_plugin_cache: always load the .wasm fresh from disk, so a rebuild + // takes effect on the next launch instead of being masked by the server's + // in-memory module cache. Costs a wasm recompile (~100-300ms) per launch. + bind "g" { + LaunchOrFocusPlugin "file:~/.config/zellij/plugins/pane-finder.wasm" { + floating true + move_to_focused_tab true + skip_plugin_cache true + }; + SwitchToMode "Locked" + } + bind "d" { Detach; } + bind "]" { FocusHostSession; SwitchToMode "Normal"; } + bind "[" { FocusGuestSession; SwitchToMode "Normal"; } + bind "f" { ToggleHostFullscreen; SwitchToMode "Normal"; } + bind "w" { + LaunchOrFocusPlugin "session-manager" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Locked" + } + bind "c" { + LaunchOrFocusPlugin "configuration" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Locked" + } + bind "p" { + LaunchOrFocusPlugin "plugin-manager" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Locked" + } + bind "a" { + LaunchOrFocusPlugin "zellij:about" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Locked" + } + bind "s" { + LaunchOrFocusPlugin "zellij:share" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Locked" + } + bind "l" { + LaunchOrFocusPlugin "zellij:layout-manager" { + floating true + move_to_focused_tab true + }; + SwitchToMode "Locked" + } + } + shared_except "locked" "renametab" "renamepane" { + bind "Ctrl g" { SwitchToMode "Locked"; } + bind "Ctrl q" { Quit; } + } + shared_except "renamepane" "renametab" "entersearch" "locked" { + bind "esc" { SwitchToMode "locked"; } + } + // The only keys Zellij still eats while locked: Alt-based quick actions. + // Comment this block out if your shell/editor wants Alt for itself. + shared_among "normal" "locked" { + bind "Alt n" { NewPane; } + bind "Alt f" { ToggleFloatingPanes; } + // Alt-f shows/hides the floating LAYER; Alt-Shift-F floats (or re-embeds) + // the pane you are on. Two easily-confused actions, so they share a key + // with Shift as the "act on this pane" variant -- same shape as + // Alt-p / Alt-Shift-P below. + bind "Alt Shift f" { TogglePaneEmbedOrFloating; } + // Ctrl-' does the same, next to Ctrl-; for the pane finder: same hand + // position, and both are keys nothing else claims. + // + // NOT Ctrl-f, which looks free but is not: nvim's page-forward, readline's + // forward-char, less/man's page-forward, and this config's own scroll and + // search modes all use it. Binding it here would shadow all of them in + // every pane. + bind "Ctrl '" { TogglePaneEmbedOrFloating; } + bind "Alt i" { MoveTab "Left"; } + bind "Alt o" { MoveTab "Right"; } + bind "Alt h" "Alt Left" { MoveFocusOrTab "Left"; } + bind "Alt l" "Alt Right" { MoveFocusOrTab "Right"; } + bind "Alt j" "Alt Down" { MoveFocus "Down"; } + bind "Alt k" "Alt Up" { MoveFocus "Up"; } + bind "Alt =" "Alt +" { Resize "Increase"; } + bind "Alt -" { Resize "Decrease"; } + bind "Alt [" { PreviousSwapLayout; } + bind "Alt ]" { NextSwapLayout; } + // tab cycling. Ctrl-= / Ctrl-- are only distinguishable from plain =/- + // under the kitty keyboard protocol, which zellij enables and kitty + // speaks; on a terminal without it these bindings simply never fire. + bind "Ctrl =" { GoToNextTab; } + bind "Ctrl -" { GoToPreviousTab; } + bind "Alt p" { TogglePaneInGroup; } + bind "Alt Shift p" { ToggleGroupMarking; } + // pane finder, straight from Locked -- the ^g o g route is too many + // keys for something used this often. Ctrl-; is deliberately obscure: + // ';' has no control-code meaning, readline does not bind it, and vim + // only binds bare ';' (repeat f/t), never Ctrl-;. Like Ctrl-= / Ctrl-- + // above it needs the kitty keyboard protocol to be distinguishable. + bind "Ctrl ;" { + LaunchOrFocusPlugin "file:~/.config/zellij/plugins/pane-finder.wasm" { + floating true + move_to_focused_tab true + skip_plugin_cache true + // sl0p fork only: pressing the chord again dismisses it, so one + // key toggles. A plugin cannot do this itself -- keybindings are + // resolved into actions before a key can reach a pane, so the + // launching chord never arrives at the plugin. + close_if_focused true + }; + SwitchToMode "Locked" + } + } + shared_except "locked" "renametab" "renamepane" { + bind "Enter" { SwitchToMode "Locked"; } + } + shared_except "pane" "locked" "renametab" "renamepane" "entersearch" { + bind "p" { SwitchToMode "Pane"; } + } + shared_except "resize" "locked" "renametab" "renamepane" "entersearch" { + bind "r" { SwitchToMode "Resize"; } + } + shared_except "scroll" "locked" "renametab" "renamepane" "entersearch" { + bind "s" { SwitchToMode "Scroll"; } + } + shared_except "session" "locked" "renametab" "renamepane" "entersearch" { + bind "o" { SwitchToMode "Session"; } + } + shared_except "tab" "locked" "renametab" "renamepane" "entersearch" { + bind "t" { SwitchToMode "Tab"; } + } + shared_except "move" "locked" "renametab" "renamepane" "entersearch" { + bind "m" { SwitchToMode "Move"; } + } +} + +// ── look & feel ──────────────────────────────────────────────────────────── +// ~/.config/zellij/themes/templeos.kdl — 16-color VGA, white desktop, blue task +// frames. Run `templeos-term on` to put the *terminal* into the same palette. +// gruber-darker-pop = stock gruber-darker + a `frame_unselected` block, so the +// FOCUSED pane's frame (gruber yellow) pops against recessive gray inactive ones. +// Defined in ~/.config/zellij/themes/gruber-darker-pop.kdl — tune `frame_unselected` +// `base` there: lower = more recessive, higher = louder. Revert with "gruber-darker". +theme "gruber-darker-pop" +// Fallbacks if you get tired of God's own colors: catppuccin-mocha, gruvbox-dark, +// tokyo-night-storm, nord, kanagawa, cyber-noir, retro-wave, gruber-darker, ansi. + +ui { + pane_frames { + rounded_corners true // (was false: "TempleOS does not do rounded. 640x480 or death.") + hide_session_name false + // sl0p fork only (~/lol/zellij, branch sl0p/base): "left" | "center" | "right". + // Upstream zellij hardcodes full-frame titles flush left. + title_alignment "center" + // sl0p fork only. + // gap - uniform space around every pane edge, in ROWS. A pane + // insets right/bottom always and left/top only at the + // screen edge, so two neighbours show exactly `gap` + // between them (not 2x) and screen edges match. + // padding - breathing room between a pane's frame and its + // content, also in ROWS. + // gap_aspect - columns per row (default 2). Terminal cells are ~2x + // taller than wide, so 1 row needs ~2 columns to look + // like the same distance. Set to 1 to disable. + gap 1 + padding 0 0 0 0 + gap_aspect 2 + // Desaturate the CONTENT of panes you are not focused on, 0-100. + // 0 = off. Colours blend toward their own luminance, so an unfocused + // pane keeps its brightness but loses its colour. + inactive_grayscale 85 + // Show no pane title at all until something names the pane: either you + // rename it (double-click its top row) or the running program sets a + // title. Upstream shows a "Pane #N" placeholder. + hide_default_pane_names true + // sl0p fork only. Collapse to the swap layout named by + // `responsive_layout` when the tab is narrower than this many columns, + // and restore the previous layout when it grows back. 0 = off. + // + // Restore only happens above min_cols + 10 (hysteresis): without a dead + // band, dragging an edge that lands on the threshold flips the layout on + // every motion event, and each flip resizes every PTY in the tab. + // + // Selects BY NAME, so it needs a matching `swap_tiled_layout name=...` + // in layouts/default.swap.kdl -- and that layout's own min_panes/ + // max_panes constraint still has to be satisfied, or nothing happens. + // + // Stops acting once you have rearranged panes by hand, same principle + // as auto_layout: an explicit arrangement outranks an automatic one. + responsive_min_cols 90 + responsive_layout "stacked" + // Cap how many entries the stacked-pane picker shows at once. It windows + // around the focused entry and reports the rest as "+N more" in the row + // it already reserves. 0 = show every member. + stack_list_max_rows 3 + // "grid" packs picker entries several per row, so the cap above becomes a + // space budget rather than a count. "rows" (default) is one per row. + stack_list_layout "grid" + // "boxed" (default) brackets each entry and marks the active one with + // "> "; "minimal" drops both and inverts the active entry instead. + stack_list_style "minimal" + // Status bar action buttons (OSC 5577): "boxed" renders [Approve], + // "indexed" renders 1:Approve -- useful when something else binds those + // numbers to the same actions. + status_bar_button_style "indexed" + } +} + +pane_frames true +pane_frame_style "full" + +// ── behaviour ────────────────────────────────────────────────────────────── +scroll_buffer_size 50000 +copy_on_select true +scrollback_editor "/usr/bin/nvim" + +// keep sessions (tabs/panes/cwds/commands) resurrectable after a crash/reboot +session_serialization true +serialize_pane_viewport true +scrollback_lines_to_serialize 10000 + +// smarter resizing / stacked panes +stacked_resize true +stacked_pane_list true + +// mouse quality-of-life +mouse_mode true +advanced_mouse_actions true +mouse_hover_effects true +// sl0p fork only: the "Ctrl <MouseScroll> or <drag borders> to resize" hint that +// appears on hover. Upstream bundles it with mouse_hover_effects, which also +// governs the hover frame highlight -- this separates them, so the reminder can +// go while the highlight stays. +mouse_hover_help_text false +mouse_scroll_resize true +// focus panes on hover (no click needed) +focus_follows_mouse true + +// less chrome on startup +show_startup_tips false +show_release_notes false + +// detach (don't kill) when the terminal window goes away +on_force_close "detach" + +styled_underlines true diff --git a/config/layouts/default.kdl b/config/layouts/default.kdl new file mode 100644 index 0000000..aa86a86 --- /dev/null +++ b/config/layouts/default.kdl @@ -0,0 +1,137 @@ +// ~/.config/zellij/layouts/default.kdl +// zjstatus bar, palette-matched to themes/gruber-darker-pop.kdl. +// +// WHY THIS FILE EXISTS: a layout named `default` is what zellij applies to every +// new tab/session, so dropping zjstatus in here replaces the stock tab-bar + +// status-bar everywhere without touching config.kdl. +// +// NOTE ON FRAMES: `hide_frame_for_single_pane` is deliberately NOT set. That +// option makes zjstatus toggle pane frames itself, which would fight the +// `frame_unselected` / `frame_selected` colors tuned in gruber-darker-pop.kdl. +// config.kdl keeps `pane_frames true` and stays authoritative over frames. +// +// NOTE ON SWAP LAYOUTS: overriding the default layout also overrides zellij's +// default swap layouts, so they are restored in default.swap.kdl next to this +// file. Alt-[ / Alt-] keep working. +// +// PERMISSIONS: on first launch zjstatus asks for ReadApplicationState, +// ChangeApplicationState and RunCommands (the last one is for the git widget). +// Click the bar, press `y`. Must be repeated whenever zjstatus.wasm is updated. + +layout { + default_tab_template { + children + pane size=1 borderless=true { + plugin location="file:~/.config/zellij/plugins/zjstatus.wasm" { + + // ── palette: lifted verbatim from gruber-darker-pop.kdl ────── + color_bg "#181818" // 24 24 24 theme background + color_bg_alt "#282828" // 40 40 40 ribbon_unselected bg + color_dim "#484848" // 72 72 72 frame_unselected base + color_fg "#e4e4ef" // 228 228 239 text base + color_sage "#95a99f" // 149 169 159 emphasis_0 + color_yellow "#ffdd33" // 255 221 51 frame_selected / gruber yellow + color_green "#73c936" // 115 201 54 frame_highlight / success + color_blue "#96a6c8" // 150 166 200 + color_purple "#9e95c7" // 158 149 199 + color_brown "#cc8c3c" // 204 140 60 + color_red "#ff4f58" // 255 79 88 exit_code_error + + format_left "{mode}{session}" + format_center "{tabs}" + format_right "{command_git} {datetime}" + format_space "#[bg=$bg]" + + // hide the least-important part instead of letting the three + // sections collide on a narrow terminal. Priority: center + // (tabs) > left (mode) > right (git/clock is the first to go). + format_hide_on_overlength "true" + format_precedence "clr" + + // ── mode ──────────────────────────────────────────────────── + // config.kdl is `default_mode "locked"` with Ctrl-g as leader, + // so LOCKED is the resting state and is styled to recede. + // Every *unlocked* mode is loud on purpose: with a leader-key + // setup the expensive mistake is typing into a pane while a + // mode is still live, so the bar should be impossible to miss. + // Powerline caps: U+E0B6 left-round, U+E0B4 right-round. + // These live in the Powerline Symbols block and exist in every + // Nerd Font AND every powerline-patched font, so they are the + // safe part. The lock/unlock glyphs below are Font Awesome + // range and are Nerd-Font-only: if ONLY those show as tofu, + // you have a powerline font rather than a Nerd Font. + // Pre-Nerd-Font version saved at default.kdl.pre-nerdfont. + mode_locked "#[bg=$dim,fg=$bg,bold] LOCKED #[bg=$bg,fg=$dim]#[bg=$bg,fg=$dim,italic] ^g " + mode_normal "#[bg=$yellow,fg=$bg,bold] NORMAL #[bg=$bg,fg=$yellow]#[bg=$bg,fg=$sage,italic] p t r m s o " + mode_pane "#[bg=$blue,fg=$bg,bold] PANE #[bg=$bg,fg=$blue]#[bg=$bg,fg=$sage,italic] hjkl n d r s x f w e c " + mode_tab "#[bg=$purple,fg=$bg,bold] TAB #[bg=$bg,fg=$purple]#[bg=$bg,fg=$sage,italic] hl n x r b 1-9 " + mode_resize "#[bg=$brown,fg=$bg,bold] RESIZE #[bg=$bg,fg=$brown]#[bg=$bg,fg=$sage,italic] hjkl HJKL +- " + mode_move "#[bg=$brown,fg=$bg,bold] MOVE #[bg=$bg,fg=$brown]#[bg=$bg,fg=$sage,italic] hjkl n p " + mode_scroll "#[bg=$green,fg=$bg,bold] SCROLL #[bg=$bg,fg=$green]#[bg=$bg,fg=$sage,italic] jk du f=find e=edit " + mode_enter_search "#[bg=$green,fg=$bg,bold] SEARCH: #[bg=$bg,fg=$green]#[bg=$bg,fg=$sage,italic] type, Enter " + mode_search "#[bg=$green,fg=$bg,bold] FOUND #[bg=$bg,fg=$green]#[bg=$bg,fg=$sage,italic] n p c w o " + mode_session "#[bg=$sage,fg=$bg,bold] SESSION #[bg=$bg,fg=$sage]#[bg=$bg,fg=$sage,italic] g=panes w d c p " + mode_rename_tab "#[bg=$red,fg=$bg,bold] RENAME TAB #[bg=$bg,fg=$red]" + mode_rename_pane "#[bg=$red,fg=$bg,bold] RENAME PANE #[bg=$bg,fg=$red]" + mode_prompt "#[bg=$red,fg=$bg,bold] PROMPT #[bg=$bg,fg=$red]" + mode_tmux "#[bg=$brown,fg=$bg,bold] TMUX #[bg=$bg,fg=$brown]" + mode_default_to_mode "locked" + + // ── session ───────────────────────────────────────────────── + session "#[bg=$bg,fg=$blue,bold] {name} #[bg=$bg,fg=$dim] " + + // ── tabs ──────────────────────────────────────────────────── + // ▐ / ▌ are half-block glyphs: they fake a hard-edged powerline + // pill without needing a Nerd Font, and stay square-cornered to + // match `rounded_corners false`. + tab_normal "#[bg=$bg,fg=$dim] {index}#[bg=$bg,fg=$sage] {name} {floating_indicator}#[bg=$bg,fg=$dim] " + tab_normal_fullscreen "#[bg=$bg,fg=$dim] {index}#[bg=$bg,fg=$sage] {name} {fullscreen_indicator}#[bg=$bg,fg=$dim] " + tab_normal_sync "#[bg=$bg,fg=$dim] {index}#[bg=$bg,fg=$sage] {name} {sync_indicator}#[bg=$bg,fg=$dim] " + + tab_active "#[bg=$yellow,fg=$bg,bold] {index} {name} {floating_indicator}#[bg=$bg,fg=$yellow]" + tab_active_fullscreen "#[bg=$yellow,fg=$bg,bold] {index} {name} {fullscreen_indicator}#[bg=$bg,fg=$yellow]" + tab_active_sync "#[bg=$yellow,fg=$bg,bold] {index} {name} {sync_indicator}#[bg=$bg,fg=$yellow]" + + tab_separator "#[bg=$bg] " + tab_rename "#[bg=$red,fg=$bg,bold] {index} {name} #[bg=$bg,fg=$red]" + + tab_sync_indicator "<> " + tab_fullscreen_indicator "[] " + tab_floating_indicator "^ " + + // ── git branch of the FOCUSED pane's cwd ──────────────────── + // {focused_pane_cwd} (zjstatus 0.24.0+) makes this track + // whichever pane you are in rather than the session's cwd. + // + // The `bash -c` wrapper is REQUIRED, not cosmetic: zjstatus + // execs the command directly with no shell, so a bare + // `git ... 2>/dev/null` hands `2>/dev/null` to git as a literal + // revision argument and git dies with "ambiguous argument". + // The redirect also matters: outside a git repo git writes to + // stderr, and without it that noise can reach the bar. + // + // rendermode "static" = format with the config's format string + // and ignore any zjstatus directives in the command's own output. + command_git_command "bash -c \"git rev-parse --abbrev-ref HEAD 2>/dev/null\"" + command_git_format "#[bg=$bg,fg=$green] {stdout}" + command_git_interval "10" + command_git_rendermode "static" + command_git_cwd "{focused_pane_cwd}" + + // ── clock ─────────────────────────────────────────────────── + // Keep `#[..]` directives OUT of datetime_format: that string is + // strftime output substituted into {format} after the outer + // format is parsed, so a directive in here renders literally. + datetime "#[bg=$bg,fg=$sage]{format}" + datetime_format "%a %d %b %H:%M" + datetime_timezone "Europe/Amsterdam" + + // ── thin rule above the bar, so it reads as chrome ────────── + border_enabled "false" + border_char "─" + border_format "#[fg=$dim]{char}" + border_position "top" + } + } + } +} diff --git a/config/layouts/default.swap.kdl b/config/layouts/default.swap.kdl new file mode 100644 index 0000000..4e97b79 --- /dev/null +++ b/config/layouts/default.swap.kdl @@ -0,0 +1,131 @@ +// ~/.config/zellij/layouts/default.swap.kdl +// Zellij's stock swap layouts, restored. +// +// Defining layouts/default.kdl overrides the built-in default layout, which +// silently takes the built-in swap layouts with it and leaves Alt-[ / Alt-] +// doing nothing. This file is `zellij setup --dump-swap-layout default` with +// the hardcoded `ui` tab_template stripped, so each swap layout inherits the +// zjstatus `default_tab_template` from default.kdl instead of re-adding +// zellij's own tab-bar/status-bar. +// +// Regenerate with: +// zellij setup --dump-swap-layout default +// then delete the `tab_template name="ui"` block and s/^ui /tab /. + +swap_tiled_layout name="vertical" { + tab max_panes=5 { + pane split_direction="vertical" { + pane + pane { children; } + } + } + tab max_panes=8 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + } + } + tab max_panes=12 { + pane split_direction="vertical" { + pane { children; } + pane { pane; pane; pane; pane; } + pane { pane; pane; pane; pane; } + } + } +} + +swap_tiled_layout name="horizontal" { + tab max_panes=4 { + pane + pane + } + tab max_panes=8 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } + tab max_panes=12 { + pane { + pane split_direction="vertical" { children; } + pane split_direction="vertical" { pane; pane; pane; pane; } + pane split_direction="vertical" { pane; pane; pane; pane; } + } + } +} + +// min_panes lowered 4 -> 2 for the responsive-layout collapse: the switch finds +// this layout by name, but still has to satisfy this constraint, so at 4 a +// narrow 2- or 3-pane tab would silently refuse to collapse. +swap_tiled_layout name="stacked" { + tab min_panes=2 { + pane stacked=true { children; } + } +} + +swap_tiled_layout name="half-stacked" { + tab min_panes=5 { + pane split_direction="vertical" { + pane + pane stacked=true { children; } + } + } +} + +swap_floating_layout name="staggered" { + floating_panes +} + +swap_floating_layout name="enlarged" { + floating_panes max_panes=10 { + pane { x "5%"; y 1; width "90%"; height "90%"; } + pane { x "5%"; y 2; width "90%"; height "90%"; } + pane { x "5%"; y 3; width "90%"; height "90%"; } + pane { x "5%"; y 4; width "90%"; height "90%"; } + pane { x "5%"; y 5; width "90%"; height "90%"; } + pane { x "5%"; y 6; width "90%"; height "90%"; } + pane { x "5%"; y 7; width "90%"; height "90%"; } + pane { x "5%"; y 8; width "90%"; height "90%"; } + pane { x "5%"; y 9; width "90%"; height "90%"; } + pane { x 10; y 10; width "90%"; height "90%"; } + } +} + +swap_floating_layout name="spread" { + floating_panes max_panes=1 { + pane {y "50%"; x "50%"; } + } + floating_panes max_panes=2 { + pane { x "1%"; y "25%"; width "45%"; } + pane { x "50%"; y "25%"; width "45%"; } + } + floating_panes max_panes=3 { + pane { y "55%"; width "45%"; height "45%"; } + pane { x "1%"; y "1%"; width "45%"; } + pane { x "50%"; y "1%"; width "45%"; } + } + floating_panes max_panes=4 { + pane { x "1%"; y "55%"; width "45%"; height "45%"; } + pane { x "50%"; y "55%"; width "45%"; height "45%"; } + pane { x "1%"; y "1%"; width "45%"; height "45%"; } + pane { x "50%"; y "1%"; width "45%"; height "45%"; } + } +} + +// sl0p fork: `arrangement` builds a tree for N panes, instead of the default +// "every extra pane becomes a flat sibling". +swap_tiled_layout name="grid" { + tab min_panes=3 { + pane arrangement="grid" { + children + } + } +} + +swap_tiled_layout name="master-stack" { + tab min_panes=2 { + pane arrangement="master-stack" { + children + } + } +} diff --git a/config/themes/gruber-darker-pop.kdl b/config/themes/gruber-darker-pop.kdl new file mode 100644 index 0000000..1a65ad7 --- /dev/null +++ b/config/themes/gruber-darker-pop.kdl @@ -0,0 +1,159 @@ +// gruber-darker-pop — stock zellij `gruber-darker` (copied verbatim from +// ~/lol/zellij/zellij-utils/assets/themes/gruber-darker.kdl) with ONE addition: +// a `frame_unselected` block. Stock gruber-darker omits it, and zellij defaults +// it to None (data.rs:1501), so unfocused frames get no explicit color. +// focused frame = 255 221 51 (gruber yellow, from frame_selected) +// unfocused frame= 72 72 72 (muted gray, below) +// mouse hover = 115 201 54 (green, from frame_highlight) +themes { + gruber-darker-pop { + text_unselected { + base 228 228 239 + background 24 24 24 + emphasis_0 149 169 159 + emphasis_1 150 166 200 + emphasis_2 115 201 54 + emphasis_3 255 221 51 + } + text_selected { + base 228 228 239 + background 40 40 40 + emphasis_0 255 255 255 + emphasis_1 255 221 51 + emphasis_2 115 201 54 + emphasis_3 150 166 200 + } + ribbon_unselected { + base 228 228 239 + background 40 40 40 + emphasis_0 149 169 159 + emphasis_1 204 140 60 + emphasis_2 150 166 200 + emphasis_3 158 149 199 + } + ribbon_selected { + base 0 0 0 + background 255 221 51 + emphasis_0 244 244 255 + emphasis_1 255 255 255 + emphasis_2 115 201 54 + emphasis_3 150 166 200 + } + table_title { + base 255 221 51 + background 24 24 24 + emphasis_0 255 255 255 + emphasis_1 244 244 255 + emphasis_2 150 166 200 + emphasis_3 115 201 54 + } + table_cell_unselected { + base 228 228 239 + background 24 24 24 + emphasis_0 149 169 159 + emphasis_1 150 166 200 + emphasis_2 204 140 60 + emphasis_3 115 201 54 + } + table_cell_selected { + base 228 228 239 + background 69 61 65 + emphasis_0 255 255 255 + emphasis_1 255 221 51 + emphasis_2 115 201 54 + emphasis_3 150 166 200 + } + list_unselected { + base 228 228 239 + background 24 24 24 + emphasis_0 149 169 159 + emphasis_1 150 166 200 + emphasis_2 204 140 60 + emphasis_3 115 201 54 + } + list_selected { + base 244 244 255 + background 72 72 72 + emphasis_0 255 255 255 + emphasis_1 255 221 51 + emphasis_2 115 201 54 + emphasis_3 150 166 200 + } + // ── added: inactive pane frames recede so the focused one pops ── + // base = the border color of every UNFOCUSED pane. Tune this one number: + // lower = more recessive (24 24 24 is invisible against the bg), higher = louder. + // Terminals cannot do WM-style pane gaps (no gap/margin/padding option + // exists in zellij at all). Every cell is content or chrome, so the frame + // IS the only possible separator between two panes. Going the other way + // -- pane_frame_style "titles" -- removed the separator and read as MORE + // cramped, not less. So: louder frames = more perceived separation. + // Was base 72 / emphasis 90. Keep emphasis ~24 above base or the + // hierarchy inverts and frame accents go darker than the frame itself. + frame_unselected { + base 96 96 96 + background 24 24 24 + emphasis_0 114 114 114 + emphasis_1 114 114 114 + emphasis_2 114 114 114 + emphasis_3 114 114 114 + } + // ── focused pane frame ── + // base = the border color of the FOCUSED pane. Stock gruber yellow + // (255 221 51) sits at 4.68:1 contrast against the unfocused frame + // (96 96 96) -- roughly double what's needed to read as "focused", which + // is why it glares. What actually signals focus is contrast against the + // UNFOCUSED frame plus a hue shift, not raw brightness, so this drops to + // a calm desaturated blue at 2.57:1 -- still unmistakable, no glare. + // It also stays clear of frame_highlight's green (mouse hover) in hue. + // The theme keeps its yellow identity in the status bar / ribbons. + // Alternatives (swap base, all measured vs the 96 96 96 unfocused frame): + // 199 164 74 muted gold 2.65:1 <- keeps the gruber yellow hue + // 158 149 199 theme violet 2.27:1 + // 149 169 159 theme sage 2.53:1 + // 255 221 51 stock yellow 4.68:1 <- original + frame_selected { + base 150 166 200 + background 24 24 24 + emphasis_0 255 255 255 + emphasis_1 244 244 255 + emphasis_2 115 201 54 + emphasis_3 150 166 200 + } + frame_highlight { + base 115 201 54 + background 24 24 24 + emphasis_0 255 255 255 + emphasis_1 255 221 51 + emphasis_2 150 166 200 + emphasis_3 158 149 199 + } + exit_code_success { + base 115 201 54 + background 24 24 24 + emphasis_0 255 255 255 + emphasis_1 255 221 51 + emphasis_2 150 166 200 + emphasis_3 158 149 199 + } + exit_code_error { + base 255 79 88 + background 24 24 24 + emphasis_0 255 255 255 + emphasis_1 255 221 51 + emphasis_2 150 166 200 + emphasis_3 158 149 199 + } + multiplayer_user_colors { + player_1 255 221 51 + player_2 115 201 54 + player_3 150 166 200 + player_4 158 149 199 + player_5 149 169 159 + player_6 204 140 60 + player_7 255 79 88 + player_8 244 56 65 + player_9 199 60 63 + player_10 255 255 255 + } + } +} diff --git a/config/themes/templeos.kdl b/config/themes/templeos.kdl new file mode 100644 index 0000000..b6848d1 --- /dev/null +++ b/config/themes/templeos.kdl @@ -0,0 +1,133 @@ +// TempleOS — "God's temple" 640x480 16-color VGA look for Zellij. +// Palette is the exact TempleOS/VGA 16: +// BLACK 0,0,0 BLUE 0,0,170 GREEN 0,170,0 CYAN 0,170,170 +// RED 170,0,0 PURPLE 170,0,170 BROWN 170,85,0 LTGRAY 170,170,170 +// DKGRAY 85,85,85 LTBLUE 85,85,255 LTGREEN 85,255,85 LTCYAN 85,255,255 +// LTRED 255,85,85 LTPURPLE 255,85,255 YELLOW 255,255,85 WHITE 255,255,255 +// Desktop is WHITE with BLACK text, selected things are white-on-BLUE, +// like a highlighted line in a TempleOS window. + +themes { + templeos { + // normal UI text: black on white, TempleOS desktop + text_unselected { + base 0 0 0 + background 255 255 255 + emphasis_0 170 0 0 // RED + emphasis_1 0 0 170 // BLUE + emphasis_2 0 170 0 // GREEN + emphasis_3 170 0 170 // PURPLE + } + text_selected { + base 255 255 255 + background 0 0 170 // white on BLUE, the TempleOS cursor line + emphasis_0 255 255 85 // YELLOW + emphasis_1 85 255 255 // LTCYAN + emphasis_2 85 255 85 // LTGREEN + emphasis_3 255 85 255 // LTPURPLE + } + // tabs / mode ribbons + ribbon_selected { + base 255 255 255 + background 0 0 170 // BLUE "active task" + emphasis_0 255 255 85 + emphasis_1 85 255 255 + emphasis_2 85 255 85 + emphasis_3 255 85 255 + } + ribbon_unselected { + base 0 0 0 + background 170 170 170 // LTGRAY inactive + emphasis_0 170 0 0 + emphasis_1 255 255 255 + emphasis_2 0 0 170 + emphasis_3 170 0 170 + } + table_title { + base 170 0 0 // RED headings, like TempleOS docs + background 0 + emphasis_0 0 0 170 + emphasis_1 0 170 0 + emphasis_2 170 85 0 + emphasis_3 170 0 170 + } + table_cell_unselected { + base 0 0 0 + background 255 255 255 + emphasis_0 170 0 0 + emphasis_1 0 0 170 + emphasis_2 0 170 0 + emphasis_3 170 0 170 + } + table_cell_selected { + base 255 255 255 + background 0 0 170 + emphasis_0 255 255 85 + emphasis_1 85 255 255 + emphasis_2 85 255 85 + emphasis_3 255 85 255 + } + list_unselected { + base 0 0 0 + background 255 255 255 + emphasis_0 170 0 0 + emphasis_1 0 0 170 + emphasis_2 0 170 0 + emphasis_3 170 0 170 + } + list_selected { + base 255 255 255 + background 0 0 170 + emphasis_0 255 255 85 + emphasis_1 85 255 255 + emphasis_2 85 255 85 + emphasis_3 255 85 255 + } + // window borders: focused task gets a BLUE frame, like a TempleOS window + frame_selected { + base 0 0 170 + background 0 + emphasis_0 170 0 0 + emphasis_1 0 170 0 + emphasis_2 170 0 170 + emphasis_3 0 + } + frame_highlight { + base 170 0 0 // RED alert border + background 0 + emphasis_0 255 85 85 + emphasis_1 170 0 0 + emphasis_2 170 0 0 + emphasis_3 170 0 0 + } + exit_code_success { + base 0 170 0 + background 0 + emphasis_0 0 170 170 + emphasis_1 0 0 0 + emphasis_2 170 0 170 + emphasis_3 0 0 170 + } + exit_code_error { + base 170 0 0 + background 0 + emphasis_0 255 255 85 + emphasis_1 0 + emphasis_2 0 + emphasis_3 0 + } + // the 16-color rotation TempleOS uses for its "God words" + multiplayer_user_colors { + player_1 170 0 0 + player_2 0 0 170 + player_3 0 170 0 + player_4 170 85 0 + player_5 170 0 170 + player_6 0 170 170 + player_7 255 85 85 + player_8 85 85 255 + player_9 85 255 85 + player_10 85 85 85 + } + } +} |
