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 CHANGED
@@ -786,23 +786,23 @@ export class AssistantSessionsPanel extends Widget {
786
786
  };
787
787
  askCount(extra);
788
788
  confirmBody.node.appendChild(question);
789
- // One sentence, twelve words or fewer: what happens, not the detail.
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
- hint.textContent = this._deleteToTrash
793
- ? 'The current conversation is kept; the rest go to the trash.'
794
- : 'The current conversation is kept; the rest are deleted permanently.';
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 is restated and the
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, _d;
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 = caution.textContent) !== null && _c !== void 0 ? _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 states its length and a conversation newer than the kept one
939
- // adds the warning, drawn last so it reads after the caution. The same
940
- // list is what the removal deletes. A failed fetch leaves the question as
941
- // the row reported it, with no warning, and the removal fetches again
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 one kept.';
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.294",
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 states how many go and that one stays', async () => {
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: [0, 1, 2].map(index => ({
2328
- session_id: `sid-branch-${index}`,
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('current conversation is kept');
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 one kept.'
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 one kept', async () => {
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('states its hint in at most twelve words, in both disposal settings', async () => {
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
- expect(hint.trim().split(/\s+/).length).toBeLessThanOrEqual(12);
2563
- expect(hint).toContain('go to the trash');
2564
- // The hint promises the trash; the caution names the refusal that deletes
2565
- // permanently.
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.querySelector('.jp-AiAssistantsPanel-cleanupCaution')!
2568
- .textContent
2569
- ).toContain('A refused trash move deletes permanently');
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.trim().split(/\s+/).length).toBeLessThanOrEqual(12);
2581
- expect(hint).toContain('deleted permanently');
2582
- // The caution is not the hint: it stays visible on its own.
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.querySelector('.jp-AiAssistantsPanel-cleanupCaution')!
2585
- .textContent
2586
- ).toContain('cannot be undone');
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
- // One sentence, twelve words or fewer: what happens, not the detail.
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
- hint.textContent = this._deleteToTrash
888
- ? 'The current conversation is kept; the rest go to the trash.'
889
- : 'The current conversation is kept; the rest are deleted permanently.';
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 is restated and the
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 states its length and a conversation newer than the kept one
1052
- // adds the warning, drawn last so it reads after the caution. The same
1053
- // list is what the removal deletes. A failed fetch leaves the question as
1054
- // the row reported it, with no warning, and the removal fetches again
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 one kept.';
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, caution and warning. The warning
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.