jupyterlab_claude_code_extension 1.2.2 → 1.2.5
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/widget.d.ts +5 -0
- package/lib/widget.js +18 -7
- package/package.json +1 -1
- package/src/__tests__/jupyterlab_claude_code_extension.spec.ts +31 -0
- package/src/widget.ts +20 -7
- package/style/base.css +8 -0
package/lib/widget.d.ts
CHANGED
|
@@ -92,6 +92,11 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
92
92
|
* case the file browser has no way to address it. */
|
|
93
93
|
private _pathUnderRoot;
|
|
94
94
|
private _formatRelativeTime;
|
|
95
|
+
/** Branch entry display: conversation name plus short session id in
|
|
96
|
+
* brackets; branches share the project path so only the name and id
|
|
97
|
+
* distinguish them. Skips the suffix when the label already is the
|
|
98
|
+
* short id (the backend's last-resort fallback). */
|
|
99
|
+
private _branchDisplayName;
|
|
95
100
|
private _setRefreshSpinning;
|
|
96
101
|
private _setActiveRow;
|
|
97
102
|
private _setupContextMenu;
|
package/lib/widget.js
CHANGED
|
@@ -812,6 +812,12 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
812
812
|
? `${this._lookupName(session)} (${session.extra_sessions + 1})`
|
|
813
813
|
: this._lookupName(session);
|
|
814
814
|
row.appendChild(name);
|
|
815
|
+
if (session.file_mtime) {
|
|
816
|
+
const time = document.createElement('span');
|
|
817
|
+
time.className = 'jp-ClaudeSessionsPanel-rowTime';
|
|
818
|
+
time.textContent = this._formatRelativeTime(session.file_mtime);
|
|
819
|
+
row.appendChild(time);
|
|
820
|
+
}
|
|
815
821
|
// No star in the Favorites section - every row there is a favorite
|
|
816
822
|
// by definition; stars are an indicator only useful in Recent/All.
|
|
817
823
|
if (session.favourite && sectionKey !== 'favourites') {
|
|
@@ -898,7 +904,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
898
904
|
}
|
|
899
905
|
const diff = Date.now() - epochMs;
|
|
900
906
|
if (diff < 60000) {
|
|
901
|
-
return '
|
|
907
|
+
return 'now';
|
|
902
908
|
}
|
|
903
909
|
if (diff < 3600000) {
|
|
904
910
|
return `${Math.floor(diff / 60000)}m ago`;
|
|
@@ -906,10 +912,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
906
912
|
if (diff < 86400000) {
|
|
907
913
|
return `${Math.floor(diff / 3600000)}h ago`;
|
|
908
914
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
915
|
+
return `${Math.floor(diff / 86400000)}d ago`;
|
|
916
|
+
}
|
|
917
|
+
/** Branch entry display: conversation name plus short session id in
|
|
918
|
+
* brackets; branches share the project path so only the name and id
|
|
919
|
+
* distinguish them. Skips the suffix when the label already is the
|
|
920
|
+
* short id (the backend's last-resort fallback). */
|
|
921
|
+
_branchDisplayName(b) {
|
|
922
|
+
const shortId = b.session_id.slice(0, 8);
|
|
923
|
+
return b.label === shortId ? b.label : `${b.label} (${shortId})`;
|
|
913
924
|
}
|
|
914
925
|
_setRefreshSpinning(on) {
|
|
915
926
|
if (!this._refreshBtn) {
|
|
@@ -1130,7 +1141,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1130
1141
|
command: 'claude-code-sessions:switch-branch',
|
|
1131
1142
|
args: {
|
|
1132
1143
|
session_id: b.session_id,
|
|
1133
|
-
label: `${b
|
|
1144
|
+
label: `${this._branchDisplayName(b)} - ${this._formatRelativeTime(b.file_mtime)}`
|
|
1134
1145
|
}
|
|
1135
1146
|
});
|
|
1136
1147
|
}
|
|
@@ -1187,7 +1198,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1187
1198
|
row.title = `Session id: ${b.session_id}`;
|
|
1188
1199
|
const label = document.createElement('span');
|
|
1189
1200
|
label.className = 'jp-ClaudeSessionsPanel-branchLabel';
|
|
1190
|
-
label.textContent = b
|
|
1201
|
+
label.textContent = this._branchDisplayName(b);
|
|
1191
1202
|
row.appendChild(label);
|
|
1192
1203
|
const time = document.createElement('span');
|
|
1193
1204
|
time.className = 'jp-ClaudeSessionsPanel-branchTime';
|
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.5",
|
|
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",
|
|
@@ -157,6 +157,20 @@ describe('launch spinner dismiss contract', () => {
|
|
|
157
157
|
);
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
it('branch entries render name plus short id via _branchDisplayName', () => {
|
|
161
|
+
const display = (widgetSrc.match(
|
|
162
|
+
/private _branchDisplayName[\s\S]*?\n \}/
|
|
163
|
+
) ?? [''])[0];
|
|
164
|
+
expect(display).toMatch(/session_id\.slice\(0, 8\)/);
|
|
165
|
+
expect(display).toMatch(/`\$\{b\.label\} \(\$\{shortId\}\)`/);
|
|
166
|
+
expect(widgetSrc).toMatch(
|
|
167
|
+
/label: `\$\{this\._branchDisplayName\(b\)\} - \$\{this\._formatRelativeTime\(b\.file_mtime\)\}`/
|
|
168
|
+
);
|
|
169
|
+
expect(widgetSrc).toMatch(
|
|
170
|
+
/label\.textContent = this\._branchDisplayName\(b\)/
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
160
174
|
it('More... popup filters by label or session id and switches on click', () => {
|
|
161
175
|
const popup = (widgetSrc.match(
|
|
162
176
|
/private _showBranchPopup[\s\S]*?\n \}/
|
|
@@ -182,6 +196,23 @@ describe('launch spinner dismiss contract', () => {
|
|
|
182
196
|
expect(switchBranch).toMatch(/Branch no longer exists/);
|
|
183
197
|
});
|
|
184
198
|
|
|
199
|
+
it('shows last activity on session rows via the shared formatter', () => {
|
|
200
|
+
expect(widgetSrc).toMatch(
|
|
201
|
+
/jp-ClaudeSessionsPanel-rowTime[\s\S]*?_formatRelativeTime\(session\.file_mtime\)/
|
|
202
|
+
);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('formats relative time as now / m / h / d ago, no date fallback', () => {
|
|
206
|
+
const fmt = (widgetSrc.match(
|
|
207
|
+
/private _formatRelativeTime[\s\S]*?\n \}/
|
|
208
|
+
) ?? [''])[0];
|
|
209
|
+
expect(fmt).toMatch(/return 'now'/);
|
|
210
|
+
expect(fmt).toMatch(/m ago/);
|
|
211
|
+
expect(fmt).toMatch(/h ago/);
|
|
212
|
+
expect(fmt).toMatch(/d ago/);
|
|
213
|
+
expect(fmt).not.toMatch(/toLocaleDateString/);
|
|
214
|
+
});
|
|
215
|
+
|
|
185
216
|
it('warns when the resolved current differs from the requested branch', () => {
|
|
186
217
|
expect(switchBranch).toMatch(
|
|
187
218
|
/result\.current !== result\.requested[\s\S]*?Notification\.warning/
|
package/src/widget.ts
CHANGED
|
@@ -958,6 +958,13 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
958
958
|
: this._lookupName(session);
|
|
959
959
|
row.appendChild(name);
|
|
960
960
|
|
|
961
|
+
if (session.file_mtime) {
|
|
962
|
+
const time = document.createElement('span');
|
|
963
|
+
time.className = 'jp-ClaudeSessionsPanel-rowTime';
|
|
964
|
+
time.textContent = this._formatRelativeTime(session.file_mtime);
|
|
965
|
+
row.appendChild(time);
|
|
966
|
+
}
|
|
967
|
+
|
|
961
968
|
// No star in the Favorites section - every row there is a favorite
|
|
962
969
|
// by definition; stars are an indicator only useful in Recent/All.
|
|
963
970
|
if (session.favourite && sectionKey !== 'favourites') {
|
|
@@ -1050,7 +1057,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1050
1057
|
}
|
|
1051
1058
|
const diff = Date.now() - epochMs;
|
|
1052
1059
|
if (diff < 60_000) {
|
|
1053
|
-
return '
|
|
1060
|
+
return 'now';
|
|
1054
1061
|
}
|
|
1055
1062
|
if (diff < 3_600_000) {
|
|
1056
1063
|
return `${Math.floor(diff / 60_000)}m ago`;
|
|
@@ -1058,10 +1065,16 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1058
1065
|
if (diff < 86_400_000) {
|
|
1059
1066
|
return `${Math.floor(diff / 3_600_000)}h ago`;
|
|
1060
1067
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1068
|
+
return `${Math.floor(diff / 86_400_000)}d ago`;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/** Branch entry display: conversation name plus short session id in
|
|
1072
|
+
* brackets; branches share the project path so only the name and id
|
|
1073
|
+
* distinguish them. Skips the suffix when the label already is the
|
|
1074
|
+
* short id (the backend's last-resort fallback). */
|
|
1075
|
+
private _branchDisplayName(b: IBranch): string {
|
|
1076
|
+
const shortId = b.session_id.slice(0, 8);
|
|
1077
|
+
return b.label === shortId ? b.label : `${b.label} (${shortId})`;
|
|
1065
1078
|
}
|
|
1066
1079
|
|
|
1067
1080
|
private _setRefreshSpinning(on: boolean): void {
|
|
@@ -1316,7 +1329,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1316
1329
|
command: 'claude-code-sessions:switch-branch',
|
|
1317
1330
|
args: {
|
|
1318
1331
|
session_id: b.session_id,
|
|
1319
|
-
label: `${b
|
|
1332
|
+
label: `${this._branchDisplayName(b)} - ${this._formatRelativeTime(b.file_mtime)}`
|
|
1320
1333
|
}
|
|
1321
1334
|
});
|
|
1322
1335
|
}
|
|
@@ -1381,7 +1394,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1381
1394
|
|
|
1382
1395
|
const label = document.createElement('span');
|
|
1383
1396
|
label.className = 'jp-ClaudeSessionsPanel-branchLabel';
|
|
1384
|
-
label.textContent = b
|
|
1397
|
+
label.textContent = this._branchDisplayName(b);
|
|
1385
1398
|
row.appendChild(label);
|
|
1386
1399
|
|
|
1387
1400
|
const time = document.createElement('span');
|
package/style/base.css
CHANGED
|
@@ -346,3 +346,11 @@
|
|
|
346
346
|
color: var(--jp-ui-font-color2);
|
|
347
347
|
font-size: var(--jp-ui-font-size0);
|
|
348
348
|
}
|
|
349
|
+
|
|
350
|
+
/* Last-activity time on session rows - dim, right of the name. */
|
|
351
|
+
.jp-ClaudeSessionsPanel-rowTime {
|
|
352
|
+
flex: 0 0 auto;
|
|
353
|
+
margin-left: 6px;
|
|
354
|
+
color: var(--jp-ui-font-color2);
|
|
355
|
+
font-size: var(--jp-ui-font-size0);
|
|
356
|
+
}
|