jupyterlab_ai_code_assistants_extension 1.2.294 → 1.2.299
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/core/panel.js +25 -25
- package/package.json +1 -1
- package/src/__tests__/panel.spec.ts +54 -21
- package/src/core/panel.ts +25 -23
- package/style/base.css +1 -1
package/lib/core/panel.js
CHANGED
|
@@ -786,23 +786,23 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
786
786
|
};
|
|
787
787
|
askCount(extra);
|
|
788
788
|
confirmBody.node.appendChild(question);
|
|
789
|
-
//
|
|
789
|
+
// The second line states what happens to the rest: the count, the short
|
|
790
|
+
// ids once the branch list names them, and the disposal the setting chose.
|
|
790
791
|
const hint = document.createElement('p');
|
|
791
792
|
hint.className = 'jp-AiAssistantsPanel-cleanupHint';
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
793
|
+
// Announced when restated with the short ids; an aria-label change is not.
|
|
794
|
+
hint.setAttribute('role', 'status');
|
|
795
|
+
const stateHint = (ids) => {
|
|
796
|
+
const shortIds = ids === null || ids === void 0 ? void 0 : ids.map(id => shortSessionId(id, this._descriptor.sessionIdPrefix));
|
|
797
|
+
hint.textContent =
|
|
798
|
+
`Remaining ${extra} session${extra === 1 ? '' : 's'}` +
|
|
799
|
+
((shortIds === null || shortIds === void 0 ? void 0 : shortIds.length) ? ` ${shortIds.join(', ')}` : '') +
|
|
800
|
+
(this._deleteToTrash
|
|
801
|
+
? ' will be removed.'
|
|
802
|
+
: ' will be deleted permanently.');
|
|
803
|
+
};
|
|
804
|
+
stateHint(null);
|
|
795
805
|
confirmBody.node.appendChild(hint);
|
|
796
|
-
// The irreversible note is a safety statement, not the hint, so it keeps
|
|
797
|
-
// its own line. With the trash on it names the refusal: a refused move
|
|
798
|
-
// deletes permanently, while the hint promises the trash
|
|
799
|
-
// (DEF-PANE-363-CAUTION-REFUSAL).
|
|
800
|
-
const caution = document.createElement('p');
|
|
801
|
-
caution.className = 'jp-AiAssistantsPanel-cleanupCaution';
|
|
802
|
-
caution.textContent = this._deleteToTrash
|
|
803
|
-
? 'A refused trash move deletes permanently and cannot be undone.'
|
|
804
|
-
: 'This cannot be undone.';
|
|
805
|
-
confirmBody.node.appendChild(caution);
|
|
806
806
|
const title = this._trans.__('Clean Up Parallel Sessions');
|
|
807
807
|
let warning = null;
|
|
808
808
|
const confirmDialog = new Dialog({
|
|
@@ -819,16 +819,15 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
819
819
|
// Scopes the armed style in base.css to this one dialog.
|
|
820
820
|
confirmDialog.addClass('jp-AiAssistantsPanel-cleanupDialog');
|
|
821
821
|
// The Dialog names itself only when its body is a string, and this body is
|
|
822
|
-
// a Widget: name it here, and again when the question
|
|
823
|
-
// warning joins it.
|
|
822
|
+
// a Widget: name it here, and again when the question and hint are
|
|
823
|
+
// restated and the warning joins it.
|
|
824
824
|
const nameDialog = () => {
|
|
825
|
-
var _a, _b, _c
|
|
825
|
+
var _a, _b, _c;
|
|
826
826
|
confirmDialog.node.setAttribute('aria-label', [
|
|
827
827
|
title,
|
|
828
828
|
(_a = question.textContent) !== null && _a !== void 0 ? _a : '',
|
|
829
829
|
(_b = hint.textContent) !== null && _b !== void 0 ? _b : '',
|
|
830
|
-
(_c =
|
|
831
|
-
(_d = warning === null || warning === void 0 ? void 0 : warning.textContent) !== null && _d !== void 0 ? _d : ''
|
|
830
|
+
(_c = warning === null || warning === void 0 ? void 0 : warning.textContent) !== null && _c !== void 0 ? _c : ''
|
|
832
831
|
]
|
|
833
832
|
.filter(part => part)
|
|
834
833
|
.join(' '));
|
|
@@ -935,11 +934,11 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
935
934
|
updateAccept();
|
|
936
935
|
// Alongside the question, never in front of it: the request layer allows
|
|
937
936
|
// 60 s, and the confirmation must open at once. When the list lands the
|
|
938
|
-
// question
|
|
939
|
-
// adds the warning, drawn last so it reads after the
|
|
940
|
-
// list is what the removal deletes. A failed fetch leaves
|
|
941
|
-
// the row reported it, with no
|
|
942
|
-
// below.
|
|
937
|
+
// question and hint state its length and ids, and a conversation newer
|
|
938
|
+
// than the kept one adds the warning, drawn last so it reads after the
|
|
939
|
+
// hint. The same list is what the removal deletes. A failed fetch leaves
|
|
940
|
+
// the question as the row reported it, with no ids and no warning, and
|
|
941
|
+
// the removal fetches again below.
|
|
943
942
|
const branchesReady = this._fetchBranches(session).then(fetched => {
|
|
944
943
|
const list = fetched === null || fetched === void 0 ? void 0 : fetched.branches;
|
|
945
944
|
if (list && list.length === 0) {
|
|
@@ -954,6 +953,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
954
953
|
else if (list) {
|
|
955
954
|
extra = list.length;
|
|
956
955
|
askCount(extra);
|
|
956
|
+
stateHint(list.map(branch => branch.session_id));
|
|
957
957
|
const newer = list.filter(branch => branch.file_mtime > session.file_mtime).length;
|
|
958
958
|
if (newer > 0) {
|
|
959
959
|
warning = document.createElement('div');
|
|
@@ -961,7 +961,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
961
961
|
warning.setAttribute('role', 'alert');
|
|
962
962
|
warning.textContent =
|
|
963
963
|
`${newer} parallel session${newer === 1 ? ' was' : 's were'} ` +
|
|
964
|
-
'active after the
|
|
964
|
+
'active after the current session.';
|
|
965
965
|
confirmBody.node.appendChild(warning);
|
|
966
966
|
}
|
|
967
967
|
// The list just decided what the button acts on: an arm given while
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab_ai_code_assistants_extension",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.299",
|
|
4
4
|
"description": "A full launcher and manager for every AI code assistant in JupyterLab - Claude Code, Codex, Kimi, Gemini and DeepSeek. Start, resume, fork, switch, and clean up CLI sessions from a side panel per assistant - one click lands you in the right terminal with the assistant already running, no duplicate tabs, no UUID hunting.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -2317,15 +2317,17 @@ describe('ACC-SESS-61, ACC-SESS-62 - what the destructive confirmations state',
|
|
|
2317
2317
|
expect(request).not.toHaveBeenCalled();
|
|
2318
2318
|
});
|
|
2319
2319
|
|
|
2320
|
-
it('ACC-SESS-62 - the cleanup confirmation
|
|
2320
|
+
it('ACC-SESS-62 - the cleanup confirmation names the three removed sessions by short id', async () => {
|
|
2321
2321
|
// The fetched list is what the question counts: three branches despite a
|
|
2322
|
-
// row that claims nine. It also gives Cancel a DELETE to withhold.
|
|
2322
|
+
// row that claims nine. It also gives Cancel a DELETE to withhold. The
|
|
2323
|
+
// ids differ in their first eight characters, so each short id names one
|
|
2324
|
+
// conversation.
|
|
2323
2325
|
request.mockImplementationOnce(() =>
|
|
2324
2326
|
Promise.resolve({
|
|
2325
2327
|
current: 'sid-ledger-tools',
|
|
2326
2328
|
total: 4,
|
|
2327
|
-
branches: [
|
|
2328
|
-
session_id: `
|
|
2329
|
+
branches: ['alpha', 'bravo', 'charlie'].map((name, index) => ({
|
|
2330
|
+
session_id: `sess-${name}`,
|
|
2329
2331
|
file_mtime: 1,
|
|
2330
2332
|
label: `Branch ${index}`
|
|
2331
2333
|
}))
|
|
@@ -2338,7 +2340,9 @@ describe('ACC-SESS-61, ACC-SESS-62 - what the destructive confirmations state',
|
|
|
2338
2340
|
|
|
2339
2341
|
expect(body).toContain('3 parallel sessions');
|
|
2340
2342
|
expect(body).toContain('ledger-tools');
|
|
2341
|
-
expect(body).toContain(
|
|
2343
|
+
expect(body).toContain(
|
|
2344
|
+
'Remaining 3 sessions sess-alp, sess-bra, sess-cha will be removed.'
|
|
2345
|
+
);
|
|
2342
2346
|
pressEnter();
|
|
2343
2347
|
await done;
|
|
2344
2348
|
// Cancelling after the question's branches fetch sends no DELETE.
|
|
@@ -2414,6 +2418,10 @@ describe('ACC-PANE-221, ACC-PANE-222 - the cleanup confirmation warning and hint
|
|
|
2414
2418
|
expect(
|
|
2415
2419
|
document.querySelector('.jp-Dialog-body p')!.getAttribute('role')
|
|
2416
2420
|
).toBe('status');
|
|
2421
|
+
// The hint is a live region too, because the fetch restates it with the
|
|
2422
|
+
// short ids (DEF-PANE-374-HINT-ANNOUNCE).
|
|
2423
|
+
const hint = document.querySelector('.jp-AiAssistantsPanel-cleanupHint')!;
|
|
2424
|
+
expect(hint.getAttribute('role')).toBe('status');
|
|
2417
2425
|
// The Widget body leaves showDialog no string to name the dialog from, so
|
|
2418
2426
|
// the name is set here and carries the question on screen.
|
|
2419
2427
|
expect(
|
|
@@ -2431,8 +2439,12 @@ describe('ACC-PANE-221, ACC-PANE-222 - the cleanup confirmation warning and hint
|
|
|
2431
2439
|
'.jp-AiAssistantsPanel-cleanupWarning'
|
|
2432
2440
|
)!;
|
|
2433
2441
|
expect(warning.textContent).toBe(
|
|
2434
|
-
'1 parallel session was active after the
|
|
2442
|
+
'1 parallel session was active after the current session.'
|
|
2435
2443
|
);
|
|
2444
|
+
// The restatement kept the hint a live region and put the ids in it, so
|
|
2445
|
+
// the change is announced.
|
|
2446
|
+
expect(hint.getAttribute('role')).toBe('status');
|
|
2447
|
+
expect(hint.textContent).toContain('sid-bran');
|
|
2436
2448
|
// The warning is announced, and the dialog's name follows the restated
|
|
2437
2449
|
// question and the warning.
|
|
2438
2450
|
expect(warning.getAttribute('role')).toBe('alert');
|
|
@@ -2502,7 +2514,7 @@ describe('ACC-PANE-221, ACC-PANE-222 - the cleanup confirmation warning and hint
|
|
|
2502
2514
|
);
|
|
2503
2515
|
});
|
|
2504
2516
|
|
|
2505
|
-
it('warns when a conversation it removes was active after the
|
|
2517
|
+
it('warns when a conversation it removes was active after the current session', async () => {
|
|
2506
2518
|
const now = Date.now();
|
|
2507
2519
|
const row = session({
|
|
2508
2520
|
name: 'ledger-tools',
|
|
@@ -2549,7 +2561,7 @@ describe('ACC-PANE-221, ACC-PANE-222 - the cleanup confirmation warning and hint
|
|
|
2549
2561
|
await done;
|
|
2550
2562
|
});
|
|
2551
2563
|
|
|
2552
|
-
it('
|
|
2564
|
+
it('lists the sessions it removes by short id in both disposal settings', async () => {
|
|
2553
2565
|
const now = Date.now();
|
|
2554
2566
|
const row = session({ name: 'ledger-tools', extra_sessions: 1 });
|
|
2555
2567
|
request.mockResolvedValueOnce(branches([now - 3 * HOUR]));
|
|
@@ -2559,14 +2571,30 @@ describe('ACC-PANE-221, ACC-PANE-222 - the cleanup confirmation warning and hint
|
|
|
2559
2571
|
let hint = document.querySelector(
|
|
2560
2572
|
'.jp-AiAssistantsPanel-cleanupHint'
|
|
2561
2573
|
)!.textContent!;
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
//
|
|
2574
|
+
// The fetched branch is `sid-branch-0`; its short id is its first eight
|
|
2575
|
+
// characters, which for Claude is the whole display id.
|
|
2576
|
+
expect(hint).toBe('Remaining 1 session sid-bran will be removed.');
|
|
2577
|
+
// A live region from creation: the restatement with the ids is announced
|
|
2578
|
+
// (DEF-PANE-374-HINT-ANNOUNCE).
|
|
2566
2579
|
expect(
|
|
2567
|
-
document
|
|
2568
|
-
.
|
|
2569
|
-
|
|
2580
|
+
document
|
|
2581
|
+
.querySelector('.jp-AiAssistantsPanel-cleanupHint')!
|
|
2582
|
+
.getAttribute('role')
|
|
2583
|
+
).toBe('status');
|
|
2584
|
+
// The caution line is gone from the confirmation entirely.
|
|
2585
|
+
expect(
|
|
2586
|
+
document.querySelector('.jp-AiAssistantsPanel-cleanupCaution')
|
|
2587
|
+
).toBeNull();
|
|
2588
|
+
pressEnter();
|
|
2589
|
+
await done;
|
|
2590
|
+
|
|
2591
|
+
// A failed fetch leaves no ids known: the hint states the count alone.
|
|
2592
|
+
request.mockRejectedValueOnce(new Error('offline'));
|
|
2593
|
+
done = (panel as any)._cleanupParallel(row) as Promise<void>;
|
|
2594
|
+
await openButtons();
|
|
2595
|
+
expect(
|
|
2596
|
+
document.querySelector('.jp-AiAssistantsPanel-cleanupHint')!.textContent
|
|
2597
|
+
).toBe('Remaining 1 session will be removed.');
|
|
2570
2598
|
pressEnter();
|
|
2571
2599
|
await done;
|
|
2572
2600
|
|
|
@@ -2577,13 +2605,18 @@ describe('ACC-PANE-221, ACC-PANE-222 - the cleanup confirmation warning and hint
|
|
|
2577
2605
|
hint = document.querySelector(
|
|
2578
2606
|
'.jp-AiAssistantsPanel-cleanupHint'
|
|
2579
2607
|
)!.textContent!;
|
|
2580
|
-
expect(hint
|
|
2581
|
-
|
|
2582
|
-
|
|
2608
|
+
expect(hint).toBe(
|
|
2609
|
+
'Remaining 1 session sid-bran will be deleted permanently.'
|
|
2610
|
+
);
|
|
2611
|
+
// The live region holds with the trash off as well.
|
|
2583
2612
|
expect(
|
|
2584
|
-
document
|
|
2585
|
-
.
|
|
2586
|
-
|
|
2613
|
+
document
|
|
2614
|
+
.querySelector('.jp-AiAssistantsPanel-cleanupHint')!
|
|
2615
|
+
.getAttribute('role')
|
|
2616
|
+
).toBe('status');
|
|
2617
|
+
expect(
|
|
2618
|
+
document.querySelector('.jp-AiAssistantsPanel-cleanupCaution')
|
|
2619
|
+
).toBeNull();
|
|
2587
2620
|
pressEnter();
|
|
2588
2621
|
await done;
|
|
2589
2622
|
});
|
package/src/core/panel.ts
CHANGED
|
@@ -881,23 +881,25 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
881
881
|
};
|
|
882
882
|
askCount(extra);
|
|
883
883
|
confirmBody.node.appendChild(question);
|
|
884
|
-
//
|
|
884
|
+
// The second line states what happens to the rest: the count, the short
|
|
885
|
+
// ids once the branch list names them, and the disposal the setting chose.
|
|
885
886
|
const hint = document.createElement('p');
|
|
886
887
|
hint.className = 'jp-AiAssistantsPanel-cleanupHint';
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
888
|
+
// Announced when restated with the short ids; an aria-label change is not.
|
|
889
|
+
hint.setAttribute('role', 'status');
|
|
890
|
+
const stateHint = (ids: string[] | null): void => {
|
|
891
|
+
const shortIds = ids?.map(id =>
|
|
892
|
+
shortSessionId(id, this._descriptor.sessionIdPrefix)
|
|
893
|
+
);
|
|
894
|
+
hint.textContent =
|
|
895
|
+
`Remaining ${extra} session${extra === 1 ? '' : 's'}` +
|
|
896
|
+
(shortIds?.length ? ` ${shortIds.join(', ')}` : '') +
|
|
897
|
+
(this._deleteToTrash
|
|
898
|
+
? ' will be removed.'
|
|
899
|
+
: ' will be deleted permanently.');
|
|
900
|
+
};
|
|
901
|
+
stateHint(null);
|
|
890
902
|
confirmBody.node.appendChild(hint);
|
|
891
|
-
// The irreversible note is a safety statement, not the hint, so it keeps
|
|
892
|
-
// its own line. With the trash on it names the refusal: a refused move
|
|
893
|
-
// deletes permanently, while the hint promises the trash
|
|
894
|
-
// (DEF-PANE-363-CAUTION-REFUSAL).
|
|
895
|
-
const caution = document.createElement('p');
|
|
896
|
-
caution.className = 'jp-AiAssistantsPanel-cleanupCaution';
|
|
897
|
-
caution.textContent = this._deleteToTrash
|
|
898
|
-
? 'A refused trash move deletes permanently and cannot be undone.'
|
|
899
|
-
: 'This cannot be undone.';
|
|
900
|
-
confirmBody.node.appendChild(caution);
|
|
901
903
|
|
|
902
904
|
const title = this._trans.__('Clean Up Parallel Sessions');
|
|
903
905
|
let warning: HTMLElement | null = null;
|
|
@@ -915,8 +917,8 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
915
917
|
// Scopes the armed style in base.css to this one dialog.
|
|
916
918
|
confirmDialog.addClass('jp-AiAssistantsPanel-cleanupDialog');
|
|
917
919
|
// The Dialog names itself only when its body is a string, and this body is
|
|
918
|
-
// a Widget: name it here, and again when the question
|
|
919
|
-
// warning joins it.
|
|
920
|
+
// a Widget: name it here, and again when the question and hint are
|
|
921
|
+
// restated and the warning joins it.
|
|
920
922
|
const nameDialog = (): void => {
|
|
921
923
|
confirmDialog.node.setAttribute(
|
|
922
924
|
'aria-label',
|
|
@@ -924,7 +926,6 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
924
926
|
title,
|
|
925
927
|
question.textContent ?? '',
|
|
926
928
|
hint.textContent ?? '',
|
|
927
|
-
caution.textContent ?? '',
|
|
928
929
|
warning?.textContent ?? ''
|
|
929
930
|
]
|
|
930
931
|
.filter(part => part)
|
|
@@ -1048,11 +1049,11 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1048
1049
|
|
|
1049
1050
|
// Alongside the question, never in front of it: the request layer allows
|
|
1050
1051
|
// 60 s, and the confirmation must open at once. When the list lands the
|
|
1051
|
-
// question
|
|
1052
|
-
// adds the warning, drawn last so it reads after the
|
|
1053
|
-
// list is what the removal deletes. A failed fetch leaves
|
|
1054
|
-
// the row reported it, with no
|
|
1055
|
-
// below.
|
|
1052
|
+
// question and hint state its length and ids, and a conversation newer
|
|
1053
|
+
// than the kept one adds the warning, drawn last so it reads after the
|
|
1054
|
+
// hint. The same list is what the removal deletes. A failed fetch leaves
|
|
1055
|
+
// the question as the row reported it, with no ids and no warning, and
|
|
1056
|
+
// the removal fetches again below.
|
|
1056
1057
|
const branchesReady = this._fetchBranches(session).then(
|
|
1057
1058
|
fetched => {
|
|
1058
1059
|
const list = fetched?.branches;
|
|
@@ -1067,6 +1068,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1067
1068
|
} else if (list) {
|
|
1068
1069
|
extra = list.length;
|
|
1069
1070
|
askCount(extra);
|
|
1071
|
+
stateHint(list.map(branch => branch.session_id));
|
|
1070
1072
|
const newer = list.filter(
|
|
1071
1073
|
branch => branch.file_mtime > session.file_mtime
|
|
1072
1074
|
).length;
|
|
@@ -1076,7 +1078,7 @@ export class AssistantSessionsPanel extends Widget {
|
|
|
1076
1078
|
warning.setAttribute('role', 'alert');
|
|
1077
1079
|
warning.textContent =
|
|
1078
1080
|
`${newer} parallel session${newer === 1 ? ' was' : 's were'} ` +
|
|
1079
|
-
'active after the
|
|
1081
|
+
'active after the current session.';
|
|
1080
1082
|
confirmBody.node.appendChild(warning);
|
|
1081
1083
|
}
|
|
1082
1084
|
// The list just decided what the button acts on: an arm given while
|
package/style/base.css
CHANGED
|
@@ -684,7 +684,7 @@ body[data-jp-theme-light='true'] .jp-AiAssistantsPanel-dotHalo {
|
|
|
684
684
|
}
|
|
685
685
|
|
|
686
686
|
/* The two destructive confirmations' lines: the Remove question with its
|
|
687
|
-
caution, and the cleanup question, hint
|
|
687
|
+
caution, and the cleanup question, hint and warning. The warning
|
|
688
688
|
box is separate from the body text because it answers one question - is a
|
|
689
689
|
conversation this removal deletes newer than the one kept? - and it uses the
|
|
690
690
|
warning colour, not the error colour: the removal is still the user's call.
|