@vinh0809it/claude-sessions 0.3.0 → 0.3.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 +1 -1
  2. package/index.js +15 -30
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -89,7 +89,7 @@ Sessions are grouped by profile in the UI. Press `m` to move a session to a diff
89
89
  |----------|---------|-------|
90
90
  | **Kitty** | ✅ new tab | Requires `allow_remote_control yes` in `kitty.conf` |
91
91
  | **WezTerm** | ✅ new tab | Uses `wezterm cli spawn` |
92
- | **tmux** | ✅ new window | In the current session |
92
+ | **tmux** | ✅ new window | In the current session, also when tmux runs inside kitty or WezTerm |
93
93
  | **iTerm2** | ✅ new tab | Via `osascript` |
94
94
  | **Terminal.app** | ✅ new window | Via `osascript` |
95
95
  | **Windows Terminal** | ✅ new tab | Via `wt new-tab` (native Windows; in WSL it resumes in place) |
package/index.js CHANGED
@@ -457,9 +457,11 @@ function tabComplete(input) {
457
457
 
458
458
  function detectTerminal(env = process.env, platform = process.platform) {
459
459
  if (env.CLAUDE_SESSIONS_TERMINAL) return env.CLAUDE_SESSIONS_TERMINAL;
460
+ // tmux first: it is always the innermost layer, and a tmux started from kitty or
461
+ // WezTerm inherits their variables even though their tab commands can't reach it.
462
+ if (env.TMUX) return 'tmux';
460
463
  if (env.KITTY_WINDOW_ID !== undefined) return 'kitty';
461
464
  if (env.WEZTERM_PANE !== undefined) return 'wezterm';
462
- if (env.TMUX) return 'tmux';
463
465
  if (env.TERM_PROGRAM === 'iTerm.app') return 'iterm2';
464
466
  if (env.TERM_PROGRAM === 'Apple_Terminal') return 'apple-terminal';
465
467
  // WSL also sees WT_SESSION, but there `wt` would open a Windows shell, not Linux.
@@ -471,8 +473,10 @@ function shellEsc(s) { return "'" + String(s).replace(/'/g, "'\\''") + "'"; }
471
473
  function appleStr(s) { return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; }
472
474
 
473
475
  function trySpawn(cmd, args) {
474
- const r = spawnSync(cmd, args, { stdio: ['ignore', 'ignore', 'pipe'] });
475
- if (r.error) return { ok: false, msg: `${cmd}: ${r.error.code === 'ENOENT' ? 'not found' : r.error.message}` };
476
+ // Tab commands return at once; the timeout keeps a stuck one (e.g. `kitty @`
477
+ // waiting for a reply that never comes) from freezing the UI.
478
+ const r = spawnSync(cmd, args, { stdio: ['ignore', 'ignore', 'pipe'], timeout: 5000 });
479
+ if (r.error) return { ok: false, msg: `${cmd}: ${r.error.code === 'ENOENT' ? 'not found' : r.error.code === 'ETIMEDOUT' ? 'timed out' : r.error.message}` };
476
480
  return r.status === 0
477
481
  ? { ok: true }
478
482
  : { ok: false, msg: r.stderr?.toString().trim() || `${cmd} exited with ${r.status}` };
@@ -872,23 +876,6 @@ class Picker {
872
876
  }
873
877
  }
874
878
 
875
- // Word-wraps text to `width` columns; words longer than a line are split.
876
- function wrapWords(text, width) {
877
- const lines = [];
878
- let cur = '';
879
- for (let word of text.split(' ')) {
880
- while (word.length > width) {
881
- if (cur) { lines.push(cur); cur = ''; }
882
- lines.push(word.slice(0, width)); word = word.slice(width);
883
- }
884
- if (!cur) cur = word;
885
- else if (cur.length + 1 + word.length <= width) cur += ' ' + word;
886
- else { lines.push(cur); cur = word; }
887
- }
888
- if (cur || !lines.length) lines.push(cur);
889
- return lines;
890
- }
891
-
892
879
  // A stdin chunk may hold several keys (fast typing, paste): split into single
893
880
  // chars, keeping escape sequences like "\x1b[A" or "\x1b[5~" whole.
894
881
  function splitKeys(chunk) {
@@ -1123,16 +1110,14 @@ class TUI {
1123
1110
  const key = s.jsonlPath + ':' + s.lastModified.valueOf();
1124
1111
  let msgs = this._previewCache.get(key);
1125
1112
  if (!msgs) { msgs = readRecentMessages(s.jsonlPath); this._previewCache.set(key, msgs); }
1126
- const lines = [];
1127
- for (const m of msgs) {
1128
- const tag = m.role === 'user' ? fgc('92', 'you ') : fgc('96', 'cc ');
1129
- const width = Math.max(10, W - 8);
1130
- const chunks = wrapWords(m.text, width);
1131
- chunks.slice(0, 3).forEach((c, i) => lines.push(` ${i === 0 ? tag : ' '} ${i === 2 && chunks.length > 3 ? trunc(c, width - 1) + '…' : c}`));
1132
- }
1133
- if (!lines.length) lines.push(dim(' (no messages)'));
1113
+ // One line per message with a blank line between, newest at the bottom.
1114
+ const width = Math.max(10, W - 8);
1115
+ const lines = msgs.flatMap(m => ['',
1116
+ ` ${m.role === 'user' ? fgc('92', 'you ') : fgc('96', 'cc ')} ${trunc(m.text.replace(/\s+/g, ' ').trim(), width)}`]);
1117
+ const body = lines.slice(-(H - 2));
1118
+ if (body[0] === '') body.shift();
1134
1119
  const head = dim(` ${fmtDate(s.lastModified)} · ${fmtSize(s.size)} · ${s.sessionId}`);
1135
- return [head, ...lines.slice(-(H - 1))];
1120
+ return [head, '', ...(body.length ? body : [dim(' (no messages)')])];
1136
1121
  }
1137
1122
 
1138
1123
  // ── Render ──────────────────────────────────────────────────────────────────
@@ -1425,6 +1410,6 @@ if (require.main === module) {
1425
1410
  module.exports = {
1426
1411
  encodePathToDir, projectDirNameFor, tabComplete, resolveProjectPath, moveSession, readSession,
1427
1412
  readRecentMessages, renameSession, deleteSessionFiles, emptyTrash, scanConfigDir, sortSessions,
1428
- detectTerminal, wtArgs, parseArgs, fmtAgo, fmtSize, splitKeys, wrapWords, cache, LineInput,
1413
+ detectTerminal, wtArgs, parseArgs, fmtAgo, fmtSize, splitKeys, cache, LineInput,
1429
1414
  };
1430
1415
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vinh0809it/claude-sessions",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Fast terminal UI for browsing, previewing, resuming, renaming, moving, and deleting Claude Code sessions",
5
5
  "bin": {
6
6
  "claude-sessions": "index.js"