pi-sync-system-theme 0.2.0 → 0.2.2

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.
Files changed (3) hide show
  1. package/README.md +37 -1
  2. package/index.ts +23 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -33,6 +33,17 @@ pi install npm:pi-sync-system-theme
33
33
  > pi remove npm:pi-system-theme
34
34
  > ```
35
35
 
36
+ ## Package rename migration
37
+
38
+ This package was renamed from `pi-system-theme-ssh-bridge` to `pi-sync-system-theme`.
39
+
40
+ If you installed the old package name, run:
41
+
42
+ ```bash
43
+ pi remove npm:pi-system-theme-ssh-bridge
44
+ pi install npm:pi-sync-system-theme
45
+ ```
46
+
36
47
  ## Configuration
37
48
 
38
49
  Use the `/system-theme` command inside pi to configure:
@@ -67,6 +78,30 @@ For environments where neither OSC 11 nor OS detection works, you can push an ov
67
78
  }
68
79
  ```
69
80
 
81
+ ## ⚠️ Performance tuning notes (important)
82
+
83
+ This extension queries terminal background color (OSC 11) in SSH/tmux sessions. Aggressive polling can cause terminal artifacts (garbled startup output) or input lag on some terminal/SSH combinations.
84
+
85
+ Recommended ranges:
86
+
87
+ - `pollMs`: **3000–8000** (default `8000`)
88
+ - `PI_SYSTEM_THEME_OSC11_MIN_INTERVAL_MS`: **8000–15000** (default `15000`)
89
+
90
+ Avoid overly aggressive values unless you have tested your environment thoroughly:
91
+
92
+ - `pollMs < 2000`
93
+ - `PI_SYSTEM_THEME_OSC11_MIN_INTERVAL_MS < 5000`
94
+
95
+ If you notice lag, slash-command stutter, or startup artifacts:
96
+
97
+ 1. Increase `pollMs`
98
+ 2. Increase `PI_SYSTEM_THEME_OSC11_MIN_INTERVAL_MS`
99
+ 3. Temporarily disable OSC11 probing with:
100
+
101
+ ```bash
102
+ export PI_SYSTEM_THEME_OSC11_ENABLED=0
103
+ ```
104
+
70
105
  ## Environment variables
71
106
 
72
107
  | Variable | Default | Description |
@@ -74,13 +109,14 @@ For environments where neither OSC 11 nor OS detection works, you can push an ov
74
109
  | `PI_SYSTEM_THEME_OVERRIDE_FILE` | `~/.pi/agent/system-theme-override.json` | Override file path |
75
110
  | `PI_SYSTEM_THEME_OVERRIDE_MAX_AGE_MS` | `60000` | Max age before override is considered stale |
76
111
  | `PI_SYSTEM_THEME_OSC11_ENABLED` | `1` | Enable/disable OSC 11 terminal query (`0` to disable) |
77
- | `PI_SYSTEM_THEME_OSC11_MIN_INTERVAL_MS` | `15000` | Minimum interval between OSC 11 probes in SSH sessions |
112
+ | `PI_SYSTEM_THEME_OSC11_MIN_INTERVAL_MS` | `15000` | Minimum interval between OSC 11 probes in SSH/tmux sessions |
78
113
 
79
114
  ## Compatibility
80
115
 
81
116
  - **Terminals:** Any terminal supporting OSC 11 color queries (Ghostty, iTerm2, kitty, foot, WezTerm, xterm, etc.)
82
117
  - **OS detection:** macOS, Linux (GNOME gsettings), Windows
83
118
  - **SSH:** Works transparently — no special setup required
119
+ - **tmux:** Supported (including long-lived sessions where `SSH_*` env vars may be missing)
84
120
  - **Ghostty `theme = auto`:** Fully supported. When Ghostty switches colors, the next poll detects it.
85
121
 
86
122
  ## Migrating from pi-system-theme
package/index.ts CHANGED
@@ -116,6 +116,10 @@ function isSSHSession(): boolean {
116
116
  return !!(process.env.SSH_CONNECTION || process.env.SSH_TTY || process.env.SSH_CLIENT);
117
117
  }
