pi-goosedump 0.12.32 → 0.12.33
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/dist/index.ts +92 -16
- package/package.json +2 -2
package/dist/index.ts
CHANGED
|
@@ -1251,6 +1251,40 @@ async function runSearchPanel(ctx, query) {
|
|
|
1251
1251
|
async function runSessionsPanel(ctx, executablePath) {
|
|
1252
1252
|
const OVERLAY_WIDTH = 84;
|
|
1253
1253
|
const VIEWPORT = 18;
|
|
1254
|
+
function buildChildrenMap(sessions) {
|
|
1255
|
+
const byKey = new Map(sessions.map((s) => [`${s.provider}:${s.id}`, s]));
|
|
1256
|
+
const childrenMap = new Map;
|
|
1257
|
+
for (const session of sessions) {
|
|
1258
|
+
if (!session.parent)
|
|
1259
|
+
continue;
|
|
1260
|
+
const parentKey = `${session.provider}:${session.parent}`;
|
|
1261
|
+
if (!byKey.has(parentKey))
|
|
1262
|
+
continue;
|
|
1263
|
+
const siblings = childrenMap.get(parentKey) ?? [];
|
|
1264
|
+
siblings.push(session);
|
|
1265
|
+
childrenMap.set(parentKey, siblings);
|
|
1266
|
+
}
|
|
1267
|
+
for (const siblings of childrenMap.values()) {
|
|
1268
|
+
siblings.sort((a, b) => {
|
|
1269
|
+
const timeA = a.from ? new Date(a.from).getTime() : 0;
|
|
1270
|
+
const timeB = b.from ? new Date(b.from).getTime() : 0;
|
|
1271
|
+
return timeB - timeA;
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
return childrenMap;
|
|
1275
|
+
}
|
|
1276
|
+
function flattenTree(roots, childrenMap, expanded, depth) {
|
|
1277
|
+
const rows = [];
|
|
1278
|
+
for (const session of roots) {
|
|
1279
|
+
const key = `${session.provider}:${session.id}`;
|
|
1280
|
+
const children = childrenMap.get(key) ?? [];
|
|
1281
|
+
rows.push({ session, depth, hasChildren: children.length > 0 });
|
|
1282
|
+
if (children.length > 0 && expanded.has(key)) {
|
|
1283
|
+
rows.push(...flattenTree(children, childrenMap, expanded, depth + 1));
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
return rows;
|
|
1287
|
+
}
|
|
1254
1288
|
await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
1255
1289
|
const currentTarget = currentSessionTarget(ctx);
|
|
1256
1290
|
const borderFg = (s) => theme.fg("border", s);
|
|
@@ -1260,38 +1294,55 @@ async function runSessionsPanel(ctx, executablePath) {
|
|
|
1260
1294
|
const dim = (s) => theme.fg("dim", s);
|
|
1261
1295
|
const muted = (s) => theme.fg("muted", s);
|
|
1262
1296
|
let sessions = [];
|
|
1297
|
+
let childrenMap = new Map;
|
|
1298
|
+
let roots = [];
|
|
1299
|
+
let displayList = [];
|
|
1300
|
+
let expandedNodes = new Set;
|
|
1263
1301
|
let error = null;
|
|
1264
1302
|
let selected = 0;
|
|
1265
1303
|
let scroll = 0;
|
|
1304
|
+
function rebuildDisplayList() {
|
|
1305
|
+
displayList = flattenTree(roots, childrenMap, expandedNodes, 0);
|
|
1306
|
+
}
|
|
1266
1307
|
async function loadSessions() {
|
|
1267
1308
|
try {
|
|
1268
1309
|
const all = await goosedumpListSessions(undefined, executablePath);
|
|
1269
|
-
const
|
|
1310
|
+
const cwdIds = new Set(all.filter((s) => s.cwd === ctx.cwd).map((s) => `${s.provider}:${s.id}`));
|
|
1311
|
+
const cwdSessions = all.filter((s) => s.cwd === ctx.cwd || s.parent !== null && cwdIds.has(`${s.provider}:${s.parent}`));
|
|
1270
1312
|
cwdSessions.sort((a, b) => {
|
|
1271
1313
|
const timeA = a.from ? new Date(a.from).getTime() : 0;
|
|
1272
1314
|
const timeB = b.from ? new Date(b.from).getTime() : 0;
|
|
1273
1315
|
return timeB - timeA;
|
|
1274
1316
|
});
|
|
1275
1317
|
sessions = cwdSessions;
|
|
1318
|
+
childrenMap = buildChildrenMap(sessions);
|
|
1319
|
+
const sessionKeys = new Set(sessions.map((s) => `${s.provider}:${s.id}`));
|
|
1320
|
+
roots = sessions.filter((s) => s.parent === null || !sessionKeys.has(`${s.provider}:${s.parent}`));
|
|
1321
|
+
expandedNodes = new Set([...expandedNodes].filter((key) => sessionKeys.has(key)));
|
|
1322
|
+
rebuildDisplayList();
|
|
1276
1323
|
if (sessions.length === 0) {
|
|
1277
1324
|
error = "No sessions found for current directory.";
|
|
1278
1325
|
} else {
|
|
1279
1326
|
error = null;
|
|
1280
1327
|
}
|
|
1281
|
-
selected = Math.min(selected, Math.max(0,
|
|
1282
|
-
scroll = Math.min(scroll, Math.max(0,
|
|
1328
|
+
selected = Math.min(selected, Math.max(0, displayList.length - 1));
|
|
1329
|
+
scroll = Math.min(scroll, Math.max(0, displayList.length - VIEWPORT));
|
|
1283
1330
|
tui.requestRender();
|
|
1284
1331
|
} catch (err) {
|
|
1285
1332
|
sessions = [];
|
|
1333
|
+
childrenMap = new Map;
|
|
1334
|
+
roots = [];
|
|
1335
|
+
displayList = [];
|
|
1286
1336
|
error = err instanceof Error ? err.message : String(err);
|
|
1287
1337
|
tui.requestRender();
|
|
1288
1338
|
}
|
|
1289
1339
|
}
|
|
1290
1340
|
loadSessions();
|
|
1291
1341
|
async function deleteSelected() {
|
|
1292
|
-
const
|
|
1293
|
-
if (!
|
|
1342
|
+
const row = displayList[selected];
|
|
1343
|
+
if (!row)
|
|
1294
1344
|
return;
|
|
1345
|
+
const s = row.session;
|
|
1295
1346
|
const target = formatTarget(s.provider, s.id);
|
|
1296
1347
|
if (currentTarget === target) {
|
|
1297
1348
|
ctx.ui.notify("Cannot delete the active session.", "error");
|
|
@@ -1319,29 +1370,33 @@ async function runSessionsPanel(ctx, executablePath) {
|
|
|
1319
1370
|
lines.push(`${borderFg("╰")}${borderFg("─".repeat(width - 2))}${borderFg("╯")}`);
|
|
1320
1371
|
return lines;
|
|
1321
1372
|
}
|
|
1322
|
-
const maxScroll = Math.max(0,
|
|
1373
|
+
const maxScroll = Math.max(0, displayList.length - VIEWPORT);
|
|
1323
1374
|
const clampScroll = Math.min(scroll, maxScroll);
|
|
1324
|
-
const window =
|
|
1375
|
+
const window = displayList.slice(clampScroll, clampScroll + VIEWPORT);
|
|
1325
1376
|
for (let i = 0;i < window.length; i++) {
|
|
1326
1377
|
const absolute = clampScroll + i;
|
|
1327
|
-
const
|
|
1378
|
+
const row = window[i];
|
|
1379
|
+
const s = row.session;
|
|
1328
1380
|
const isSelected = absolute === selected;
|
|
1329
1381
|
const isActive = currentTarget === formatTarget(s.provider, s.id);
|
|
1382
|
+
const key = `${s.provider}:${s.id}`;
|
|
1330
1383
|
const cursor = isSelected ? accent("▶") : " ";
|
|
1384
|
+
const indent = " ".repeat(row.depth * 2);
|
|
1385
|
+
const toggle = row.hasChildren ? expandedNodes.has(key) ? muted("−") : muted("+") : " ";
|
|
1331
1386
|
const activeMark = isActive ? accent("(active)") : "";
|
|
1332
1387
|
const idText = isSelected ? accent(s.id) : text(s.id);
|
|
1333
1388
|
const providerTag = muted(`[${s.provider}]`);
|
|
1334
1389
|
const countTag = dim(`${s.count} entries`);
|
|
1335
1390
|
const dateTag = s.from ? dim(new Date(s.from).toLocaleDateString()) : "";
|
|
1336
|
-
const rowContent = ` ${cursor} ${idText} ${providerTag} ${countTag} ${dateTag} ${activeMark}`.trimEnd();
|
|
1391
|
+
const rowContent = ` ${cursor}${indent}${toggle} ${idText} ${providerTag} ${countTag} ${dateTag} ${activeMark}`.trimEnd();
|
|
1337
1392
|
lines.push(makeRow(rowContent, innerW, border));
|
|
1338
1393
|
}
|
|
1339
1394
|
for (let i = window.length;i < VIEWPORT; i++)
|
|
1340
1395
|
lines.push(makeRow("", innerW, border));
|
|
1341
|
-
const count =
|
|
1396
|
+
const count = displayList.length;
|
|
1342
1397
|
const position = count > VIEWPORT ? `${clampScroll + 1}-${Math.min(clampScroll + VIEWPORT, count)} of ${count}` : `${count} session${count === 1 ? "" : "s"}`;
|
|
1343
1398
|
lines.push(makeRow("", innerW, border));
|
|
1344
|
-
lines.push(makeRow(dim(`↑↓ navigate enter resume d delete esc close (${position})`), innerW, border));
|
|
1399
|
+
lines.push(makeRow(dim(`↑↓ navigate space/+/− fold enter resume d delete esc close (${position})`), innerW, border));
|
|
1345
1400
|
lines.push(`${borderFg("╰")}${borderFg("─".repeat(width - 2))}${borderFg("╯")}`);
|
|
1346
1401
|
return lines;
|
|
1347
1402
|
},
|
|
@@ -1350,9 +1405,9 @@ async function runSessionsPanel(ctx, executablePath) {
|
|
|
1350
1405
|
done(undefined);
|
|
1351
1406
|
return;
|
|
1352
1407
|
}
|
|
1353
|
-
if (error &&
|
|
1408
|
+
if (error && displayList.length === 0)
|
|
1354
1409
|
return;
|
|
1355
|
-
const maxScroll = Math.max(0,
|
|
1410
|
+
const maxScroll = Math.max(0, displayList.length - VIEWPORT);
|
|
1356
1411
|
if (matchesKey(data, Key.up)) {
|
|
1357
1412
|
selected = Math.max(0, selected - 1);
|
|
1358
1413
|
if (selected < scroll)
|
|
@@ -1361,7 +1416,7 @@ async function runSessionsPanel(ctx, executablePath) {
|
|
|
1361
1416
|
return;
|
|
1362
1417
|
}
|
|
1363
1418
|
if (matchesKey(data, Key.down)) {
|
|
1364
|
-
selected = Math.min(Math.max(0,
|
|
1419
|
+
selected = Math.min(Math.max(0, displayList.length - 1), selected + 1);
|
|
1365
1420
|
if (selected >= scroll + VIEWPORT)
|
|
1366
1421
|
scroll = selected - VIEWPORT + 1;
|
|
1367
1422
|
tui.requestRender();
|
|
@@ -1374,17 +1429,38 @@ async function runSessionsPanel(ctx, executablePath) {
|
|
|
1374
1429
|
return;
|
|
1375
1430
|
}
|
|
1376
1431
|
if (matchesKey(data, Key.pageDown)) {
|
|
1377
|
-
selected = Math.min(
|
|
1432
|
+
selected = Math.min(displayList.length - 1, selected + VIEWPORT);
|
|
1378
1433
|
scroll = Math.min(maxScroll, Math.max(scroll, selected - VIEWPORT + 1));
|
|
1379
1434
|
tui.requestRender();
|
|
1380
1435
|
return;
|
|
1381
1436
|
}
|
|
1437
|
+
const row = displayList[selected];
|
|
1438
|
+
if (row && row.hasChildren && (data === " " || data === "+" || data === "-")) {
|
|
1439
|
+
const key = `${row.session.provider}:${row.session.id}`;
|
|
1440
|
+
if (data === "+") {
|
|
1441
|
+
expandedNodes.add(key);
|
|
1442
|
+
} else if (data === "-") {
|
|
1443
|
+
expandedNodes.delete(key);
|
|
1444
|
+
} else if (expandedNodes.has(key)) {
|
|
1445
|
+
expandedNodes.delete(key);
|
|
1446
|
+
} else {
|
|
1447
|
+
expandedNodes.add(key);
|
|
1448
|
+
}
|
|
1449
|
+
rebuildDisplayList();
|
|
1450
|
+
const newIndex = displayList.findIndex((d) => `${d.session.provider}:${d.session.id}` === key);
|
|
1451
|
+
if (newIndex >= 0)
|
|
1452
|
+
selected = newIndex;
|
|
1453
|
+
if (selected < scroll)
|
|
1454
|
+
scroll = selected;
|
|
1455
|
+
tui.requestRender();
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1382
1458
|
if (data === "d" || data === "D" || matchesKey(data, Key.delete)) {
|
|
1383
1459
|
deleteSelected();
|
|
1384
1460
|
return;
|
|
1385
1461
|
}
|
|
1386
1462
|
if (matchesKey(data, Key.enter) || matchesKey(data, Key.return)) {
|
|
1387
|
-
const s =
|
|
1463
|
+
const s = displayList[selected]?.session;
|
|
1388
1464
|
if (!s)
|
|
1389
1465
|
return;
|
|
1390
1466
|
if (s.provider !== "pi") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.33",
|
|
4
4
|
"description": "Pi extension for goosedump session search, durable memory, session management, and compaction",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compaction",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@earendil-works/pi-tui": "^0.82.1",
|
|
41
|
-
"@jarkkojs/goosedump": "^0.12.
|
|
41
|
+
"@jarkkojs/goosedump": "^0.12.33",
|
|
42
42
|
"@sinclair/typebox": "^0.34.49"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|