jupyterlab_claude_code_extension 1.2.19 → 1.2.21
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 +3 -3
- package/lib/widget.js +40 -18
- package/package.json +1 -1
- package/src/__tests__/jupyterlab_claude_code_extension.spec.ts +12 -5
- package/src/widget.ts +45 -18
package/README.md
CHANGED
|
@@ -29,9 +29,9 @@ Chat-panel extensions re-implement the agent loop and trail the real tool. This
|
|
|
29
29
|
- **Live indicator** - a green dot marks sessions that are currently running somewhere
|
|
30
30
|
- **One-click resume** - click a row to jump back into that session in a terminal. If a terminal for the project is already open, it's reused instead of duplicated
|
|
31
31
|
- **Favorites** - star projects you keep coming back to via the right-click menu
|
|
32
|
-
- **Remove** - drop a project's Claude history from the panel via the right-click menu; the history folder is moved to the trash (it honours JupyterLab's "move files to trash" setting), not deleted permanently
|
|
33
|
-
- **Clean up parallel sessions** - when a project has accumulated extra sessions beyond the main one, a right-click menu item (showing the count in brackets) removes them all, keeping only the main session; removed files honour the same trash setting
|
|
34
|
-
- **Conversation switcher** - a right-click "Switch and Manage Sessions" submenu lists a project's other conversations by name and short session id, e.g. `home (3f2a1b9c)`, with last-activity time; pick one and it becomes the row's current conversation - the next click resumes exactly that one. The submenu shows the 5 most recent; "Manage Sessions..." opens a searchable popup over the full list where conversations can also be deleted - select one, many, or all via checkboxes, then confirm
|
|
32
|
+
- **Remove** - drop a project's Claude history from the panel via the right-click menu; a confirmation dialog names the project and warns it removes the whole project and all its conversations before anything is touched, then the history folder is moved to the trash (it honours JupyterLab's "move files to trash" setting), not deleted permanently
|
|
33
|
+
- **Clean up parallel sessions** - when a project has accumulated extra sessions beyond the main one, a right-click menu item (showing the count in brackets) removes them all, keeping only the main session; a confirmation dialog naming the project and the count appears first, and removed files honour the same trash setting
|
|
34
|
+
- **Conversation switcher** - a right-click "Switch and Manage Sessions" submenu lists a project's other conversations by name and short session id, e.g. `home (3f2a1b9c)`, with last-activity time; pick one and it becomes the row's current conversation - the next click resumes exactly that one. The submenu shows the 5 most recent; "Manage Sessions..." opens a searchable popup over the full list where conversations can also be deleted - select one, many, or all via checkboxes, then confirm in a dialog that names the project and the count (removed files honour the trash setting). Rows with multiple conversations show a branch icon with the count after the name
|
|
35
35
|
- **Branch session** - fork the current conversation into a new named session via the right-click menu (normal or skip-permissions mode); uses Claude's native `--fork-session`, opens in a new terminal, the chosen name is stamped automatically, and the fork becomes the row's current conversation
|
|
36
36
|
- **Activity at a glance** - each row shows its last activity (`now`, `5m ago`, `2h ago`, `3d ago`) in an aligned column, with the favourite star in its own column beside it; rows active within the last minute light up in the theme's brand colour (including the `now` label), rows idle for over a week dim slightly
|
|
37
37
|
- **Search** - fuzzy filter toggled by the funnel button next to refresh
|
package/lib/widget.js
CHANGED
|
@@ -331,6 +331,16 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
331
331
|
}
|
|
332
332
|
async _remove(session) {
|
|
333
333
|
var _a;
|
|
334
|
+
const name = this._lookupName(session);
|
|
335
|
+
const confirm = await showDialog({
|
|
336
|
+
title: 'Remove from Claude',
|
|
337
|
+
body: `Remove "${name}" from Claude? This deletes the entire Claude ` +
|
|
338
|
+
'project and every conversation it holds. This cannot be undone.',
|
|
339
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Remove' })]
|
|
340
|
+
});
|
|
341
|
+
if (!confirm.button.accept) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
334
344
|
this._removingPaths.add(session.encoded_path);
|
|
335
345
|
this._render();
|
|
336
346
|
try {
|
|
@@ -350,6 +360,18 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
350
360
|
}
|
|
351
361
|
}
|
|
352
362
|
async _cleanupParallel(session) {
|
|
363
|
+
const extra = session.extra_sessions;
|
|
364
|
+
const name = this._lookupName(session);
|
|
365
|
+
const confirm = await showDialog({
|
|
366
|
+
title: 'Clean Up Parallel Sessions',
|
|
367
|
+
body: `Remove ${extra} parallel session${extra === 1 ? '' : 's'} from ` +
|
|
368
|
+
`"${name}"? The main conversation is kept; the rest are moved to ` +
|
|
369
|
+
'trash. This cannot be undone.',
|
|
370
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Remove' })]
|
|
371
|
+
});
|
|
372
|
+
if (!confirm.button.accept) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
353
375
|
const body = new Widget();
|
|
354
376
|
body.node.className = 'jp-ClaudeSessionsPanel-cleanupBody';
|
|
355
377
|
const message = document.createElement('div');
|
|
@@ -1221,7 +1243,6 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1221
1243
|
// Local working copy so deletions can refresh the list in place.
|
|
1222
1244
|
let items = [...branches];
|
|
1223
1245
|
const selected = new Set();
|
|
1224
|
-
let confirmArmed = false;
|
|
1225
1246
|
const body = document.createElement('div');
|
|
1226
1247
|
body.className = 'jp-ClaudeSessionsPanel-branchPopup';
|
|
1227
1248
|
const search = document.createElement('input');
|
|
@@ -1257,12 +1278,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1257
1278
|
b.label.toLowerCase().includes(needle) ||
|
|
1258
1279
|
b.session_id.toLowerCase().includes(needle));
|
|
1259
1280
|
};
|
|
1260
|
-
// Any selection change disarms a pending confirm.
|
|
1261
1281
|
const updateControls = () => {
|
|
1262
|
-
confirmArmed = false;
|
|
1263
1282
|
deleteBtn.disabled = selected.size === 0;
|
|
1264
1283
|
deleteBtn.textContent = `Delete (${selected.size})`;
|
|
1265
|
-
deleteBtn.classList.remove('jp-mod-confirm');
|
|
1266
1284
|
const visible = visibleMatches();
|
|
1267
1285
|
const visibleSelected = visible.filter(b => selected.has(b.session_id)).length;
|
|
1268
1286
|
selectAll.checked =
|
|
@@ -1362,22 +1380,26 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1362
1380
|
if (selected.size === 0) {
|
|
1363
1381
|
return;
|
|
1364
1382
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
if (deleted === null) {
|
|
1383
|
+
const n = selected.size;
|
|
1384
|
+
void showDialog({
|
|
1385
|
+
title: 'Delete Sessions',
|
|
1386
|
+
body: `Delete ${n} session${n === 1 ? '' : 's'} from "${this._activeSession ? this._lookupName(this._activeSession) : ''}"? ` +
|
|
1387
|
+
'They are moved to trash. This cannot be undone.',
|
|
1388
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Delete' })]
|
|
1389
|
+
}).then(confirm => {
|
|
1390
|
+
if (!confirm.button.accept) {
|
|
1374
1391
|
return;
|
|
1375
1392
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1393
|
+
void this._deleteBranches([...selected]).then(deleted => {
|
|
1394
|
+
if (deleted === null) {
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
items = items.filter(b => !selected.has(b.session_id));
|
|
1398
|
+
selected.clear();
|
|
1399
|
+
this._lastBranches = items;
|
|
1400
|
+
render();
|
|
1401
|
+
updateControls();
|
|
1402
|
+
});
|
|
1381
1403
|
});
|
|
1382
1404
|
});
|
|
1383
1405
|
search.addEventListener('input', () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab_claude_code_extension",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.21",
|
|
4
4
|
"description": "Browse, resume, and manage your Claude Code CLI sessions from a JupyterLab side panel. One click reactivates the right terminal - no duplicate tabs, live remote-control indicator, and favourites for the projects you keep coming back to.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -96,6 +96,14 @@ describe('launch spinner dismiss contract', () => {
|
|
|
96
96
|
/private async _cleanupParallel[\s\S]*?\n \}/
|
|
97
97
|
) ?? [''])[0];
|
|
98
98
|
|
|
99
|
+
it('confirms before removing and aborts when not accepted', () => {
|
|
100
|
+
expect(cleanup).toMatch(
|
|
101
|
+
/showDialog\(\{\s*title: 'Clean Up Parallel Sessions'/
|
|
102
|
+
);
|
|
103
|
+
expect(cleanup).toMatch(/Dialog\.warnButton\(\{ label: 'Remove' \}\)/);
|
|
104
|
+
expect(cleanup).toMatch(/if \(!confirm\.button\.accept\) \{\s*return;/);
|
|
105
|
+
});
|
|
106
|
+
|
|
99
107
|
it('creates a progress element in the dialog body', () => {
|
|
100
108
|
expect(cleanup).toMatch(/createElement\('progress'\)/);
|
|
101
109
|
});
|
|
@@ -232,15 +240,14 @@ describe('launch spinner dismiss contract', () => {
|
|
|
232
240
|
);
|
|
233
241
|
});
|
|
234
242
|
|
|
235
|
-
it('delete
|
|
243
|
+
it('delete opens a confirmation dialog and only deletes on accept', () => {
|
|
236
244
|
const popup = (widgetSrc.match(
|
|
237
245
|
/private _showBranchPopup[\s\S]*?\n \}/
|
|
238
246
|
) ?? [''])[0];
|
|
239
|
-
expect(popup).toMatch(
|
|
240
|
-
|
|
241
|
-
// state and the button label.
|
|
247
|
+
expect(popup).toMatch(/showDialog\(\{\s*title: 'Delete Sessions'/);
|
|
248
|
+
expect(popup).toMatch(/Dialog\.warnButton\(\{ label: 'Delete' \}\)/);
|
|
242
249
|
expect(popup).toMatch(
|
|
243
|
-
/
|
|
250
|
+
/if \(!confirm\.button\.accept\) \{\s*return;[\s\S]*?_deleteBranches/
|
|
244
251
|
);
|
|
245
252
|
});
|
|
246
253
|
|
package/src/widget.ts
CHANGED
|
@@ -407,6 +407,18 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
private async _remove(session: ISession): Promise<void> {
|
|
410
|
+
const name = this._lookupName(session);
|
|
411
|
+
const confirm = await showDialog({
|
|
412
|
+
title: 'Remove from Claude',
|
|
413
|
+
body:
|
|
414
|
+
`Remove "${name}" from Claude? This deletes the entire Claude ` +
|
|
415
|
+
'project and every conversation it holds. This cannot be undone.',
|
|
416
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Remove' })]
|
|
417
|
+
});
|
|
418
|
+
if (!confirm.button.accept) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
410
422
|
this._removingPaths.add(session.encoded_path);
|
|
411
423
|
this._render();
|
|
412
424
|
try {
|
|
@@ -431,6 +443,20 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
431
443
|
}
|
|
432
444
|
|
|
433
445
|
private async _cleanupParallel(session: ISession): Promise<void> {
|
|
446
|
+
const extra = session.extra_sessions;
|
|
447
|
+
const name = this._lookupName(session);
|
|
448
|
+
const confirm = await showDialog({
|
|
449
|
+
title: 'Clean Up Parallel Sessions',
|
|
450
|
+
body:
|
|
451
|
+
`Remove ${extra} parallel session${extra === 1 ? '' : 's'} from ` +
|
|
452
|
+
`"${name}"? The main conversation is kept; the rest are moved to ` +
|
|
453
|
+
'trash. This cannot be undone.',
|
|
454
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Remove' })]
|
|
455
|
+
});
|
|
456
|
+
if (!confirm.button.accept) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
|
|
434
460
|
const body = new Widget();
|
|
435
461
|
body.node.className = 'jp-ClaudeSessionsPanel-cleanupBody';
|
|
436
462
|
|
|
@@ -1420,7 +1446,6 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1420
1446
|
// Local working copy so deletions can refresh the list in place.
|
|
1421
1447
|
let items = [...branches];
|
|
1422
1448
|
const selected = new Set<string>();
|
|
1423
|
-
let confirmArmed = false;
|
|
1424
1449
|
|
|
1425
1450
|
const body = document.createElement('div');
|
|
1426
1451
|
body.className = 'jp-ClaudeSessionsPanel-branchPopup';
|
|
@@ -1467,12 +1492,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1467
1492
|
);
|
|
1468
1493
|
};
|
|
1469
1494
|
|
|
1470
|
-
// Any selection change disarms a pending confirm.
|
|
1471
1495
|
const updateControls = () => {
|
|
1472
|
-
confirmArmed = false;
|
|
1473
1496
|
deleteBtn.disabled = selected.size === 0;
|
|
1474
1497
|
deleteBtn.textContent = `Delete (${selected.size})`;
|
|
1475
|
-
deleteBtn.classList.remove('jp-mod-confirm');
|
|
1476
1498
|
const visible = visibleMatches();
|
|
1477
1499
|
const visibleSelected = visible.filter(b =>
|
|
1478
1500
|
selected.has(b.session_id)
|
|
@@ -1580,22 +1602,27 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1580
1602
|
if (selected.size === 0) {
|
|
1581
1603
|
return;
|
|
1582
1604
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
if (
|
|
1605
|
+
const n = selected.size;
|
|
1606
|
+
void showDialog({
|
|
1607
|
+
title: 'Delete Sessions',
|
|
1608
|
+
body:
|
|
1609
|
+
`Delete ${n} session${n === 1 ? '' : 's'} from "${this._activeSession ? this._lookupName(this._activeSession) : ''}"? ` +
|
|
1610
|
+
'They are moved to trash. This cannot be undone.',
|
|
1611
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Delete' })]
|
|
1612
|
+
}).then(confirm => {
|
|
1613
|
+
if (!confirm.button.accept) {
|
|
1592
1614
|
return;
|
|
1593
1615
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1616
|
+
void this._deleteBranches([...selected]).then(deleted => {
|
|
1617
|
+
if (deleted === null) {
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
items = items.filter(b => !selected.has(b.session_id));
|
|
1621
|
+
selected.clear();
|
|
1622
|
+
this._lastBranches = items;
|
|
1623
|
+
render();
|
|
1624
|
+
updateControls();
|
|
1625
|
+
});
|
|
1599
1626
|
});
|
|
1600
1627
|
});
|
|
1601
1628
|
|