@jx0/jmux 0.3.4 → 0.3.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jx0/jmux",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "The terminal workspace for agentic development",
5
5
  "type": "module",
6
6
  "bin": {
package/src/main.ts CHANGED
@@ -12,7 +12,7 @@ import { homedir } from "os";
12
12
 
13
13
  // --- CLI commands (run and exit before TUI) ---
14
14
 
15
- const VERSION = "0.3.4";
15
+ const VERSION = "0.3.6";
16
16
 
17
17
  const HELP = `jmux — a persistent session sidebar for tmux
18
18
 
@@ -145,6 +145,7 @@ process.stdout.write("\x1b[?1049h");
145
145
  process.stdout.write("\x1b[?1000h"); // mouse button tracking
146
146
  process.stdout.write("\x1b[?1002h"); // mouse drag tracking
147
147
  process.stdout.write("\x1b[?1006h"); // SGR extended mouse mode
148
+ process.stdout.write("\x1b[?2004h"); // bracketed paste mode
148
149
  if (process.stdin.setRawMode) {
149
150
  process.stdin.setRawMode(true);
150
151
  }
@@ -313,17 +314,51 @@ const inputRouter = new InputRouter(
313
314
 
314
315
  let writesPending = 0;
315
316
 
316
- // OSC 52 clipboard: \x1b]52;...;...\x07 or \x1b]52;...;...\x1b\\
317
- const OSC52_RE = /\x1b\]52;[^;]*;[^\x07\x1b]*(?:\x07|\x1b\\)/g;
317
+ // OSC 52 clipboard passthrough buffers across split chunks
318
+ const OSC52_START = "\x1b]52;";
319
+ let osc52Pending = "";
320
+
321
+ function forwardOsc52(data: string): void {
322
+ let search = osc52Pending ? osc52Pending + data : data;
323
+ osc52Pending = "";
324
+
325
+ let pos = 0;
326
+ while (pos < search.length) {
327
+ const start = search.indexOf(OSC52_START, pos);
328
+ if (start < 0) break;
329
+
330
+ // Find terminator: BEL (\x07) or ST (\x1b\\)
331
+ let end = -1;
332
+ let endLen = 0;
333
+ for (let i = start + OSC52_START.length; i < search.length; i++) {
334
+ if (search[i] === "\x07") {
335
+ end = i;
336
+ endLen = 1;
337
+ break;
338
+ }
339
+ if (search[i] === "\x1b" && i + 1 < search.length && search[i + 1] === "\\") {
340
+ end = i;
341
+ endLen = 2;
342
+ break;
343
+ }
344
+ }
318
345
 
319
- pty.onData((data: string) => {
320
- // Pass OSC 52 clipboard sequences directly to the outer terminal
321
- const osc52Matches = data.match(OSC52_RE);
322
- if (osc52Matches) {
323
- for (const seq of osc52Matches) {
324
- process.stdout.write(seq);
346
+ if (end >= 0) {
347
+ process.stdout.write(search.slice(start, end + endLen));
348
+ pos = end + endLen;
349
+ } else {
350
+ // Incomplete — buffer for next chunk (cap at 512KB to avoid leaks)
351
+ const remainder = search.slice(start);
352
+ if (remainder.length < 512 * 1024) {
353
+ osc52Pending = remainder;
354
+ }
355
+ return;
325
356
  }
326
357
  }
358
+ }
359
+
360
+ pty.onData((data: string) => {
361
+ forwardOsc52(data);
327
362
 
328
363
  writesPending++;
329
364
  bridge.write(data).then(() => {
@@ -466,6 +501,7 @@ async function start(): Promise<void> {
466
501
 
467
502
  function cleanup(): void {
468
503
  control.close().catch(() => {});
504
+ process.stdout.write("\x1b[?2004l"); // disable bracketed paste mode
469
505
  process.stdout.write("\x1b[?1000l"); // disable mouse button tracking
470
506
  process.stdout.write("\x1b[?1002l"); // disable mouse drag tracking
471
507
  process.stdout.write("\x1b[?1006l"); // disable SGR mouse mode
package/src/renderer.ts CHANGED
@@ -67,7 +67,8 @@ export function compositeGrids(
67
67
  grid.cells[y][borderCol] = {
68
68
  ...DEFAULT_CELL,
69
69
  char: BORDER_CHAR,
70
- dim: true,
70
+ fg: 8,
71
+ fgMode: ColorMode.Palette,
71
72
  };
72
73
  // Copy main cells
73
74
  for (let x = 0; x < main.cols; x++) {
package/src/sidebar.ts CHANGED
@@ -9,10 +9,14 @@ const ACCENT_ATTRS: CellAttrs = {
9
9
  fg: 2,
10
10
  fgMode: ColorMode.Palette,
11
11
  };
12
+ // #1e2a35 as packed RGB for subtle active row background
13
+ const ACTIVE_BG = (0x1e << 16) | (0x2a << 8) | 0x35;
12
14
  const ACTIVE_MARKER_ATTRS: CellAttrs = {
13
15
  fg: 2,
14
16
  fgMode: ColorMode.Palette,
15
17
  bold: true,
18
+ bg: ACTIVE_BG,
19
+ bgMode: ColorMode.RGB,
16
20
  };
17
21
  const ACTIVITY_ATTRS: CellAttrs = {
18
22
  fg: 2,
@@ -24,9 +28,16 @@ const ATTENTION_ATTRS: CellAttrs = {
24
28
  bold: true,
25
29
  };
26
30
  const ACTIVE_NAME_ATTRS: CellAttrs = {
27
- fg: 15,
31
+ fg: 2,
28
32
  fgMode: ColorMode.Palette,
29
33
  bold: true,
34
+ bg: ACTIVE_BG,
35
+ bgMode: ColorMode.RGB,
36
+ };
37
+ const ACTIVE_DETAIL_ATTRS: CellAttrs = {
38
+ dim: true,
39
+ bg: ACTIVE_BG,
40
+ bgMode: ColorMode.RGB,
30
41
  };
31
42
  const INACTIVE_NAME_ATTRS: CellAttrs = {
32
43
  fg: 7,
@@ -314,6 +325,14 @@ export class Sidebar {
314
325
  this.rowToSessionIndex.set(detailRow, sessionIdx);
315
326
  }
316
327
 
328
+ // Paint active background across both rows
329
+ if (isActive) {
330
+ const bgFill = " ".repeat(this.width);
331
+ const bgAttrs: CellAttrs = { bg: ACTIVE_BG, bgMode: ColorMode.RGB };
332
+ writeString(grid, nameRow, 0, bgFill, bgAttrs);
333
+ writeString(grid, detailRow, 0, bgFill, bgAttrs);
334
+ }
335
+
317
336
  // Active marker
318
337
  if (isActive) {
319
338
  writeString(grid, nameRow, 0, "\u258e", ACTIVE_MARKER_ATTRS);
@@ -342,11 +361,15 @@ export class Sidebar {
342
361
  : { ...INACTIVE_NAME_ATTRS };
343
362
  writeString(grid, nameRow, nameStart, displayName, nameAttrs);
344
363
 
364
+ const wcAttrs: CellAttrs = isActive
365
+ ? { ...DIM_ATTRS, bg: ACTIVE_BG, bgMode: ColorMode.RGB }
366
+ : DIM_ATTRS;
345
367
  if (windowCountCol > nameStart) {
346
- writeString(grid, nameRow, windowCountCol, windowCountStr, DIM_ATTRS);
368
+ writeString(grid, nameRow, windowCountCol, windowCountStr, wcAttrs);
347
369
  }
348
370
 
349
371
  // Detail line
372
+ const detailAttrs: CellAttrs = isActive ? ACTIVE_DETAIL_ATTRS : DIM_ATTRS;
350
373
  if (item.grouped) {
351
374
  if (session.gitBranch) {
352
375
  const detailStart = 3;
@@ -355,7 +378,7 @@ export class Sidebar {
355
378
  if (branch.length > maxLen) {
356
379
  branch = branch.slice(0, maxLen - 1) + "\u2026";
357
380
  }
358
- writeString(grid, detailRow, detailStart, branch, DIM_ATTRS);
381
+ writeString(grid, detailRow, detailStart, branch, detailAttrs);
359
382
  }
360
383
  } else {
361
384
  const detailStart = 3;
@@ -363,7 +386,7 @@ export class Sidebar {
363
386
  if (session.gitBranch) {
364
387
  const branchCol = this.width - session.gitBranch.length - 1;
365
388
  if (branchCol > detailStart + 1) {
366
- writeString(grid, detailRow, branchCol, session.gitBranch, DIM_ATTRS);
389
+ writeString(grid, detailRow, branchCol, session.gitBranch, detailAttrs);
367
390
  branchCols = session.gitBranch.length + 2;
368
391
  }
369
392
  }
@@ -373,7 +396,7 @@ export class Sidebar {
373
396
  if (displayDir.length > dirMaxLen) {
374
397
  displayDir = displayDir.slice(0, dirMaxLen - 1) + "\u2026";
375
398
  }
376
- writeString(grid, detailRow, detailStart, displayDir, DIM_ATTRS);
399
+ writeString(grid, detailRow, detailStart, displayDir, detailAttrs);
377
400
  }
378
401
  }
379
402
  }