118
118
 
119
+ function isTmuxSession(): boolean {
120
+ return typeof process.env.TMUX === "string" && process.env.TMUX.length > 0;
121
+ }
122
+
119
123
  // ---------------------------------------------------------------------------
120
124
  // Config I/O (reads ~/.pi/agent/system-theme.json written by /system-theme)
121
125
  // ---------------------------------------------------------------------------
@@ -212,8 +216,9 @@ let fd;
212
216
  try { fd = fs.openSync('/dev/tty', fs.constants.O_RDWR | fs.constants.O_NOCTTY | O_NONBLOCK); }
213
217
  catch { process.exit(1); }
214
218
 
215
- // Send OSC 11 query (BEL terminator is widely supported)
216
- try { fs.writeSync(fd, '\x1b]11;?\x07'); }
219
+ // Send OSC 11 query.
220
+ // Use ST terminator for better compatibility with tmux passthrough.
221
+ try { fs.writeSync(fd, '\x1b]11;?\x1b\\'); }
217
222
  catch { try { fs.closeSync(fd); } catch {} process.exit(1); }
218
223
 
219
224
  const buf = Buffer.alloc(1024);
@@ -235,13 +240,21 @@ function tryRead() {
235
240
  }
236
241
  }
237
242
 
243
+ function to8Bit(hex) {
244
+ if (!hex) return 0;
245
+ const h = String(hex);
246
+ if (h.length <= 2) return parseInt(h.padEnd(2, h[h.length - 1] || '0'), 16);
247
+ return parseInt(h.slice(0, 2), 16);
248
+ }
249
+
238
250
  function done() {
239
251
  try { fs.closeSync(fd); } catch {}
240
- const m = response.match(/\x1b\]11;rgb:([0-9a-fA-F]+)\\/([0-9a-fA-F]+)\\/([0-9a-fA-F]+)(?:\x07|\x1b\\\\)/);
252
+ // Keep parser permissive: tmux may wrap control sequences, but rgb payload remains stable.
253
+ const m = response.match(/rgb:([0-9a-fA-F]{2,4})\\/([0-9a-fA-F]{2,4})\\/([0-9a-fA-F]{2,4})/);
241
254
  if (m) {
242
- const r = parseInt(m[1].slice(0, 2), 16);
243
- const g = parseInt(m[2].slice(0, 2), 16);
244
- const b = parseInt(m[3].slice(0, 2), 16);
255
+ const r = to8Bit(m[1]);
256
+ const g = to8Bit(m[2]);
257
+ const b = to8Bit(m[3]);
245
258
  const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
246
259
  process.stdout.write(luminance < 128 ? 'dark' : 'light');
247
260
  }
@@ -250,7 +263,7 @@ function done() {
250
263
 
251
264
  function poll() {
252
265
  tryRead();
253
- if (response.includes('\x1b]11;') || Date.now() > deadline) return done();
266
+ if (response.includes('rgb:') || Date.now() > deadline) return done();
254
267
  setTimeout(poll, 16);
255
268
  }
256
269
 
@@ -406,8 +419,9 @@ async function resolveAppearance(config: Config, osc11State: Osc11State): Promis
406
419
  if (override === "dark" || override === "light") return override;
407
420
  // "auto" or null → continue
408
421
 
409
- // 2. Terminal query via OSC 11 (SSH only, throttled)
410
- if (isSSHSession() && isOsc11Enabled()) {
422
+ // 2. Terminal query via OSC 11 (SSH/tmux, throttled)
423
+ // tmux sessions may not carry SSH_* vars, so include TMUX explicitly.
424
+ if ((isSSHSession() || isTmuxSession()) && isOsc11Enabled()) {
411
425
  const now = Date.now();
412
426
  const minIntervalMs = getOsc11MinIntervalMs();
413
427
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sync-system-theme",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Sync pi theme with system appearance — works locally and over SSH via OSC 11 terminal queries",
5
5
  "keywords": [
6
6
  "pi-package",