pi-sync-system-theme 0.2.3 → 0.2.5

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 +1 -0
  2. package/index.ts +26 -25
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -118,6 +118,7 @@ export PI_SYSTEM_THEME_OSC11_ENABLED=0
118
118
  - **SSH:** Works transparently — no special setup required
119
119
  - **tmux:** Supported (including long-lived sessions where `SSH_*` env vars may be missing)
120
120
  - **Ghostty `theme = auto`:** Fully supported. When Ghostty switches colors, the next poll detects it.
121
+ - **Session resume:** On `/resume`, the extension immediately re-checks appearance and reconciles the active theme.
121
122
 
122
123
  ## Migrating from pi-system-theme
123
124
 
package/index.ts CHANGED
@@ -61,7 +61,7 @@ const DEFAULT_CONFIG: Config = {
61
61
  const GLOBAL_CONFIG_PATH = path.join(os.homedir(), ".pi", "agent", "system-theme.json");
62
62
  const DETECTION_TIMEOUT_MS = 1200;
63
63
  const MIN_POLL_MS = 1000;
64
- const OSC11_QUERY_TIMEOUT_MS = 2200;
64
+ const OSC11_QUERY_TIMEOUT_MS = 3200;
65
65
  const OSC11_MIN_INTERVAL_MS = 15_000;
66
66
  const OSC11_DISABLE_AFTER_FAILURES = 3;
67
67
  const OSC11_DISABLE_COOLDOWN_MS = 60_000;
@@ -211,35 +211,23 @@ const OSC11_QUERY_SCRIPT = `
211
211
  'use strict';
212
212
  const fs = require('fs');
213
213
 
214
- const O_NONBLOCK = fs.constants.O_NONBLOCK ?? 0;
215
214
  let fd;
216
- try { fd = fs.openSync('/dev/tty', fs.constants.O_RDWR | fs.constants.O_NOCTTY | O_NONBLOCK); }
215
+ try { fd = fs.openSync('/dev/tty', fs.constants.O_RDWR | fs.constants.O_NOCTTY); }
217
216
  catch { process.exit(1); }
218
217
 
219
- // Send OSC 11 query. Emit both ST and BEL terminated variants for compatibility.
220
- try {
221
- fs.writeSync(fd, '\x1b]11;?\x1b\\');
222
- fs.writeSync(fd, '\x1b]11;?\x07');
223
- }
218
+ // Send OSC 11 query (ST terminator)
219
+ try { fs.writeSync(fd, '\x1b]11;?\x1b\\'); }
224
220
  catch { try { fs.closeSync(fd); } catch {} process.exit(1); }
225
221
 
226
222
  const buf = Buffer.alloc(1024);
227
223
  let response = '';
228
- const deadline = Date.now() + 1800;
224
+ const deadline = Date.now() + 2500;
229
225
 
230
226
  function tryRead() {
231
- while (true) {
232
- try {
233
- const n = fs.readSync(fd, buf, 0, buf.length, null);
234
- if (n <= 0) return;
235
- response += buf.toString('utf8', 0, n);
236
- if (response.length > 8192) response = response.slice(-4096);
237
- } catch (err) {
238
- const code = err && err.code;
239
- if (code === 'EAGAIN' || code === 'EWOULDBLOCK') return;
240
- return;
241
- }
242
- }
227
+ try {
228
+ const n = fs.readSync(fd, buf, 0, buf.length);
229
+ if (n > 0) response += buf.toString('utf8', 0, n);
230
+ } catch {}
243
231
  }
244
232
 
245
233
  function to8Bit(hex) {
@@ -251,7 +239,6 @@ function to8Bit(hex) {
251
239
 
252
240
  function done() {
253
241
  try { fs.closeSync(fd); } catch {}
254
- // Keep parser permissive: tmux may wrap control sequences, but rgb payload remains stable.
255
242
  const m = response.match(/rgb:([0-9a-fA-F]{2,8})\\/([0-9a-fA-F]{2,8})\\/([0-9a-fA-F]{2,8})/);
256
243
  if (m) {
257
244
  const r = to8Bit(m[1]);
@@ -266,7 +253,7 @@ function done() {
266
253
  function poll() {
267
254
  tryRead();
268
255
  if (response.includes('rgb:') || Date.now() > deadline) return done();
269
- setTimeout(poll, 16);
256
+ setTimeout(poll, 20);
270
257
  }
271
258
 
272
259
  poll();
@@ -640,20 +627,34 @@ export default function systemThemeBridge(pi: ExtensionAPI): void {
640
627
 
641
628
  // -- Lifecycle ------------------------------------------------------------
642
629
 
643
- pi.on("session_start", async (_event, ctx) => {
644
- config = await loadConfig();
630
+ function resetOsc11State(): void {
645
631
  osc11State.lastCheckedAt = 0;
646
632
  osc11State.lastAppearance = null;
647
633
  osc11State.failures = 0;
648
634
  osc11State.disabledUntil = 0;
635
+ }
636
+
637
+ async function applyOnSessionEnter(ctx: ExtensionContext): Promise<void> {
638
+ config = await loadConfig();
639
+ resetOsc11State();
649
640
 
650
641
  if (!shouldAutoSync(ctx)) {
651
642
  maybeWarnCustomTheme(ctx);
652
643
  return;
653
644
  }
654
645
 
646
+ // Force immediate theme reconciliation when entering a session
647
+ // (especially important after /resume from a differently-themed session).
655
648
  await tick(ctx);
656
649
  restartPolling(ctx);
650
+ }
651
+
652
+ pi.on("session_start", async (_event, ctx) => {
653
+ await applyOnSessionEnter(ctx);
654
+ });
655
+
656
+ pi.on("session_switch", async (_event, ctx) => {
657
+ await applyOnSessionEnter(ctx);
657
658
  });
658
659
 
659
660
  pi.on("session_shutdown", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sync-system-theme",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
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",