detached 3fc69dd

feat(examples): add the bundled bar with workspaces, focused app and clock, and document themes

commit
3fc69ddb45cfd8ba6bc6e9ce12e674a4b6493958
author
brice · · 2026-10-05 15:42 UTC
parent
bb8f0da
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
7 files · +233 -4
M README.md +54 -4
@@ -36,8 +36,7 @@ Create `~/.config/neferbar/config.toml`:
3636 [bar]
3737 font = "JetBrainsMono Nerd Font Mono" # use a "Mono" Nerd Font so icons fit one cell
3838 size = 14 # text size
39-background = "#1e1e2e"
40-foreground = "#cdd6f4"
39+theme = "auto" # colors of your terminal, or the path of a theme file
4140
4241 [[module]]
4342 name = "clock" # a unique name
@@ -53,10 +52,33 @@ Add one `[[module]]` block per script. Save the file and the bar updates immedia
5352 | `bar.size` | `14` | Text size in logical pixels. |
5453 | `bar.scale` | `1.0` | Extra zoom on top of the monitor scale. |
5554 | `bar.output` | any | A monitor name such as `HDMI-A-1`. Needs a restart. |
56-| `bar.background`, `bar.foreground` | catppuccin | `#rrggbb`. |
55+| `bar.theme` | `auto` | `auto` reads the colors of the terminal you use. Or the path of a theme file (see Colors). |
56+| `bar.background`, `bar.foreground` | from the theme | `#rrggbb`. Set them to override the theme. |
5757
5858 If the font is not installed, neferbar warns and falls back to another font, and icons may be missing.
5959
60+## Colors
61+
62+The bar takes its colors from your terminal, so it matches the rest of your desktop.
63+
64+```toml
65+theme = "auto" # the terminal you use
66+theme = "~/.config/kitty/themes/mytheme.conf" # or one file
67+```
68+
69+`auto` picks the terminal named by `$TERMINAL` (or the first entry of `xdg-terminals.list`), and otherwise the first of kitty, ghostty, foot and alacritty that has a config with colors. A file can be any of those formats, and the bar recognizes it by its content. Includes and `theme =` names are followed. Colors the file does not set come from a built-in dark theme.
70+
71+The bar watches every file it read: change your terminal's theme and the bar changes with it. If the theme cannot be read, the bar uses the built-in colors and says why in its log.
72+
73+Scripts get the colors as environment variables, each `#rrggbb`, so a script never hard-codes one:
74+
75+| Variable | Color |
76+|---|---|
77+| `NEFERBAR_BACKGROUND`, `NEFERBAR_FOREGROUND` | the bar's own background and text |
78+| `NEFERBAR_COLOR0` .. `NEFERBAR_COLOR15` | the theme's 16 ANSI colors |
79+
80+The 16 ANSI codes a script prints (`\033[31m` red, `\033[44m` blue background and so on) use the theme as well.
81+
6082 ## Write a script
6183
6284 A module is any program that prints lines. **Each line replaces what the module showed before.**
@@ -142,9 +164,37 @@ done
142164
143165 neferbar draws at most as often as your monitor refreshes. If a script prints faster than that, only the latest frame is drawn. A script that prints nothing costs nothing.
144166
167+### The bundled bar
168+
169+`examples/bundle` is a ready-made bar: workspaces on the left, the focused app in the middle, the clock on the right, in your terminal's colors.
170+
171+![The bundled bar](docs/img/bundle.png)
172+ Copy it to `~/.config/neferbar/` (the install steps are at the top of `examples/bundle/config.toml`).
173+
174+Its helper `lib.sh` has what a themed script needs: `fg`/`bg` to set a color from `#rrggbb`, `mix` to blend two, `fade` for the soft edge, and `state_changed_loop` to react to NeferWL. Read the three scripts: each is about thirty lines.
175+
176+On NeferWL the workspaces show one dot per window and the stash. On other compositors the workspace and focus modules show nothing yet, and the clock still works.
177+
178+### Reacting to your compositor (workspace effects)
179+
180+A module can watch anything, including the compositor's own state. NeferWL keeps a JSON file up to date and passes its path to every program it starts as `$NEFERWL_STATE`. It replaces the file on each change, so a module only has to check the file's modification time:
181+
182+```sh
183+stamp=$(mktemp)
184+touch -r "$NEFERWL_STATE" "$stamp"
185+while sleep 0.1; do
186+ if [[ $NEFERWL_STATE -nt $stamp ]]; then # the file changed
187+ touch -r "$NEFERWL_STATE" "$stamp"
188+ jq -r '.outputs[] | "\(.name) is on workspace \(.active)"' "$NEFERWL_STATE"
189+ fi
190+done
191+```
192+
193+That is all it takes to draw an effect when you switch workspace. `examples/bundle/left.sh` redraws the workspaces that way.
194+
145195 ### Example modules
146196
147-The `examples/modules` directory has three:
197+The `examples/modules` directory has these:
148198
149199 - `static.sh`: one line, then idle.
150200 - `clock.sh`: a clock with an icon.
@@ -36,8 +36,7 @@ Create `~/.config/neferbar/config.toml`:
36 [bar]36 [bar]
37 font = "JetBrainsMono Nerd Font Mono" # use a "Mono" Nerd Font so icons fit one cell37 font = "JetBrainsMono Nerd Font Mono" # use a "Mono" Nerd Font so icons fit one cell
38 size = 14 # text size38 size = 14 # text size
39-background = "#1e1e2e"39+theme = "auto" # colors of your terminal, or the path of a theme file
40-foreground = "#cdd6f4"
41 40
42 [[module]]41 [[module]]
43 name = "clock" # a unique name42 name = "clock" # a unique name
@@ -53,10 +52,33 @@ Add one `[[module]]` block per script. Save the file and the bar updates immedia
53 | `bar.size` | `14` | Text size in logical pixels. |52 | `bar.size` | `14` | Text size in logical pixels. |
54 | `bar.scale` | `1.0` | Extra zoom on top of the monitor scale. |53 | `bar.scale` | `1.0` | Extra zoom on top of the monitor scale. |
55 | `bar.output` | any | A monitor name such as `HDMI-A-1`. Needs a restart. |54 | `bar.output` | any | A monitor name such as `HDMI-A-1`. Needs a restart. |
56-| `bar.background`, `bar.foreground` | catppuccin | `#rrggbb`. |55+| `bar.theme` | `auto` | `auto` reads the colors of the terminal you use. Or the path of a theme file (see Colors). |
56+| `bar.background`, `bar.foreground` | from the theme | `#rrggbb`. Set them to override the theme. |
57 57
58 If the font is not installed, neferbar warns and falls back to another font, and icons may be missing.58 If the font is not installed, neferbar warns and falls back to another font, and icons may be missing.
59 59
60+## Colors
61+
62+The bar takes its colors from your terminal, so it matches the rest of your desktop.
63+
64+```toml
65+theme = "auto" # the terminal you use
66+theme = "~/.config/kitty/themes/mytheme.conf" # or one file
67+```
68+
69+`auto` picks the terminal named by `$TERMINAL` (or the first entry of `xdg-terminals.list`), and otherwise the first of kitty, ghostty, foot and alacritty that has a config with colors. A file can be any of those formats, and the bar recognizes it by its content. Includes and `theme =` names are followed. Colors the file does not set come from a built-in dark theme.
70+
71+The bar watches every file it read: change your terminal's theme and the bar changes with it. If the theme cannot be read, the bar uses the built-in colors and says why in its log.
72+
73+Scripts get the colors as environment variables, each `#rrggbb`, so a script never hard-codes one:
74+
75+| Variable | Color |
76+|---|---|
77+| `NEFERBAR_BACKGROUND`, `NEFERBAR_FOREGROUND` | the bar's own background and text |
78+| `NEFERBAR_COLOR0` .. `NEFERBAR_COLOR15` | the theme's 16 ANSI colors |
79+
80+The 16 ANSI codes a script prints (`\033[31m` red, `\033[44m` blue background and so on) use the theme as well.
81+
60 ## Write a script82 ## Write a script
61 83
62 A module is any program that prints lines. **Each line replaces what the module showed before.**84 A module is any program that prints lines. **Each line replaces what the module showed before.**
@@ -142,9 +164,37 @@ done
142 164
143 neferbar draws at most as often as your monitor refreshes. If a script prints faster than that, only the latest frame is drawn. A script that prints nothing costs nothing.165 neferbar draws at most as often as your monitor refreshes. If a script prints faster than that, only the latest frame is drawn. A script that prints nothing costs nothing.
144 166
167+### The bundled bar
168+
169+`examples/bundle` is a ready-made bar: workspaces on the left, the focused app in the middle, the clock on the right, in your terminal's colors.
170+
171+![The bundled bar](docs/img/bundle.png)
172+ Copy it to `~/.config/neferbar/` (the install steps are at the top of `examples/bundle/config.toml`).
173+
174+Its helper `lib.sh` has what a themed script needs: `fg`/`bg` to set a color from `#rrggbb`, `mix` to blend two, `fade` for the soft edge, and `state_changed_loop` to react to NeferWL. Read the three scripts: each is about thirty lines.
175+
176+On NeferWL the workspaces show one dot per window and the stash. On other compositors the workspace and focus modules show nothing yet, and the clock still works.
177+
178+### Reacting to your compositor (workspace effects)
179+
180+A module can watch anything, including the compositor's own state. NeferWL keeps a JSON file up to date and passes its path to every program it starts as `$NEFERWL_STATE`. It replaces the file on each change, so a module only has to check the file's modification time:
181+
182+```sh
183+stamp=$(mktemp)
184+touch -r "$NEFERWL_STATE" "$stamp"
185+while sleep 0.1; do
186+ if [[ $NEFERWL_STATE -nt $stamp ]]; then # the file changed
187+ touch -r "$NEFERWL_STATE" "$stamp"
188+ jq -r '.outputs[] | "\(.name) is on workspace \(.active)"' "$NEFERWL_STATE"
189+ fi
190+done
191+```
192+
193+That is all it takes to draw an effect when you switch workspace. `examples/bundle/left.sh` redraws the workspaces that way.
194+
145 ### Example modules195 ### Example modules
146 196
147-The `examples/modules` directory has three:197+The `examples/modules` directory has these:
148 198
149 - `static.sh`: one line, then idle.199 - `static.sh`: one line, then idle.
150 - `clock.sh`: a clock with an icon.200 - `clock.sh`: a clock with an icon.
A docs/img/bundle.png +0 -0 binary file: no text diff

