claude-starter 1.3.0 → 1.3.1
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 +10 -4
- package/index.js +244 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,7 +75,9 @@ claude-starter
|
|
|
75
75
|
| 🔀 | **多种排序** | 时间 / 大小 / 消息数 / 项目 |
|
|
76
76
|
| 📎 | **复制 ID** | `c` 一键复制到剪贴板 |
|
|
77
77
|
| 🔒 | **权限模式** | `m` 设置权限模式,`d` 一键 danger 模式恢复 |
|
|
78
|
+
| ✏️ | **重命名会话** | `r` 直接重命名,支持中文输入 |
|
|
78
79
|
| 🗑️ | **删除会话** | `x` 删除不需要的会话 |
|
|
80
|
+
| ⌨️ | **Vim 快捷键** | `j`/`k` 上下,`g`/`G` 跳顶/底 |
|
|
79
81
|
| 🧠 | **智能 CLI** | 自动检测 `mai-claude` / `claude` |
|
|
80
82
|
| 🔐 | **完全本地** | 不联网,不上传,不追踪 |
|
|
81
83
|
|
|
@@ -110,11 +112,12 @@ claude-starter --help # 显示帮助信息
|
|
|
110
112
|
|
|
111
113
|
| 按键 | 功能 |
|
|
112
114
|
|:---:|------|
|
|
113
|
-
| `↑` `↓` | 上下导航 |
|
|
115
|
+
| `↑` `↓` / `j` `k` | 上下导航 |
|
|
114
116
|
| `Enter` | 新建 / 恢复对话 |
|
|
115
117
|
| `n` | 直接新建 |
|
|
116
118
|
| `d` | Danger 模式恢复(bypassPermissions) |
|
|
117
119
|
| `m` | 权限模式选择器 |
|
|
120
|
+
| `r` | 重命名会话 |
|
|
118
121
|
| `/` | 搜索 |
|
|
119
122
|
| `Backspace` | 删除搜索字符,删空自动退出 |
|
|
120
123
|
| `Esc` | 清空搜索 |
|
|
@@ -122,7 +125,7 @@ claude-starter --help # 显示帮助信息
|
|
|
122
125
|
| `s` | 切换排序(时间/大小/消息数/项目) |
|
|
123
126
|
| `c` | 复制 Session ID |
|
|
124
127
|
| `x` / `Delete` | 删除会话 |
|
|
125
|
-
| `
|
|
128
|
+
| `g` / `G` | 跳到顶 / 底 |
|
|
126
129
|
| `Ctrl-D` / `Ctrl-U` | 翻页 |
|
|
127
130
|
| `q` / `Ctrl-C` | 退出 |
|
|
128
131
|
|
|
@@ -180,7 +183,9 @@ Searches across **everything** — project names, Git branches, conversation con
|
|
|
180
183
|
| 🔀 | **Sort Modes** | Sort by time, size, messages, or project |
|
|
181
184
|
| 📎 | **Copy ID** | Press `c` to copy session ID |
|
|
182
185
|
| 🔒 | **Permission Modes** | Press `m` to configure, `d` for quick danger-mode resume |
|
|
186
|
+
| ✏️ | **Rename Sessions** | Press `r` to rename, supports CJK input |
|
|
183
187
|
| 🗑️ | **Delete Sessions** | Press `x` to remove unwanted sessions |
|
|
188
|
+
| ⌨️ | **Vim Keybindings** | `j`/`k` navigate, `g`/`G` jump to top/bottom |
|
|
184
189
|
| 🧠 | **Smart CLI** | Auto-detects `mai-claude` vs `claude` |
|
|
185
190
|
| 🔐 | **100% Local** | No network, no telemetry, no data leaves your machine |
|
|
186
191
|
|
|
@@ -219,11 +224,12 @@ claude-starter --help # Show help
|
|
|
219
224
|
|
|
220
225
|
| Key | Action |
|
|
221
226
|
|:---:|--------|
|
|
222
|
-
| `↑` `↓` | Navigate sessions |
|
|
227
|
+
| `↑` `↓` / `j` `k` | Navigate sessions |
|
|
223
228
|
| `Enter` | Start new / resume selected session |
|
|
224
229
|
| `n` | New session |
|
|
225
230
|
| `d` | Resume with bypassPermissions (danger mode) |
|
|
226
231
|
| `m` | Permission mode picker |
|
|
232
|
+
| `r` | Rename session |
|
|
227
233
|
| `/` | Search |
|
|
228
234
|
| `Backspace` | Edit search, auto-exit when empty |
|
|
229
235
|
| `Esc` | Clear filter |
|
|
@@ -231,7 +237,7 @@ claude-starter --help # Show help
|
|
|
231
237
|
| `s` | Cycle sort mode (time/size/messages/project) |
|
|
232
238
|
| `c` | Copy session ID |
|
|
233
239
|
| `x` / `Delete` | Delete session |
|
|
234
|
-
| `
|
|
240
|
+
| `g` / `G` | Jump to top / bottom |
|
|
235
241
|
| `Ctrl-D` / `Ctrl-U` | Page down / up |
|
|
236
242
|
| `q` / `Ctrl-C` | Quit |
|
|
237
243
|
|
package/index.js
CHANGED
|
@@ -206,16 +206,27 @@ function loadSessionQuick(filePath, projectName) {
|
|
|
206
206
|
const headBuf = Buffer.alloc(Math.min(8192, stat.size));
|
|
207
207
|
fs.readSync(fd, headBuf, 0, headBuf.length, 0);
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
// Read tail with progressive expansion: start at 32KB, grow up to 256KB
|
|
210
|
+
// until we find a JSON line with a top-level timestamp (to get accurate lastTs).
|
|
211
|
+
let tailStr = '';
|
|
210
212
|
if (stat.size > 8192) {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
const tailSizes = [32768, 65536, 131072, 262144];
|
|
214
|
+
for (const ts of tailSizes) {
|
|
215
|
+
const tailSize = Math.min(ts, stat.size - 8192);
|
|
216
|
+
const tailBuf = Buffer.alloc(tailSize);
|
|
217
|
+
fs.readSync(fd, tailBuf, 0, tailSize, stat.size - tailSize);
|
|
218
|
+
tailStr = tailBuf.toString('utf-8');
|
|
219
|
+
// Check if any parseable JSON line has a top-level timestamp
|
|
220
|
+
const hasTopLevelTs = tailStr.split('\n').some(line => {
|
|
221
|
+
try { return !!JSON.parse(line).timestamp; } catch { return false; }
|
|
222
|
+
});
|
|
223
|
+
if (hasTopLevelTs) break;
|
|
224
|
+
if (tailSize >= stat.size - 8192) break; // already read entire file
|
|
225
|
+
}
|
|
214
226
|
}
|
|
215
227
|
fs.closeSync(fd);
|
|
216
228
|
|
|
217
229
|
const headStr = headBuf.toString('utf-8');
|
|
218
|
-
const tailStr = tailBuf.toString('utf-8');
|
|
219
230
|
|
|
220
231
|
let firstTs = null, lastTs = null;
|
|
221
232
|
let version = '', gitBranch = '', cwd = '', permissionMode = '';
|
|
@@ -520,16 +531,17 @@ function createApp() {
|
|
|
520
531
|
|
|
521
532
|
function updateFooter() {
|
|
522
533
|
const keys = [
|
|
523
|
-
'{#
|
|
524
|
-
'{#7aa2f7-fg}{bold}↵{/} {#
|
|
525
|
-
'{#
|
|
526
|
-
'{#f7768e-fg}{bold}d{/} {#
|
|
527
|
-
'{#
|
|
528
|
-
'{#
|
|
529
|
-
'{#
|
|
530
|
-
'{#
|
|
531
|
-
'{#
|
|
532
|
-
'{#
|
|
534
|
+
'{#9ece6a-fg}{bold}n{/} {#9ece6a-fg}New{/}',
|
|
535
|
+
'{#7aa2f7-fg}{bold}↵{/} {#7aa2f7-fg}Resume{/}',
|
|
536
|
+
'{#bb9af7-fg}{bold}m{/} {#bb9af7-fg}Mode{/}',
|
|
537
|
+
'{#f7768e-fg}{bold}d{/} {#f7768e-fg}Danger{/}',
|
|
538
|
+
'{#e0af68-fg}{bold}/{/} {#e0af68-fg}Search{/}',
|
|
539
|
+
'{#7dcfff-fg}{bold}p{/} {#7dcfff-fg}Project{/}',
|
|
540
|
+
'{#73daca-fg}{bold}s{/} {#73daca-fg}Sort{/}',
|
|
541
|
+
'{#565f89-fg}{bold}c{/} {#565f89-fg}Copy ID{/}',
|
|
542
|
+
'{#ff9e64-fg}{bold}r{/} {#ff9e64-fg}Rename{/}',
|
|
543
|
+
'{#f7768e-fg}{bold}x{/} {#f7768e-fg}Delete{/}',
|
|
544
|
+
'{#565f89-fg}{bold}q{/} {#565f89-fg}Quit{/}',
|
|
533
545
|
];
|
|
534
546
|
footer.setContent(`\n ${keys.join(' {#414868-fg}│{/} ')}`);
|
|
535
547
|
}
|
|
@@ -829,17 +841,15 @@ function createApp() {
|
|
|
829
841
|
}
|
|
830
842
|
|
|
831
843
|
screen.key(['down'], () => {
|
|
832
|
-
if (popupOpen) return;
|
|
833
|
-
if (isSearchMode) { isSearchMode = false; updateHeader(); screen.render(); }
|
|
844
|
+
if (renameMode || popupOpen || isSearchMode) return;
|
|
834
845
|
moveSelection(1);
|
|
835
846
|
});
|
|
836
847
|
screen.key(['up'], () => {
|
|
837
|
-
if (popupOpen) return;
|
|
838
|
-
if (isSearchMode) { isSearchMode = false; updateHeader(); screen.render(); }
|
|
848
|
+
if (renameMode || popupOpen || isSearchMode) return;
|
|
839
849
|
moveSelection(-1);
|
|
840
850
|
});
|
|
841
851
|
screen.key(['home'], () => {
|
|
842
|
-
if (popupOpen) return;
|
|
852
|
+
if (renameMode || popupOpen) return;
|
|
843
853
|
if (isSearchMode) { isSearchMode = false; }
|
|
844
854
|
selectedIndex = -1;
|
|
845
855
|
suppressSelectEvent = true; listPanel.select(0); suppressSelectEvent = false;
|
|
@@ -847,7 +857,7 @@ function createApp() {
|
|
|
847
857
|
renderDetail(); updateHeader(); screen.render();
|
|
848
858
|
});
|
|
849
859
|
screen.key(['end'], () => {
|
|
850
|
-
if (popupOpen) return;
|
|
860
|
+
if (renameMode || popupOpen) return;
|
|
851
861
|
if (isSearchMode) { isSearchMode = false; }
|
|
852
862
|
selectedIndex = Math.max(0, filteredSessions.length - 1);
|
|
853
863
|
suppressSelectEvent = true; listPanel.select(selectedIndex + 1); suppressSelectEvent = false;
|
|
@@ -855,31 +865,86 @@ function createApp() {
|
|
|
855
865
|
renderDetail(); updateHeader(); screen.render();
|
|
856
866
|
});
|
|
857
867
|
screen.key(['pagedown', 'C-d'], () => {
|
|
858
|
-
if (popupOpen) return;
|
|
868
|
+
if (renameMode || popupOpen) return;
|
|
859
869
|
if (isSearchMode) { isSearchMode = false; updateHeader(); screen.render(); }
|
|
860
870
|
moveSelection(Math.floor((listPanel.height || 20) / 2));
|
|
861
871
|
});
|
|
862
872
|
screen.key(['pageup', 'C-u'], () => {
|
|
863
|
-
if (popupOpen) return;
|
|
873
|
+
if (renameMode || popupOpen) return;
|
|
864
874
|
if (isSearchMode) { isSearchMode = false; updateHeader(); screen.render(); }
|
|
865
875
|
moveSelection(-Math.floor((listPanel.height || 20) / 2));
|
|
866
876
|
});
|
|
867
877
|
|
|
868
878
|
// Search
|
|
869
879
|
screen.key(['/'], () => {
|
|
870
|
-
if (isSearchMode) return;
|
|
880
|
+
if (renameMode || isSearchMode) return;
|
|
871
881
|
isSearchMode = true; filterText = ''; applyFilter();
|
|
872
882
|
});
|
|
873
883
|
|
|
874
884
|
screen.on('keypress', (ch, key) => {
|
|
875
|
-
//
|
|
876
|
-
if (
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
885
|
+
// ── Rename mode: capture all input ──
|
|
886
|
+
if (renameMode) {
|
|
887
|
+
if (key.name === 'return' || key.name === 'enter') {
|
|
888
|
+
const session = renameSession;
|
|
889
|
+
const value = renameValue;
|
|
890
|
+
closeRename();
|
|
891
|
+
submitRename(session, value);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (key.name === 'escape') {
|
|
895
|
+
closeRename();
|
|
896
|
+
listPanel.focus();
|
|
897
|
+
screen.render();
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
if (key.name === 'backspace') {
|
|
901
|
+
if (renameValue.length > 0) {
|
|
902
|
+
renameValue = [...renameValue].slice(0, -1).join('');
|
|
903
|
+
renderRenameInput();
|
|
904
|
+
}
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
if (ch && ch.length >= 1 && ch.charCodeAt(0) >= 32 && !key.ctrl && !key.meta) {
|
|
908
|
+
renameValue += ch;
|
|
909
|
+
renderRenameInput();
|
|
910
|
+
}
|
|
911
|
+
return; // swallow all keys while in rename mode
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// Backspace: delete search char, or exit search mode if empty
|
|
915
|
+
if (key.name === 'backspace') {
|
|
916
|
+
if (filterText) {
|
|
917
|
+
filterText = filterText.slice(0, -1);
|
|
918
|
+
selectedIndex = -1;
|
|
919
|
+
isSearchMode = !!filterText;
|
|
920
|
+
applyFilter();
|
|
921
|
+
} else if (isSearchMode) {
|
|
922
|
+
isSearchMode = false;
|
|
923
|
+
applyFilter();
|
|
924
|
+
}
|
|
881
925
|
return;
|
|
882
926
|
}
|
|
927
|
+
|
|
928
|
+
// Vim-like navigation (only when NOT in search mode)
|
|
929
|
+
if (!isSearchMode && !popupOpen) {
|
|
930
|
+
if (ch === 'j') { moveSelection(1); return; }
|
|
931
|
+
if (ch === 'k') { moveSelection(-1); return; }
|
|
932
|
+
if (ch === 'G') {
|
|
933
|
+
selectedIndex = Math.max(0, filteredSessions.length - 1);
|
|
934
|
+
suppressSelectEvent = true; listPanel.select(selectedIndex + 1); suppressSelectEvent = false;
|
|
935
|
+
listPanel.childBase = Math.max(0, selectedIndex + 1 - listPanel.height + 1);
|
|
936
|
+
renderDetail(); updateHeader(); screen.render();
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
if (ch === 'g') {
|
|
940
|
+
selectedIndex = -1;
|
|
941
|
+
suppressSelectEvent = true; listPanel.select(0); suppressSelectEvent = false;
|
|
942
|
+
listPanel.childBase = 0;
|
|
943
|
+
renderDetail(); updateHeader(); screen.render();
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
883
948
|
if (!isSearchMode) return;
|
|
884
949
|
if (key.name === 'return' || key.name === 'enter') { isSearchMode = false; renderAll(); return; }
|
|
885
950
|
if (key.name === 'escape') { isSearchMode = false; filterText = ''; applyFilter(); return; }
|
|
@@ -937,7 +1002,23 @@ function createApp() {
|
|
|
937
1002
|
child.on('exit', (code) => process.exit(code || 0));
|
|
938
1003
|
}
|
|
939
1004
|
|
|
1005
|
+
// Track the rename confirm popup and its session for Enter handling
|
|
1006
|
+
let renameConfirmPopup = null;
|
|
1007
|
+
let renameConfirmSession = null;
|
|
1008
|
+
|
|
940
1009
|
screen.key(['enter'], () => {
|
|
1010
|
+
if (renameMode) return;
|
|
1011
|
+
if (renameJustFinished) return;
|
|
1012
|
+
// Handle rename confirm popup Enter
|
|
1013
|
+
if (renameConfirmPopup && popupOpen) {
|
|
1014
|
+
const session = renameConfirmSession;
|
|
1015
|
+
renameConfirmPopup.destroy();
|
|
1016
|
+
renameConfirmPopup = null;
|
|
1017
|
+
renameConfirmSession = null;
|
|
1018
|
+
popupOpen = false;
|
|
1019
|
+
resumeSession(session);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
941
1022
|
if (isSearchMode) { isSearchMode = false; renderAll(); return; }
|
|
942
1023
|
if (popupOpen) return;
|
|
943
1024
|
if (selectedIndex === -1) { startNewSession(); return; }
|
|
@@ -947,13 +1028,13 @@ function createApp() {
|
|
|
947
1028
|
|
|
948
1029
|
// Quick shortcut: n = new session
|
|
949
1030
|
screen.key(['n'], () => {
|
|
950
|
-
if (isSearchMode) return;
|
|
1031
|
+
if (renameMode || isSearchMode) return;
|
|
951
1032
|
startNewSession();
|
|
952
1033
|
});
|
|
953
1034
|
|
|
954
1035
|
// Copy session ID
|
|
955
1036
|
screen.key(['c'], () => {
|
|
956
|
-
if (isSearchMode) return;
|
|
1037
|
+
if (renameMode || isSearchMode) return;
|
|
957
1038
|
if (filteredSessions.length === 0) return;
|
|
958
1039
|
const sid = filteredSessions[selectedIndex].sessionId;
|
|
959
1040
|
try {
|
|
@@ -1097,14 +1178,14 @@ function createApp() {
|
|
|
1097
1178
|
|
|
1098
1179
|
// ─── Quick dangerous resume (d key) ────────────────────────────────────
|
|
1099
1180
|
screen.key(['d'], () => {
|
|
1100
|
-
if (isSearchMode || popupOpen) return;
|
|
1181
|
+
if (renameMode || isSearchMode || popupOpen) return;
|
|
1101
1182
|
if (selectedIndex < 0 || selectedIndex >= filteredSessions.length) return;
|
|
1102
1183
|
resumeSession(filteredSessions[selectedIndex], 'bypassPermissions');
|
|
1103
1184
|
});
|
|
1104
1185
|
|
|
1105
1186
|
// ─── Permission mode picker (m key) ───────────────────────────────────
|
|
1106
1187
|
screen.key(['m'], () => {
|
|
1107
|
-
if (isSearchMode || popupOpen) return;
|
|
1188
|
+
if (renameMode || isSearchMode || popupOpen) return;
|
|
1108
1189
|
if (selectedIndex < 0 || selectedIndex >= filteredSessions.length) return;
|
|
1109
1190
|
showPermissionModePicker(filteredSessions[selectedIndex]);
|
|
1110
1191
|
});
|
|
@@ -1169,18 +1250,143 @@ function createApp() {
|
|
|
1169
1250
|
}
|
|
1170
1251
|
|
|
1171
1252
|
screen.key(['x', 'delete'], () => {
|
|
1172
|
-
if (isSearchMode || popupOpen) return;
|
|
1253
|
+
if (renameMode || isSearchMode || popupOpen) return;
|
|
1173
1254
|
if (selectedIndex < 0 || selectedIndex >= filteredSessions.length) return;
|
|
1174
1255
|
showDeleteConfirm(filteredSessions[selectedIndex]);
|
|
1175
1256
|
});
|
|
1176
1257
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1258
|
+
// ─── Rename Session ───────────────────────────────────────────────────
|
|
1259
|
+
const stringWidth = require('string-width');
|
|
1260
|
+
let renameMode = false;
|
|
1261
|
+
let renameJustFinished = false;
|
|
1262
|
+
let renameValue = '';
|
|
1263
|
+
let renameSession = null;
|
|
1264
|
+
let renamePopup = null;
|
|
1265
|
+
let renameDisplay = null;
|
|
1266
|
+
const renameMaxWidth = 46;
|
|
1267
|
+
|
|
1268
|
+
function renderRenameInput() {
|
|
1269
|
+
let display = renameValue;
|
|
1270
|
+
while (stringWidth(display) > renameMaxWidth && display.length > 0) {
|
|
1271
|
+
display = display.substring(1);
|
|
1272
|
+
}
|
|
1273
|
+
renameDisplay.setContent(display + '▌');
|
|
1274
|
+
screen.render();
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
function showRenameInput(session) {
|
|
1278
|
+
renameSession = session;
|
|
1279
|
+
renameValue = session.customTitle || '';
|
|
1280
|
+
|
|
1281
|
+
renamePopup = blessed.box({
|
|
1282
|
+
parent: screen, top: 'center', left: 'center',
|
|
1283
|
+
width: 52, height: 7,
|
|
1284
|
+
label: ' {bold}{#73daca-fg}Rename Session{/} ',
|
|
1285
|
+
tags: true, border: { type: 'line' },
|
|
1286
|
+
style: {
|
|
1287
|
+
border: { fg: '#73daca' }, bg: '#24283b', fg: '#a9b1d6',
|
|
1288
|
+
label: { fg: '#73daca' },
|
|
1289
|
+
},
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
renameDisplay = blessed.box({
|
|
1293
|
+
parent: renamePopup,
|
|
1294
|
+
top: 1, left: 1, right: 1, height: 1,
|
|
1295
|
+
tags: false,
|
|
1296
|
+
style: { fg: 'white', bg: '#1a1b26' },
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
blessed.box({
|
|
1300
|
+
parent: renamePopup,
|
|
1301
|
+
top: 3, left: 1, right: 1, height: 1,
|
|
1302
|
+
tags: true,
|
|
1303
|
+
style: { bg: '#24283b' },
|
|
1304
|
+
content: ' {#9ece6a-fg}{bold}Enter{/}{#a9b1d6-fg} Save {/}{#565f89-fg}Esc{/}{#a9b1d6-fg} Cancel{/}',
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
popupOpen = true;
|
|
1308
|
+
renameMode = true;
|
|
1309
|
+
renderRenameInput();
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function closeRename() {
|
|
1313
|
+
renameMode = false;
|
|
1314
|
+
if (renamePopup) { renamePopup.destroy(); renamePopup = null; }
|
|
1315
|
+
popupOpen = false;
|
|
1316
|
+
renameSession = null;
|
|
1317
|
+
renameDisplay = null;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
function submitRename(session, newTitle) {
|
|
1321
|
+
newTitle = (newTitle || '').trim();
|
|
1322
|
+
|
|
1323
|
+
// Save to meta
|
|
1324
|
+
if (!meta.sessions[session.sessionId]) meta.sessions[session.sessionId] = {};
|
|
1325
|
+
meta.sessions[session.sessionId].customTitle = newTitle || undefined;
|
|
1326
|
+
if (!newTitle) delete meta.sessions[session.sessionId].customTitle;
|
|
1327
|
+
saveMeta(meta);
|
|
1328
|
+
|
|
1329
|
+
// Update in-memory session
|
|
1330
|
+
session.customTitle = newTitle;
|
|
1331
|
+
|
|
1332
|
+
// Also append to JSONL file so Claude Code sees it
|
|
1333
|
+
if (newTitle && fs.existsSync(session.filePath)) {
|
|
1334
|
+
try {
|
|
1335
|
+
const entry = JSON.stringify({ type: 'custom-title', customTitle: newTitle });
|
|
1336
|
+
fs.appendFileSync(session.filePath, '\n' + entry);
|
|
1337
|
+
} catch (e) { /* silently fail */ }
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
renderAll();
|
|
1341
|
+
|
|
1342
|
+
// Ask whether to resume this session after rename
|
|
1343
|
+
// We use renameJustFinished flag to prevent the Enter key from rename
|
|
1344
|
+
// from immediately triggering resume
|
|
1345
|
+
renameJustFinished = true;
|
|
1346
|
+
setTimeout(() => { renameJustFinished = false; }, 200);
|
|
1347
|
+
|
|
1348
|
+
setTimeout(() => {
|
|
1349
|
+
const titleLabel = newTitle ? `{#73daca-fg}${esc(newTitle)}{/}` : '{#565f89-fg}(title cleared){/}';
|
|
1350
|
+
renameConfirmSession = session;
|
|
1351
|
+
renameConfirmPopup = blessed.box({
|
|
1352
|
+
parent: screen, top: 'center', left: 'center',
|
|
1353
|
+
width: 48, height: 8,
|
|
1354
|
+
label: ' {bold}{#9ece6a-fg}Renamed{/} ',
|
|
1355
|
+
tags: true, border: { type: 'line' },
|
|
1356
|
+
style: {
|
|
1357
|
+
border: { fg: '#9ece6a' }, bg: '#24283b', fg: '#a9b1d6',
|
|
1358
|
+
label: { fg: '#9ece6a' },
|
|
1359
|
+
},
|
|
1360
|
+
content: `\n ${titleLabel}\n\n {#9ece6a-fg}{bold}Enter{/}{#a9b1d6-fg} Resume {/}{#565f89-fg}Esc{/}{#a9b1d6-fg} Back to list{/}`,
|
|
1361
|
+
});
|
|
1362
|
+
popupOpen = true;
|
|
1363
|
+
renameConfirmPopup.focus();
|
|
1364
|
+
screen.render();
|
|
1365
|
+
|
|
1366
|
+
renameConfirmPopup.key(['escape', 'q'], () => {
|
|
1367
|
+
renameConfirmPopup.destroy();
|
|
1368
|
+
renameConfirmPopup = null;
|
|
1369
|
+
renameConfirmSession = null;
|
|
1370
|
+
popupOpen = false;
|
|
1371
|
+
renderAll();
|
|
1372
|
+
});
|
|
1373
|
+
}, 50);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
screen.key(['r'], () => {
|
|
1377
|
+
if (isSearchMode || popupOpen) return;
|
|
1378
|
+
if (selectedIndex < 0 || selectedIndex >= filteredSessions.length) return;
|
|
1379
|
+
showRenameInput(filteredSessions[selectedIndex]);
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
screen.key(['s'], () => { if (!renameMode && !isSearchMode) cycleSort(); });
|
|
1383
|
+
screen.key(['p'], () => { if (!renameMode && !isSearchMode) showProjectPicker(); });
|
|
1179
1384
|
screen.key(['escape'], () => {
|
|
1385
|
+
if (renameMode) return; // handled in keypress
|
|
1180
1386
|
if (isSearchMode) { isSearchMode = false; filterText = ''; applyFilter(); return; }
|
|
1181
1387
|
filterText = ''; selectedIndex = -1; applyFilter();
|
|
1182
1388
|
});
|
|
1183
|
-
screen.key(['q', 'C-c'], () => { process.stdout.write('\x1b[0m'); screen.destroy(); process.exit(0); });
|
|
1389
|
+
screen.key(['q', 'C-c'], () => { if (renameMode) return; process.stdout.write('\x1b[0m'); screen.destroy(); process.exit(0); });
|
|
1184
1390
|
|
|
1185
1391
|
// Remove blessed's built-in wheel handlers (they call select which changes selection)
|
|
1186
1392
|
listPanel.removeAllListeners('element wheeldown');
|