tsunami-code 3.11.8 → 3.11.9
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/index.js +13 -6
- package/lib/ui.js +38 -1
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from './lib/memory.js';
|
|
27
27
|
import { listMemories, readMemory, saveMemory, deleteMemory, getMemdirPath } from './lib/memdir.js';
|
|
28
28
|
|
|
29
|
-
const VERSION = '3.11.
|
|
29
|
+
const VERSION = '3.11.9';
|
|
30
30
|
const CONFIG_DIR = join(os.homedir(), '.tsunami-code');
|
|
31
31
|
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
32
32
|
const DEFAULT_SERVER = 'https://radiometric-reita-amuck.ngrok-free.dev';
|
|
@@ -830,12 +830,19 @@ async function run() {
|
|
|
830
830
|
case 'mode': {
|
|
831
831
|
const m = rest[0]?.toLowerCase();
|
|
832
832
|
if (!m) {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
833
|
+
// Interactive arrow-key picker
|
|
834
|
+
process.stdout.write(blue('\n Select permission mode:\n\n'));
|
|
835
|
+
const currentIdx = PERM_MODES.indexOf(permMode);
|
|
836
|
+
const selected = await ui.selectFromList(PERM_MODES, currentIdx >= 0 ? currentIdx : 0);
|
|
837
|
+
if (selected) {
|
|
838
|
+
permMode = selected;
|
|
839
|
+
planMode = selected === 'readonly';
|
|
840
|
+
ui.setPlanMode(planMode);
|
|
841
|
+
updateModeLabel();
|
|
842
|
+
console.log(green(`\n Mode: ${selected}\n`));
|
|
843
|
+
} else {
|
|
844
|
+
console.log(dim(' Cancelled.\n'));
|
|
837
845
|
}
|
|
838
|
-
console.log(dim('\n Usage: /mode <name>\n'));
|
|
839
846
|
break;
|
|
840
847
|
}
|
|
841
848
|
if (!PERM_MODES.includes(m)) {
|
package/lib/ui.js
CHANGED
|
@@ -281,6 +281,43 @@ export function createUI({ planMode: initPlanMode = false, onLine, onTab, onExit
|
|
|
281
281
|
});
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
// ── selectFromList — arrow-key interactive picker ─────────────────────────
|
|
285
|
+
function selectFromList(items, initialIdx = 0) {
|
|
286
|
+
return new Promise((resolve) => {
|
|
287
|
+
let idx = initialIdx;
|
|
288
|
+
const n = items.length;
|
|
289
|
+
|
|
290
|
+
function render(isFirst) {
|
|
291
|
+
if (!isFirst) process.stdout.write(`\x1b[${n}A`); // move back up
|
|
292
|
+
for (let i = 0; i < n; i++) {
|
|
293
|
+
const active = i === idx;
|
|
294
|
+
const label = active ? ` ${cyan('❯')} ${items[i]}` : ` ${dim(items[i])}`;
|
|
295
|
+
process.stdout.write(`\x1b[2K${label}\n`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
render(true);
|
|
300
|
+
|
|
301
|
+
lineHandler = (key) => {
|
|
302
|
+
if (key === '\x1b[A') { // up
|
|
303
|
+
idx = (idx - 1 + n) % n;
|
|
304
|
+
render(false);
|
|
305
|
+
} else if (key === '\x1b[B') { // down
|
|
306
|
+
idx = (idx + 1) % n;
|
|
307
|
+
render(false);
|
|
308
|
+
} else if (key === '\r' || key === '\n') { // confirm
|
|
309
|
+
lineHandler = null;
|
|
310
|
+
process.stdout.write('\n');
|
|
311
|
+
resolve(items[idx]);
|
|
312
|
+
} else if (key === '\x1b' || key === '\x03') { // cancel
|
|
313
|
+
lineHandler = null;
|
|
314
|
+
process.stdout.write('\n');
|
|
315
|
+
resolve(null);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
284
321
|
function readChar(promptText) {
|
|
285
322
|
return new Promise((resolve) => {
|
|
286
323
|
process.stdout.write(promptText);
|
|
@@ -374,7 +411,7 @@ export function createUI({ planMode: initPlanMode = false, onLine, onTab, onExit
|
|
|
374
411
|
return {
|
|
375
412
|
start, pause, resume,
|
|
376
413
|
setPlanMode, setContinuation, setModelLabel, setModeLabel,
|
|
377
|
-
readLine, readChar,
|
|
414
|
+
readLine, readChar, selectFromList,
|
|
378
415
|
wasInterrupted, stop, exitUI,
|
|
379
416
|
};
|
|
380
417
|
}
|