ghostty-bg 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -21
- package/bin/gbg.js +54 -18
- package/package.json +1 -1
- package/src/paths.js +47 -0
- package/src/config.js +0 -57
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# ghostty-bg (`gbg`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Per-directory background colors for Ghostty — one command per project.
|
|
4
4
|
|
|
5
5
|
https://github.com/user-attachments/assets/28d9a5a6-a82c-4da5-b39d-c8d9bf58036f
|
|
6
6
|
|
|
7
7
|
`gbg` is a tiny, zero-dependency Node CLI. It sends the standard
|
|
8
8
|
[OSC 11](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html) escape
|
|
9
|
-
sequence to recolor the current window instantly, and
|
|
10
|
-
|
|
11
|
-
or special API required.
|
|
9
|
+
sequence to recolor the current window instantly, and remembers the color per
|
|
10
|
+
directory. With the shell hook installed, `cd` into a directory and its color
|
|
11
|
+
is applied automatically — no Ghostty fork or special API required.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -25,13 +25,14 @@ npx ghostty-bg --color dracula
|
|
|
25
25
|
## Usage
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
gbg # random color
|
|
28
|
+
gbg # random color, remembered for this directory
|
|
29
29
|
gbg --color dracula # a named theme
|
|
30
30
|
gbg --color "#1a2b3c" # a hex value (#rgb or #rrggbb)
|
|
31
31
|
gbg teal # shorthand for --color
|
|
32
|
-
gbg --no-save tomato # current window only,
|
|
33
|
-
gbg --reset #
|
|
32
|
+
gbg --no-save tomato # current window only, not remembered
|
|
33
|
+
gbg --reset # forget this directory's color, reset background
|
|
34
34
|
gbg --list # list available color names
|
|
35
|
+
gbg shell-init # print the zsh cd hook
|
|
35
36
|
gbg --help # show help
|
|
36
37
|
gbg --version # show version
|
|
37
38
|
```
|
|
@@ -51,29 +52,35 @@ Short flags: `-c` (color), `-n` (no-save), `-r` (reset), `-l` (list),
|
|
|
51
52
|
Running `gbg` with no arguments picks a random color from a curated palette of
|
|
52
53
|
dark, readable backgrounds.
|
|
53
54
|
|
|
54
|
-
##
|
|
55
|
+
## Per-directory colors
|
|
55
56
|
|
|
56
57
|
By default `gbg` does two things:
|
|
57
58
|
|
|
58
59
|
1. Recolors the **current window** immediately via OSC 11.
|
|
59
|
-
2.
|
|
60
|
+
2. Remembers the color for the **current directory**.
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`config-file = .../gbg.conf` line to your main `config` (only that one line is
|
|
64
|
-
ever added — the color itself lives only in `gbg.conf`).
|
|
62
|
+
To make `cd` apply the remembered color automatically, add the hook to your
|
|
63
|
+
shell:
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
```sh
|
|
66
|
+
echo 'eval "$(gbg shell-init)"' >> ~/.zshrc
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
After that:
|
|
70
|
+
|
|
71
|
+
- `cd` into a remembered directory → its color is applied.
|
|
72
|
+
- `cd` anywhere else → the background returns to your theme default.
|
|
73
|
+
- `gbg --no-save` applies to the current window without remembering it.
|
|
74
|
+
- `gbg --reset` forgets the current directory's color.
|
|
75
|
+
|
|
76
|
+
The hook is plain zsh and reads a small `paths` file with `awk`, so it adds no
|
|
77
|
+
Node startup cost on `cd`. Colors are matched by exact directory.
|
|
71
78
|
|
|
72
79
|
## How It Works
|
|
73
80
|
|
|
74
|
-
`gbg` writes `ESC ] 11 ; <color> BEL` to the terminal to set the
|
|
75
|
-
|
|
76
|
-
`
|
|
81
|
+
`gbg` writes `ESC ] 11 ; <color> BEL` to the terminal to set the background and
|
|
82
|
+
`ESC ] 111 BEL` to reset it. Remembered colors live in
|
|
83
|
+
`${XDG_CONFIG_HOME:-~/.config}/gbg/paths` as `<dir><TAB><#hex>` lines.
|
|
77
84
|
|
|
78
85
|
## Requirements
|
|
79
86
|
|
package/bin/gbg.js
CHANGED
|
@@ -9,41 +9,61 @@ import {
|
|
|
9
9
|
UnknownColorError,
|
|
10
10
|
} from "../src/colors.js";
|
|
11
11
|
import { setBackground, resetBackground } from "../src/terminal.js";
|
|
12
|
-
import {
|
|
12
|
+
import { setPathColor, clearPathColor, pathsFile } from "../src/paths.js";
|
|
13
13
|
|
|
14
14
|
function readVersion() {
|
|
15
15
|
const pkgPath = fileURLToPath(new URL("../package.json", import.meta.url));
|
|
16
16
|
return JSON.parse(readFileSync(pkgPath, "utf8")).version;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const HELP = `gbg —
|
|
19
|
+
const HELP = `gbg — per-directory background colors for Ghostty (& any OSC 11 terminal)
|
|
20
20
|
|
|
21
21
|
Usage:
|
|
22
|
-
gbg Apply a random color
|
|
23
|
-
gbg --color <name|hex> Apply a specific color (name or #hex)
|
|
22
|
+
gbg Apply a random color and remember it for this directory
|
|
23
|
+
gbg --color <name|hex> Apply a specific color (name or #hex) and remember it
|
|
24
24
|
gbg <name|hex> Shorthand for --color
|
|
25
|
-
gbg --no-save Apply to the current
|
|
26
|
-
gbg --reset
|
|
25
|
+
gbg --no-save Apply to the current window only (don't remember)
|
|
26
|
+
gbg --reset Forget this directory's color and reset the background
|
|
27
27
|
gbg --list List available color names
|
|
28
|
+
gbg shell-init Print the zsh hook that switches color on cd
|
|
28
29
|
gbg --help Show this help
|
|
29
30
|
gbg --version Show version
|
|
30
31
|
|
|
31
32
|
Examples:
|
|
32
|
-
gbg
|
|
33
|
-
gbg --color dracula
|
|
34
|
-
gbg -c "#1a2b3c"
|
|
33
|
+
gbg --color dracula # this directory is dracula from now on
|
|
35
34
|
gbg teal
|
|
36
|
-
gbg --no-save tomato
|
|
35
|
+
gbg --no-save tomato # one-off, not remembered
|
|
37
36
|
gbg --reset
|
|
38
37
|
|
|
38
|
+
Per-directory colors:
|
|
39
|
+
By default gbg remembers the color for the current directory. Add the cd hook
|
|
40
|
+
to your shell so it switches automatically:
|
|
41
|
+
|
|
42
|
+
echo 'eval "$(gbg shell-init)"' >> ~/.zshrc
|
|
43
|
+
|
|
44
|
+
Then cd into a remembered directory and its color is applied; cd elsewhere and
|
|
45
|
+
the background returns to your theme default.
|
|
46
|
+
|
|
39
47
|
Colors:
|
|
40
48
|
- Named themes (dracula, nord, gruvbox, tokyonight, ...)
|
|
41
49
|
- CSS color names (red, teal, midnightblue, ...)
|
|
42
|
-
- Hex: #rgb or #rrggbb (with or without the leading #)
|
|
50
|
+
- Hex: #rgb or #rrggbb (with or without the leading #)`;
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
const SHELL_INIT = `# gbg: per-directory background colors
|
|
53
|
+
_gbg_paths="\${XDG_CONFIG_HOME:-$HOME/.config}/gbg/paths"
|
|
54
|
+
_gbg_apply() {
|
|
55
|
+
[[ -t 1 ]] || return
|
|
56
|
+
local hex=""
|
|
57
|
+
[[ -r "$_gbg_paths" ]] && hex=$(command awk -F'\\t' -v p="$PWD" '$1==p{print $2; exit}' "$_gbg_paths")
|
|
58
|
+
if [[ -n "$hex" ]]; then
|
|
59
|
+
printf '\\033]11;%s\\007' "$hex"
|
|
60
|
+
else
|
|
61
|
+
printf '\\033]111\\007'
|
|
62
|
+
fi
|
|
63
|
+
}
|
|
64
|
+
autoload -Uz add-zsh-hook
|
|
65
|
+
add-zsh-hook chpwd _gbg_apply
|
|
66
|
+
_gbg_apply`;
|
|
47
67
|
|
|
48
68
|
function printList() {
|
|
49
69
|
const { themes, css } = colorNames();
|
|
@@ -76,6 +96,10 @@ function main() {
|
|
|
76
96
|
|
|
77
97
|
const { values, positionals } = parsed;
|
|
78
98
|
|
|
99
|
+
if (positionals[0] === "shell-init") {
|
|
100
|
+
console.log(SHELL_INIT);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
79
103
|
if (values.help) {
|
|
80
104
|
console.log(HELP);
|
|
81
105
|
return;
|
|
@@ -88,10 +112,22 @@ function main() {
|
|
|
88
112
|
printList();
|
|
89
113
|
return;
|
|
90
114
|
}
|
|
115
|
+
|
|
116
|
+
// Match the shell's logical $PWD (which the cd hook uses) rather than the
|
|
117
|
+
// symlink-resolved physical path, so keys line up across symlinked dirs.
|
|
118
|
+
const cwd =
|
|
119
|
+
process.env.PWD && process.env.PWD.startsWith("/")
|
|
120
|
+
? process.env.PWD
|
|
121
|
+
: process.cwd();
|
|
122
|
+
|
|
91
123
|
if (values.reset) {
|
|
92
124
|
resetBackground();
|
|
93
|
-
|
|
94
|
-
console.error(
|
|
125
|
+
const had = clearPathColor(cwd);
|
|
126
|
+
console.error(
|
|
127
|
+
had
|
|
128
|
+
? `gbg: forgot color for ${cwd} and reset the background`
|
|
129
|
+
: "gbg: background reset to theme default",
|
|
130
|
+
);
|
|
95
131
|
return;
|
|
96
132
|
}
|
|
97
133
|
|
|
@@ -116,8 +152,8 @@ function main() {
|
|
|
116
152
|
console.error(`gbg: background → ${label}`);
|
|
117
153
|
|
|
118
154
|
if (!values["no-save"]) {
|
|
119
|
-
|
|
120
|
-
console.error(`gbg:
|
|
155
|
+
setPathColor(cwd, result.hex);
|
|
156
|
+
console.error(`gbg: remembered for ${cwd} (applies when you cd here)`);
|
|
121
157
|
}
|
|
122
158
|
}
|
|
123
159
|
|
package/package.json
CHANGED
package/src/paths.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Per-directory color memory. Stored as a simple tab-separated file
|
|
2
|
+
// (`<abs-dir>\t<#hex>` per line) so the zsh cd-hook can read it with awk
|
|
3
|
+
// without spawning Node on every directory change.
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
7
|
+
|
|
8
|
+
function gbgDir() {
|
|
9
|
+
const base = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
10
|
+
return join(base, "gbg");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function pathsFile() {
|
|
14
|
+
return join(gbgDir(), "paths");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function read() {
|
|
18
|
+
const file = pathsFile();
|
|
19
|
+
const map = new Map();
|
|
20
|
+
if (!existsSync(file)) return map;
|
|
21
|
+
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
22
|
+
if (!line) continue;
|
|
23
|
+
const tab = line.indexOf("\t");
|
|
24
|
+
if (tab < 0) continue;
|
|
25
|
+
map.set(line.slice(0, tab), line.slice(tab + 1));
|
|
26
|
+
}
|
|
27
|
+
return map;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function write(map) {
|
|
31
|
+
mkdirSync(gbgDir(), { recursive: true });
|
|
32
|
+
const body = [...map.entries()].map(([dir, hex]) => `${dir}\t${hex}`).join("\n");
|
|
33
|
+
writeFileSync(pathsFile(), body.length ? body + "\n" : "");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setPathColor(dir, hex) {
|
|
37
|
+
const map = read();
|
|
38
|
+
map.set(dir, hex);
|
|
39
|
+
write(map);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function clearPathColor(dir) {
|
|
43
|
+
const map = read();
|
|
44
|
+
const had = map.delete(dir);
|
|
45
|
+
if (had) write(map);
|
|
46
|
+
return had;
|
|
47
|
+
}
|
package/src/config.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// Persist the background across Ghostty restarts and new windows.
|
|
2
|
-
//
|
|
3
|
-
// gbg owns a small include file (gbg.conf) and makes the main Ghostty config
|
|
4
|
-
// load it via `config-file`. Only that one include line is ever added to the
|
|
5
|
-
// user's config; the color itself only ever lives in gbg.conf, so gbg never
|
|
6
|
-
// rewrites the rest of the user's configuration.
|
|
7
|
-
import { homedir } from "node:os";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
10
|
-
|
|
11
|
-
function ghosttyDir() {
|
|
12
|
-
const base = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
13
|
-
return join(base, "ghostty");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function gbgConfPath() {
|
|
17
|
-
return join(ghosttyDir(), "gbg.conf");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function mainConfigPath() {
|
|
21
|
-
return join(ghosttyDir(), "config");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const INCLUDE_MARKER = "# gbg: persist background across Ghostty restarts";
|
|
25
|
-
|
|
26
|
-
function ensureInclude() {
|
|
27
|
-
const dir = ghosttyDir();
|
|
28
|
-
mkdirSync(dir, { recursive: true });
|
|
29
|
-
|
|
30
|
-
const main = mainConfigPath();
|
|
31
|
-
const includeLine = `config-file = ${gbgConfPath()}`;
|
|
32
|
-
const content = existsSync(main) ? readFileSync(main, "utf8") : "";
|
|
33
|
-
if (content.includes(includeLine)) return;
|
|
34
|
-
|
|
35
|
-
// Appended last so gbg.conf's `background` overrides any theme background.
|
|
36
|
-
const sep = content.length && !content.endsWith("\n") ? "\n" : "";
|
|
37
|
-
writeFileSync(main, `${content}${sep}\n${INCLUDE_MARKER}\n${includeLine}\n`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function saveColor(hex) {
|
|
41
|
-
ensureInclude();
|
|
42
|
-
const body =
|
|
43
|
-
"# Managed by gbg — do not edit by hand.\n" +
|
|
44
|
-
"# Set with: gbg --color <name|hex> Clear with: gbg --reset\n" +
|
|
45
|
-
`background = ${hex}\n`;
|
|
46
|
-
writeFileSync(gbgConfPath(), body);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function clearColor() {
|
|
50
|
-
// Keep the file (emptied) so the config-file include never dangles.
|
|
51
|
-
if (!existsSync(gbgConfPath())) return;
|
|
52
|
-
writeFileSync(
|
|
53
|
-
gbgConfPath(),
|
|
54
|
-
"# Managed by gbg — no persisted background (cleared).\n" +
|
|
55
|
-
"# Set one with: gbg --color <name|hex>\n",
|
|
56
|
-
);
|
|
57
|
-
}
|