jupyterlab_claude_code_extension 1.1.7 → 1.1.8
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/lib/index.js +4 -3
- package/lib/widget.d.ts +7 -10
- package/lib/widget.js +17 -39
- package/package.json +1 -1
- package/src/index.ts +5 -4
- package/src/widget.ts +18 -45
package/lib/index.js
CHANGED
|
@@ -39,9 +39,10 @@ const plugin = {
|
|
|
39
39
|
labShell.add(widget, currentSidebar, { rank: 600 });
|
|
40
40
|
const apply = () => {
|
|
41
41
|
const mode = settings.get('presentationMode').composite;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
// 'session' was a pre-1.1.8 mode that pulled labels from the
|
|
43
|
+
// session's `name` field; we now ignore that field entirely, so
|
|
44
|
+
// any value other than 'path' is treated as 'folder'.
|
|
45
|
+
widget.setPresentationMode(mode === 'path' ? 'path' : 'folder');
|
|
45
46
|
const limit = settings.get('recentLimit').composite;
|
|
46
47
|
if (typeof limit === 'number') {
|
|
47
48
|
widget.setRecentLimit(limit);
|
package/lib/widget.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
|
2
2
|
import { ITerminalTracker } from '@jupyterlab/terminal';
|
|
3
3
|
import { Widget } from '@lumino/widgets';
|
|
4
4
|
import { Message } from '@lumino/messaging';
|
|
5
|
-
export type PresentationMode = '
|
|
5
|
+
export type PresentationMode = 'folder' | 'path';
|
|
6
6
|
export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
7
7
|
constructor(app: JupyterFrontEnd, rootDir: string, terminalTracker?: ITerminalTracker | null);
|
|
8
8
|
refresh(): void;
|
|
@@ -54,18 +54,15 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
54
54
|
*/
|
|
55
55
|
private _showLaunchSpinner;
|
|
56
56
|
private _wireTerminalDisposal;
|
|
57
|
-
/** Apply the presentation-mode setting (
|
|
58
|
-
*
|
|
57
|
+
/** Apply the presentation-mode setting (basename collisions for folder
|
|
58
|
+
* mode are resolved separately in ``_disambiguate``). */
|
|
59
59
|
private _displayName;
|
|
60
60
|
private _basename;
|
|
61
61
|
/** Walk path tails until every name in a colliding group is unique.
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* folder basename. Basename-derived rows in the same group get the path
|
|
67
|
-
* tail suffix, picked so it stays distinct from every rename label too.
|
|
68
|
-
*/
|
|
62
|
+
* Folder-mode labels are folder basenames, so two different projects can
|
|
63
|
+
* end up with the same label (e.g. two `datascience` folders under
|
|
64
|
+
* different parents); we extend each colliding label with as much of its
|
|
65
|
+
* parent path as it takes to make it unique. */
|
|
69
66
|
private _disambiguate;
|
|
70
67
|
private _render;
|
|
71
68
|
private _renderSection;
|
package/lib/widget.js
CHANGED
|
@@ -7,7 +7,7 @@ import { claudeIcon, refreshIcon, removeIcon, shieldIcon, starFilledIcon } from
|
|
|
7
7
|
const POLL_INTERVAL_MS = 30000;
|
|
8
8
|
const DEFAULT_RECENT_LIMIT = 10;
|
|
9
9
|
const EXPANDED_STORAGE_KEY = 'jupyterlab_claude_code_extension:expanded';
|
|
10
|
-
const DEFAULT_PRESENTATION_MODE = '
|
|
10
|
+
const DEFAULT_PRESENTATION_MODE = 'folder';
|
|
11
11
|
const SECTION_LABELS = {
|
|
12
12
|
favourites: 'Favorites',
|
|
13
13
|
recent: 'Recent',
|
|
@@ -487,19 +487,14 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
487
487
|
});
|
|
488
488
|
}
|
|
489
489
|
// -------------------------------------------------------------- rendering
|
|
490
|
-
/** Apply the presentation-mode setting (
|
|
491
|
-
*
|
|
490
|
+
/** Apply the presentation-mode setting (basename collisions for folder
|
|
491
|
+
* mode are resolved separately in ``_disambiguate``). */
|
|
492
492
|
_displayName(s) {
|
|
493
493
|
const folder = this._basename(s.project_path) || s.encoded_path;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return folder;
|
|
497
|
-
case 'path':
|
|
498
|
-
return this._displayPath(s.project_path) || folder;
|
|
499
|
-
case 'session':
|
|
500
|
-
default:
|
|
501
|
-
return s.name || folder;
|
|
494
|
+
if (this._presentationMode === 'path') {
|
|
495
|
+
return this._displayPath(s.project_path) || folder;
|
|
502
496
|
}
|
|
497
|
+
return folder;
|
|
503
498
|
}
|
|
504
499
|
_basename(p) {
|
|
505
500
|
if (!p) {
|
|
@@ -509,13 +504,10 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
509
504
|
return parts[parts.length - 1] || '';
|
|
510
505
|
}
|
|
511
506
|
/** Walk path tails until every name in a colliding group is unique.
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
* folder basename. Basename-derived rows in the same group get the path
|
|
517
|
-
* tail suffix, picked so it stays distinct from every rename label too.
|
|
518
|
-
*/
|
|
507
|
+
* Folder-mode labels are folder basenames, so two different projects can
|
|
508
|
+
* end up with the same label (e.g. two `datascience` folders under
|
|
509
|
+
* different parents); we extend each colliding label with as much of its
|
|
510
|
+
* parent path as it takes to make it unique. */
|
|
519
511
|
_disambiguate(rows) {
|
|
520
512
|
var _a;
|
|
521
513
|
const out = new Map();
|
|
@@ -529,38 +521,24 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
529
521
|
out.set(group[0].project_path, name);
|
|
530
522
|
continue;
|
|
531
523
|
}
|
|
532
|
-
const
|
|
533
|
-
const basenames = group.filter(r => r.name_source !== 'rename');
|
|
534
|
-
// Rename rows win unchanged. The ``taken`` set guards basename
|
|
535
|
-
// disambiguation from colliding back into a rename's label.
|
|
536
|
-
const taken = new Set();
|
|
537
|
-
for (const r of renames) {
|
|
538
|
-
out.set(r.project_path, r.name);
|
|
539
|
-
taken.add(r.name);
|
|
540
|
-
}
|
|
541
|
-
if (basenames.length === 0) {
|
|
542
|
-
continue;
|
|
543
|
-
}
|
|
544
|
-
// Walk path tails for basename rows: tail must be unique among
|
|
545
|
-
// basenames AND not equal to any rename label already taken.
|
|
546
|
-
const segs = basenames.map(r => r.project_path.split('/').filter(Boolean));
|
|
524
|
+
const segs = group.map(r => r.project_path.split('/').filter(Boolean));
|
|
547
525
|
const max = Math.max(...segs.map(s => s.length));
|
|
548
526
|
let depth = 1;
|
|
549
527
|
let resolved = false;
|
|
550
528
|
while (depth <= max) {
|
|
551
529
|
const tails = segs.map(s => s.slice(-depth).join('/'));
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
if (unique && noConflict) {
|
|
555
|
-
basenames.forEach((r, i) => out.set(r.project_path, tails[i]));
|
|
530
|
+
if (new Set(tails).size === tails.length) {
|
|
531
|
+
group.forEach((r, i) => out.set(r.project_path, tails[i]));
|
|
556
532
|
resolved = true;
|
|
557
533
|
break;
|
|
558
534
|
}
|
|
559
535
|
depth += 1;
|
|
560
536
|
}
|
|
561
537
|
if (!resolved) {
|
|
562
|
-
//
|
|
563
|
-
|
|
538
|
+
// Identical project_path values across rows shouldn't happen
|
|
539
|
+
// (list_sessions dedups by path) but if it ever does, fall back to
|
|
540
|
+
// the absolute path so rows stay distinguishable.
|
|
541
|
+
group.forEach(r => out.set(r.project_path, r.project_path));
|
|
564
542
|
}
|
|
565
543
|
}
|
|
566
544
|
return out;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab_claude_code_extension",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
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",
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { ITerminalTracker } from '@jupyterlab/terminal';
|
|
|
9
9
|
|
|
10
10
|
import { requestAPI } from './request';
|
|
11
11
|
import { IStatusResponse } from './types';
|
|
12
|
-
import { ClaudeCodeSessionsWidget
|
|
12
|
+
import { ClaudeCodeSessionsWidget } from './widget';
|
|
13
13
|
|
|
14
14
|
const PLUGIN_ID = 'jupyterlab_claude_code_extension:plugin';
|
|
15
15
|
const WIDGET_ID = 'jupyterlab-claude-code-extension';
|
|
@@ -69,9 +69,10 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
|
|
69
69
|
|
|
70
70
|
const apply = (): void => {
|
|
71
71
|
const mode = settings.get('presentationMode').composite as string;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
// 'session' was a pre-1.1.8 mode that pulled labels from the
|
|
73
|
+
// session's `name` field; we now ignore that field entirely, so
|
|
74
|
+
// any value other than 'path' is treated as 'folder'.
|
|
75
|
+
widget.setPresentationMode(mode === 'path' ? 'path' : 'folder');
|
|
75
76
|
const limit = settings.get('recentLimit').composite as number;
|
|
76
77
|
if (typeof limit === 'number') {
|
|
77
78
|
widget.setRecentLimit(limit);
|
package/src/widget.ts
CHANGED
|
@@ -34,9 +34,9 @@ const EXPANDED_STORAGE_KEY = 'jupyterlab_claude_code_extension:expanded';
|
|
|
34
34
|
|
|
35
35
|
type SectionKey = 'favourites' | 'recent' | 'all';
|
|
36
36
|
|
|
37
|
-
export type PresentationMode = '
|
|
37
|
+
export type PresentationMode = 'folder' | 'path';
|
|
38
38
|
|
|
39
|
-
const DEFAULT_PRESENTATION_MODE: PresentationMode = '
|
|
39
|
+
const DEFAULT_PRESENTATION_MODE: PresentationMode = 'folder';
|
|
40
40
|
|
|
41
41
|
const SECTION_LABELS: Record<SectionKey, string> = {
|
|
42
42
|
favourites: 'Favorites',
|
|
@@ -585,19 +585,14 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
585
585
|
|
|
586
586
|
// -------------------------------------------------------------- rendering
|
|
587
587
|
|
|
588
|
-
/** Apply the presentation-mode setting (
|
|
589
|
-
*
|
|
588
|
+
/** Apply the presentation-mode setting (basename collisions for folder
|
|
589
|
+
* mode are resolved separately in ``_disambiguate``). */
|
|
590
590
|
private _displayName(s: ISession): string {
|
|
591
591
|
const folder = this._basename(s.project_path) || s.encoded_path;
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
return folder;
|
|
595
|
-
case 'path':
|
|
596
|
-
return this._displayPath(s.project_path) || folder;
|
|
597
|
-
case 'session':
|
|
598
|
-
default:
|
|
599
|
-
return s.name || folder;
|
|
592
|
+
if (this._presentationMode === 'path') {
|
|
593
|
+
return this._displayPath(s.project_path) || folder;
|
|
600
594
|
}
|
|
595
|
+
return folder;
|
|
601
596
|
}
|
|
602
597
|
|
|
603
598
|
private _basename(p: string): string {
|
|
@@ -609,13 +604,10 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
609
604
|
}
|
|
610
605
|
|
|
611
606
|
/** Walk path tails until every name in a colliding group is unique.
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
* folder basename. Basename-derived rows in the same group get the path
|
|
617
|
-
* tail suffix, picked so it stays distinct from every rename label too.
|
|
618
|
-
*/
|
|
607
|
+
* Folder-mode labels are folder basenames, so two different projects can
|
|
608
|
+
* end up with the same label (e.g. two `datascience` folders under
|
|
609
|
+
* different parents); we extend each colliding label with as much of its
|
|
610
|
+
* parent path as it takes to make it unique. */
|
|
619
611
|
private _disambiguate(rows: ISession[]): Map<string, string> {
|
|
620
612
|
const out = new Map<string, string>();
|
|
621
613
|
const groups = new Map<string, ISession[]>();
|
|
@@ -628,43 +620,24 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
628
620
|
out.set(group[0].project_path, name);
|
|
629
621
|
continue;
|
|
630
622
|
}
|
|
631
|
-
|
|
632
|
-
const renames = group.filter(r => r.name_source === 'rename');
|
|
633
|
-
const basenames = group.filter(r => r.name_source !== 'rename');
|
|
634
|
-
|
|
635
|
-
// Rename rows win unchanged. The ``taken`` set guards basename
|
|
636
|
-
// disambiguation from colliding back into a rename's label.
|
|
637
|
-
const taken = new Set<string>();
|
|
638
|
-
for (const r of renames) {
|
|
639
|
-
out.set(r.project_path, r.name);
|
|
640
|
-
taken.add(r.name);
|
|
641
|
-
}
|
|
642
|
-
if (basenames.length === 0) {
|
|
643
|
-
continue;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// Walk path tails for basename rows: tail must be unique among
|
|
647
|
-
// basenames AND not equal to any rename label already taken.
|
|
648
|
-
const segs = basenames.map(r =>
|
|
649
|
-
r.project_path.split('/').filter(Boolean)
|
|
650
|
-
);
|
|
623
|
+
const segs = group.map(r => r.project_path.split('/').filter(Boolean));
|
|
651
624
|
const max = Math.max(...segs.map(s => s.length));
|
|
652
625
|
let depth = 1;
|
|
653
626
|
let resolved = false;
|
|
654
627
|
while (depth <= max) {
|
|
655
628
|
const tails = segs.map(s => s.slice(-depth).join('/'));
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
if (unique && noConflict) {
|
|
659
|
-
basenames.forEach((r, i) => out.set(r.project_path, tails[i]));
|
|
629
|
+
if (new Set(tails).size === tails.length) {
|
|
630
|
+
group.forEach((r, i) => out.set(r.project_path, tails[i]));
|
|
660
631
|
resolved = true;
|
|
661
632
|
break;
|
|
662
633
|
}
|
|
663
634
|
depth += 1;
|
|
664
635
|
}
|
|
665
636
|
if (!resolved) {
|
|
666
|
-
//
|
|
667
|
-
|
|
637
|
+
// Identical project_path values across rows shouldn't happen
|
|
638
|
+
// (list_sessions dedups by path) but if it ever does, fall back to
|
|
639
|
+
// the absolute path so rows stay distinguishable.
|
|
640
|
+
group.forEach(r => out.set(r.project_path, r.project_path));
|
|
668
641
|
}
|
|
669
642
|
}
|
|
670
643
|
return out;
|