binary file: no text diff

A examples/bundle/center.sh +21 -0
@@ -0,0 +1,21 @@
1+#!/bin/bash
2+# Center: the focused window. NeferWL's state file has the app id but no title;
3+# a title needs the compositor's toplevel protocol, which the bar does not read yet.
4+. "$(dirname "$0")/lib.sh"
5+
6+[ -r "${NEFERWL_STATE:-}" ] || {
7+ echo
8+ exec sleep infinity
9+}
10+
11+draw() {
12+ local app
13+ app=$(jq -r '.window.app_id // empty' "$NEFERWL_STATE" 2>/dev/null) || return
14+ if [ -n "$app" ]; then
15+ printf '%s%s%s %s%s%s\f' "$(bg "$BAR")" "$(fg "$NEFERBAR_COLOR3")" "$ICON_APP" "$(fg "$NEFERBAR_FOREGROUND")" "$app" "$RESET"
16+ else
17+ printf '\f'
18+ fi
19+}
20+
21+state_changed_loop draw
@@ -0,0 +1,21 @@
1+#!/bin/bash
2+# Center: the focused window. NeferWL's state file has the app id but no title;
3+# a title needs the compositor's toplevel protocol, which the bar does not read yet.
4+. "$(dirname "$0")/lib.sh"
5+
6+[ -r "${NEFERWL_STATE:-}" ] || {
7+ echo
8+ exec sleep infinity
9+}
10+
11+draw() {
12+ local app
13+ app=$(jq -r '.window.app_id // empty' "$NEFERWL_STATE" 2>/dev/null) || return
14+ if [ -n "$app" ]; then
15+ printf '%s%s%s %s%s%s\f' "$(bg "$BAR")" "$(fg "$NEFERBAR_COLOR3")" "$ICON_APP" "$(fg "$NEFERBAR_FOREGROUND")" "$app" "$RESET"
16+ else
17+ printf '\f'
18+ fi
19+}
20+
21+state_changed_loop draw
A examples/bundle/config.toml +27 -0
@@ -0,0 +1,27 @@
1+# The bundled bar: workspaces on the left, the focused app in the middle, the
2+# clock on the right. Colors come from the terminal you use.
3+#
4+# Install:
5+# mkdir -p ~/.config/neferbar
6+# cp -r examples/bundle ~/.config/neferbar/
7+# cp examples/bundle/config.toml ~/.config/neferbar/config.toml
8+
9+[bar]
10+font = "JetBrainsMono Nerd Font Mono" # any "Mono" Nerd Font
11+size = 14
12+theme = "auto" # the colors of your terminal, or the path of a theme file
13+
14+[[module]]
15+name = "workspaces"
16+zone = "left"
17+exec = "~/.config/neferbar/bundle/left.sh"
18+
19+[[module]]
20+name = "focus"
21+zone = "center"
22+exec = "~/.config/neferbar/bundle/center.sh"
23+
24+[[module]]
25+name = "clock"
26+zone = "right"
27+exec = "~/.config/neferbar/bundle/right.sh"
@@ -0,0 +1,27 @@
1+# The bundled bar: workspaces on the left, the focused app in the middle, the
2+# clock on the right. Colors come from the terminal you use.
3+#
4+# Install:
5+# mkdir -p ~/.config/neferbar
6+# cp -r examples/bundle ~/.config/neferbar/
7+# cp examples/bundle/config.toml ~/.config/neferbar/config.toml
8+
9+[bar]
10+font = "JetBrainsMono Nerd Font Mono" # any "Mono" Nerd Font
11+size = 14
12+theme = "auto" # the colors of your terminal, or the path of a theme file
13+
14+[[module]]
15+name = "workspaces"
16+zone = "left"
17+exec = "~/.config/neferbar/bundle/left.sh"
18+
19+[[module]]
20+name = "focus"
21+zone = "center"
22+exec = "~/.config/neferbar/bundle/center.sh"
23+
24+[[module]]
25+name = "clock"
26+zone = "right"
27+exec = "~/.config/neferbar/bundle/right.sh"
A examples/bundle/left.sh +46 -0
@@ -0,0 +1,46 @@
1+#!/bin/bash
2+# Left: the active workspace as an accent block that fades into the bar, then
3+# the other workspaces with one dot per window. On NeferWL it also shows the
4+# stash. Without NeferWL's state file it shows nothing.
5+. "$(dirname "$0")/lib.sh"
6+
7+DOT=$ICON_DOT STASH=$ICON_STASH
8+[ -r "${NEFERWL_STATE:-}" ] || {
9+ echo
10+ exec sleep infinity
11+}
12+
13+draw() {
14+ # First line: "<active> <count>" of the focused output. Then one line per
15+ # workspace: "<n> <windows> <stashed>".
16+ local data active count n w s line pips
17+ data=$(jq -r '
18+ .output as $o
19+ | (.outputs[] | select(.name == $o)) as $out
20+ | [.windows[] | select(.output == $o)] as $ws
21+ | "\($out.active) \($out.count)",
22+ (range(1; $out.count + 1) as $n
23+ | [$ws[] | select(.workspace == $n)] as $here
24+ | "\($n) \($here | length) \([$here[] | select(.stash_count > 0)] | length)")' \
25+ "$NEFERWL_STATE" 2>/dev/null) || return
26+ read -r active count <<<"$(head -1 <<<"$data")"
27+ [ -n "$active" ] || return
28+
29+ line="$(bg "$ACCENT")$(fg "$(ink "$ACCENT")")$BOLD $active $RESET$(fade "$ACCENT" "$BAR")$(bg "$BAR")"
30+ while read -r n w s; do
31+ pips=""
32+ ((w > 0)) && printf -v pips "$DOT%.0s" $(seq 1 $((w > 4 ? 4 : w)))
33+ if ((n == active)); then
34+ line+="$(fg "$NEFERBAR_FOREGROUND") $n $(fg "$ACCENT")$pips"
35+ ((s > 0)) && line+="$(fg "$NEFERBAR_COLOR2") $STASH$s"
36+ line+=" "
37+ elif ((w > 0)); then
38+ line+="$(fg "$NEFERBAR_FOREGROUND") $n $(fg "$DIM")$pips "
39+ else
40+ line+="$(fg "$DIM") $n "
41+ fi
42+ done < <(tail -n +2 <<<"$data")
43+ printf '%s%s\f' "$line" "$RESET"
44+}
45+
46+state_changed_loop draw
@@ -0,0 +1,46 @@
1+#!/bin/bash
2+# Left: the active workspace as an accent block that fades into the bar, then
3+# the other workspaces with one dot per window. On NeferWL it also shows the
4+# stash. Without NeferWL's state file it shows nothing.
5+. "$(dirname "$0")/lib.sh"
6+
7+DOT=$ICON_DOT STASH=$ICON_STASH
8+[ -r "${NEFERWL_STATE:-}" ] || {
9+ echo
10+ exec sleep infinity
11+}
12+
13+draw() {
14+ # First line: "<active> <count>" of the focused output. Then one line per
15+ # workspace: "<n> <windows> <stashed>".
16+ local data active count n w s line pips
17+ data=$(jq -r '
18+ .output as $o
19+ | (.outputs[] | select(.name == $o)) as $out
20+ | [.windows[] | select(.output == $o)] as $ws
21+ | "\($out.active) \($out.count)",
22+ (range(1; $out.count + 1) as $n
23+ | [$ws[] | select(.workspace == $n)] as $here
24+ | "\($n) \($here | length) \([$here[] | select(.stash_count > 0)] | length)")' \
25+ "$NEFERWL_STATE" 2>/dev/null) || return
26+ read -r active count <<<"$(head -1 <<<"$data")"
27+ [ -n "$active" ] || return
28+
29+ line="$(bg "$ACCENT")$(fg "$(ink "$ACCENT")")$BOLD $active $RESET$(fade "$ACCENT" "$BAR")$(bg "$BAR")"
30+ while read -r n w s; do
31+ pips=""
32+ ((w > 0)) && printf -v pips "$DOT%.0s" $(seq 1 $((w > 4 ? 4 : w)))
33+ if ((n == active)); then
34+ line+="$(fg "$NEFERBAR_FOREGROUND") $n $(fg "$ACCENT")$pips"
35+ ((s > 0)) && line+="$(fg "$NEFERBAR_COLOR2") $STASH$s"
36+ line+=" "
37+ elif ((w > 0)); then
38+ line+="$(fg "$NEFERBAR_FOREGROUND") $n $(fg "$DIM")$pips "
39+ else
40+ line+="$(fg "$DIM") $n "
41+ fi
42+ done < <(tail -n +2 <<<"$data")
43+ printf '%s%s\f' "$line" "$RESET"
44+}
45+
46+state_changed_loop draw
A examples/bundle/lib.sh +75 -0
@@ -0,0 +1,75 @@
1+# Shared helpers for the bundled modules. Source this file: . "$(dirname "$0")/lib.sh"
2+#
3+# The bar gives every script its colors, each as #rrggbb, so a script never
4+# hard-codes one:
5+# NEFERBAR_BACKGROUND the bar's own background
6+# NEFERBAR_FOREGROUND the bar's text color
7+# NEFERBAR_COLOR0..15 the theme's 16 ANSI colors
8+
9+: "${NEFERBAR_BACKGROUND:=#26263a}" "${NEFERBAR_FOREGROUND:=#cdd6f4}"
10+: "${NEFERBAR_COLOR0:=#45475a}" "${NEFERBAR_COLOR2:=#a6e3a1}" "${NEFERBAR_COLOR3:=#f9e2af}"
11+: "${NEFERBAR_COLOR4:=#89b4fa}" "${NEFERBAR_COLOR15:=#a6adc8}"
12+
13+RESET=$'\e[0m' BOLD=$'\e[1m'
14+
15+# Nerd Font icons as raw UTF-8 bytes. Unlike \u escapes, these do not depend on
16+# the locale of the process that runs the script.
17+ICON_DOT=$'\xef\x84\x91' # U+F111 circle
18+ICON_STASH=$'\xef\x86\x87' # U+F187 archive
19+ICON_APP=$'\xef\x8b\x90' # U+F2D0 window
20+ICON_CLOCK=$'\xef\x80\x97' # U+F017 clock
21+
22+# rgb "#rrggbb" -> "r;g;b"
23+rgb() {
24+ local h=${1#\#}
25+ printf '%d;%d;%d' "0x${h:0:2}" "0x${h:2:2}" "0x${h:4:2}"
26+}
27+fg() { printf '\e[38;2;%sm' "$(rgb "$1")"; }
28+bg() { printf '\e[48;2;%sm' "$(rgb "$1")"; }
29+
30+# mix "#a" "#b" PERCENT -> "#rrggbb", PERCENT of the way from a to b
31+mix() {
32+ local a=${1#\#} b=${2#\#} p=$3 i out=#
33+ for i in 0 2 4; do
34+ out+=$(printf '%02x' $(((0x${a:i:2} * (100 - p) + 0x${b:i:2} * p) / 100)))
35+ done
36+ printf '%s' "$out"
37+}
38+
39+# fade FROM TO -> four blank cells that blend FROM into TO
40+fade() {
41+ local i
42+ for i in 20 40 60 80; do printf '%s ' "$(bg "$(mix "$1" "$2" "$i")")"; done
43+}
44+
45+BAR=$NEFERBAR_BACKGROUND
46+ACCENT=$NEFERBAR_COLOR4
47+DIM=$(mix "$BAR" "$NEFERBAR_FOREGROUND" 45)
48+
49+# ink "#accent" -> the text color for a block of that color: the theme's black
50+# on a light accent, its white on a dark one.
51+ink() {
52+ local h=${1#\#}
53+ if (((0x${h:0:2} * 299 + 0x${h:2:2} * 587 + 0x${h:4:2} * 114) / 1000 > 140)); then
54+ printf '%s' "$NEFERBAR_COLOR0"
55+ else
56+ printf '%s' "$NEFERBAR_COLOR15"
57+ fi
58+}
59+
60+# state_changed_loop CMD... runs CMD now and again whenever $NEFERWL_STATE
61+# (NeferWL's state file) changes. NeferWL replaces the file on every change, so
62+# a newer modification time is enough, and "read -t" waits without forking.
63+state_changed_loop() {
64+ local stamp tick
65+ stamp=$(mktemp) && trap 'rm -f "$stamp"' EXIT
66+ exec {tick}<> <(:)
67+ "$@"
68+ touch -r "$NEFERWL_STATE" "$stamp" 2>/dev/null
69+ while read -r -t 0.1 -u "$tick" || true; do
70+ if [[ $NEFERWL_STATE -nt $stamp ]]; then
71+ touch -r "$NEFERWL_STATE" "$stamp"
72+ "$@"
73+ fi
74+ done
75+}
@@ -0,0 +1,75 @@
1+# Shared helpers for the bundled modules. Source this file: . "$(dirname "$0")/lib.sh"
2+#
3+# The bar gives every script its colors, each as #rrggbb, so a script never
4+# hard-codes one:
5+# NEFERBAR_BACKGROUND the bar's own background
6+# NEFERBAR_FOREGROUND the bar's text color
7+# NEFERBAR_COLOR0..15 the theme's 16 ANSI colors
8+
9+: "${NEFERBAR_BACKGROUND:=#26263a}" "${NEFERBAR_FOREGROUND:=#cdd6f4}"
10+: "${NEFERBAR_COLOR0:=#45475a}" "${NEFERBAR_COLOR2:=#a6e3a1}" "${NEFERBAR_COLOR3:=#f9e2af}"
11+: "${NEFERBAR_COLOR4:=#89b4fa}" "${NEFERBAR_COLOR15:=#a6adc8}"
12+
13+RESET=$'\e[0m' BOLD=$'\e[1m'
14+
15+# Nerd Font icons as raw UTF-8 bytes. Unlike \u escapes, these do not depend on
16+# the locale of the process that runs the script.
17+ICON_DOT=$'\xef\x84\x91' # U+F111 circle
18+ICON_STASH=$'\xef\x86\x87' # U+F187 archive
19+ICON_APP=$'\xef\x8b\x90' # U+F2D0 window
20+ICON_CLOCK=$'\xef\x80\x97' # U+F017 clock
21+
22+# rgb "#rrggbb" -> "r;g;b"
23+rgb() {
24+ local h=${1#\#}
25+ printf '%d;%d;%d' "0x${h:0:2}" "0x${h:2:2}" "0x${h:4:2}"
26+}
27+fg() { printf '\e[38;2;%sm' "$(rgb "$1")"; }
28+bg() { printf '\e[48;2;%sm' "$(rgb "$1")"; }
29+
30+# mix "#a" "#b" PERCENT -> "#rrggbb", PERCENT of the way from a to b
31+mix() {
32+ local a=${1#\#} b=${2#\#} p=$3 i out=#
33+ for i in 0 2 4; do
34+ out+=$(printf '%02x' $(((0x${a:i:2} * (100 - p) + 0x${b:i:2} * p) / 100)))
35+ done
36+ printf '%s' "$out"
37+}
38+
39+# fade FROM TO -> four blank cells that blend FROM into TO
40+fade() {
41+ local i
42+ for i in 20 40 60 80; do printf '%s ' "$(bg "$(mix "$1" "$2" "$i")")"; done
43+}
44+
45+BAR=$NEFERBAR_BACKGROUND
46+ACCENT=$NEFERBAR_COLOR4
47+DIM=$(mix "$BAR" "$NEFERBAR_FOREGROUND" 45)
48+
49+# ink "#accent" -> the text color for a block of that color: the theme's black
50+# on a light accent, its white on a dark one.
51+ink() {
52+ local h=${1#\#}
53+ if (((0x${h:0:2} * 299 + 0x${h:2:2} * 587 + 0x${h:4:2} * 114) / 1000 > 140)); then
54+ printf '%s' "$NEFERBAR_COLOR0"
55+ else
56+ printf '%s' "$NEFERBAR_COLOR15"
57+ fi
58+}
59+
60+# state_changed_loop CMD... runs CMD now and again whenever $NEFERWL_STATE
61+# (NeferWL's state file) changes. NeferWL replaces the file on every change, so
62+# a newer modification time is enough, and "read -t" waits without forking.
63+state_changed_loop() {
64+ local stamp tick
65+ stamp=$(mktemp) && trap 'rm -f "$stamp"' EXIT
66+ exec {tick}<> <(:)
67+ "$@"
68+ touch -r "$NEFERWL_STATE" "$stamp" 2>/dev/null
69+ while read -r -t 0.1 -u "$tick" || true; do
70+ if [[ $NEFERWL_STATE -nt $stamp ]]; then
71+ touch -r "$NEFERWL_STATE" "$stamp"
72+ "$@"
73+ fi
74+ done
75+}
A examples/bundle/right.sh +10 -0
@@ -0,0 +1,10 @@
1+#!/bin/bash
2+# Right: the clock in an accent block, with a fade on its left edge.
3+. "$(dirname "$0")/lib.sh"
4+
5+while true; do
6+ printf '%s%s%s%s%s %(%H:%M)T %s\f' \
7+ "$(fade "$BAR" "$ACCENT")" "$(bg "$ACCENT")" "$(fg "$(ink "$ACCENT")")" "$BOLD" "$ICON_CLOCK" -1 "$RESET"
8+ # Sleep until the next minute starts.
9+ sleep $((60 - 10#$(date +%S)))
10+done
@@ -0,0 +1,10 @@
1+#!/bin/bash
2+# Right: the clock in an accent block, with a fade on its left edge.
3+. "$(dirname "$0")/lib.sh"
4+
5+while true; do
6+ printf '%s%s%s%s%s %(%H:%M)T %s\f' \
7+ "$(fade "$BAR" "$ACCENT")" "$(bg "$ACCENT")" "$(fg "$(ink "$ACCENT")")" "$BOLD" "$ICON_CLOCK" -1 "$RESET"
8+ # Sleep until the next minute starts.
9+ sleep $((60 - 10#$(date +%S)))
10+done

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 5

Tags 4

↑↓ moveenter openesc close