// ~/.config/sl0ppij/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/sl0ppij/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 "sl0ppij:about" { floating true move_to_focused_tab true }; SwitchToMode "Locked" } bind "s" { LaunchOrFocusPlugin "sl0ppij:share" { floating true move_to_focused_tab true }; SwitchToMode "Locked" } bind "l" { LaunchOrFocusPlugin "sl0ppij: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/sl0ppij/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/sl0ppij/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/sl0ppij/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 (sl0ppij): "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 or 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