jupyterlab_claude_code_extension 1.0.26 → 1.0.29

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/icons.js CHANGED
@@ -42,7 +42,7 @@ export const removeIcon = new LabIcon({
42
42
  name: 'jupyterlab_claude_code_extension:remove',
43
43
  svgstr: removeSvgStr
44
44
  });
45
- const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
45
+ const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="13" height="13">
46
46
  <path class="jp-icon3" fill="#616161" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/>
47
47
  </svg>`;
48
48
  export const shieldIcon = new LabIcon({
package/lib/widget.d.ts CHANGED
@@ -38,6 +38,7 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
38
38
  private _resumeInTerminal;
39
39
  private _doResumeInTerminal;
40
40
  private _findTerminalForCwd;
41
+ private _showCloseExistingDialog;
41
42
  private _wireTerminalDisposal;
42
43
  /** Apply the resolve-names setting + path-segment disambiguation. */
43
44
  private _displayName;
package/lib/widget.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { Dialog, showDialog } from '@jupyterlab/apputils';
1
2
  import { CommandRegistry } from '@lumino/commands';
2
3
  import { Menu, Widget } from '@lumino/widgets';
3
4
  import { requestAPI } from './request';
@@ -308,21 +309,32 @@ export class ClaudeCodeSessionsWidget extends Widget {
308
309
  }
309
310
  async _doResumeInTerminal(session, forceDangerous) {
310
311
  try {
311
- // 1. In-memory microcache - covers rapid-click and post-creation reuse
312
- // before the new widget propagates fully through the tracker. Cleared
313
- // on widget disposal. NOT persisted to localStorage.
312
+ // Always prefer reusing an open terminal for this project. The
313
+ // skip-permissions flag can only be applied to a fresh pty, never
314
+ // retroactively. So if the user wants dangerous mode but an open
315
+ // terminal already exists, show a modal asking them to close it
316
+ // first - we won't auto-close, won't silently reuse the wrong mode.
317
+ // 1. In-memory microcache.
314
318
  const cached = this._terminalsByPath.get(session.project_path);
315
319
  if (cached && !cached.isDisposed) {
320
+ if (forceDangerous) {
321
+ await this._showCloseExistingDialog();
322
+ this._app.shell.activateById(cached.id);
323
+ return;
324
+ }
316
325
  this._app.shell.activateById(cached.id);
317
326
  return;
318
327
  }
319
- // 2. Walk every live terminal widget JL knows about and ask the
320
- // server for the cwd of EVERY process in its pty's tree. Match if
321
- // any one of them equals project_path.
328
+ // 2. Walk every live terminal widget JL knows about.
322
329
  const found = await this._findTerminalForCwd(session.project_path);
323
330
  if (found) {
324
331
  this._terminalsByPath.set(session.project_path, found);
325
332
  this._wireTerminalDisposal(session.project_path, found);
333
+ if (forceDangerous) {
334
+ await this._showCloseExistingDialog();
335
+ this._app.shell.activateById(found.id);
336
+ return;
337
+ }
326
338
  this._app.shell.activateById(found.id);
327
339
  return;
328
340
  }
@@ -384,6 +396,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
384
396
  }
385
397
  return null;
386
398
  }
399
+ async _showCloseExistingDialog() {
400
+ await showDialog({
401
+ title: 'Existing Claude session is running',
402
+ body: 'A terminal for this project is already open. To launch with ' +
403
+ '--dangerously-skip-permissions, close that terminal first then ' +
404
+ 'click "Resume (Skip Permissions)" again.',
405
+ buttons: [Dialog.okButton({ label: 'OK' })]
406
+ });
407
+ }
387
408
  _wireTerminalDisposal(projectPath, widget) {
388
409
  var _a;
389
410
  if (!((_a = widget === null || widget === void 0 ? void 0 : widget.disposed) === null || _a === void 0 ? void 0 : _a.connect)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_claude_code_extension",
3
- "version": "1.0.26",
3
+ "version": "1.0.29",
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/icons.ts CHANGED
@@ -51,7 +51,7 @@ export const removeIcon = new LabIcon({
51
51
  svgstr: removeSvgStr
52
52
  });
53
53
 
54
- const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
54
+ const shieldSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="13" height="13">
55
55
  <path class="jp-icon3" fill="#616161" d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/>
56
56
  </svg>`;
57
57
 
package/src/widget.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { JupyterFrontEnd } from '@jupyterlab/application';
2
+ import { Dialog, showDialog } from '@jupyterlab/apputils';
2
3
  import { ServerConnection } from '@jupyterlab/services';
3
4
  import { ITerminalTracker } from '@jupyterlab/terminal';
4
5
  import { CommandRegistry } from '@lumino/commands';
@@ -379,22 +380,34 @@ export class ClaudeCodeSessionsWidget extends Widget {
379
380
  forceDangerous: boolean
380
381
  ): Promise<void> {
381
382
  try {
382
- // 1. In-memory microcache - covers rapid-click and post-creation reuse
383
- // before the new widget propagates fully through the tracker. Cleared
384
- // on widget disposal. NOT persisted to localStorage.
383
+ // Always prefer reusing an open terminal for this project. The
384
+ // skip-permissions flag can only be applied to a fresh pty, never
385
+ // retroactively. So if the user wants dangerous mode but an open
386
+ // terminal already exists, show a modal asking them to close it
387
+ // first - we won't auto-close, won't silently reuse the wrong mode.
388
+
389
+ // 1. In-memory microcache.
385
390
  const cached = this._terminalsByPath.get(session.project_path);
386
391
  if (cached && !cached.isDisposed) {
392
+ if (forceDangerous) {
393
+ await this._showCloseExistingDialog();
394
+ this._app.shell.activateById(cached.id);
395
+ return;
396
+ }
387
397
  this._app.shell.activateById(cached.id);
388
398
  return;
389
399
  }
390
400
 
391
- // 2. Walk every live terminal widget JL knows about and ask the
392
- // server for the cwd of EVERY process in its pty's tree. Match if
393
- // any one of them equals project_path.
401
+ // 2. Walk every live terminal widget JL knows about.
394
402
  const found = await this._findTerminalForCwd(session.project_path);
395
403
  if (found) {
396
404
  this._terminalsByPath.set(session.project_path, found);
397
405
  this._wireTerminalDisposal(session.project_path, found);
406
+ if (forceDangerous) {
407
+ await this._showCloseExistingDialog();
408
+ this._app.shell.activateById(found.id);
409
+ return;
410
+ }
398
411
  this._app.shell.activateById(found.id);
399
412
  return;
400
413
  }
@@ -464,6 +477,17 @@ export class ClaudeCodeSessionsWidget extends Widget {
464
477
  return null;
465
478
  }
466
479
 
480
+ private async _showCloseExistingDialog(): Promise<void> {
481
+ await showDialog({
482
+ title: 'Existing Claude session is running',
483
+ body:
484
+ 'A terminal for this project is already open. To launch with ' +
485
+ '--dangerously-skip-permissions, close that terminal first then ' +
486
+ 'click "Resume (Skip Permissions)" again.',
487
+ buttons: [Dialog.okButton({ label: 'OK' })]
488
+ });
489
+ }
490
+
467
491
  private _wireTerminalDisposal(projectPath: string, widget: any): void {
468
492
  if (!widget?.disposed?.connect) {
469
493
  return;