jupyterlab_claude_code_extension 1.0.45 → 1.0.50

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 CHANGED
@@ -40,6 +40,10 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
40
40
  private _doResumeInTerminal;
41
41
  private _findTerminalForCwd;
42
42
  private _showCloseExistingDialog;
43
+ /** Show a modal with a spinner while the terminal is being launched. The
44
+ * caller must dismiss it via ``.resolve()`` once the work is done.
45
+ */
46
+ private _showLaunchSpinner;
43
47
  private _wireTerminalDisposal;
44
48
  /** Apply the presentation-mode setting (path-segment disambiguation is
45
49
  * handled separately in ``_disambiguate``). */
package/lib/widget.js CHANGED
@@ -338,21 +338,29 @@ export class ClaudeCodeSessionsWidget extends Widget {
338
338
  // as the pty's only process (no shell). Server-side endpoint calls
339
339
  // terminal_manager.create(shell_command=[claude, --resume, sid], cwd=...)
340
340
  // and returns the terminal name; we then attach JL's standard widget
341
- // via terminal:open. When claude exits, the tab closes.
342
- const launched = await requestAPI('launch-terminal', this._serverSettings, {
343
- method: 'POST',
344
- body: JSON.stringify({
345
- project_path: session.project_path,
346
- session_id: session.session_id,
347
- dangerously_skip_permissions: forceDangerous || this._dangerouslySkip
348
- })
349
- });
350
- const widget = await this._app.commands.execute('terminal:open', {
351
- name: launched.terminal_name
352
- });
353
- if (widget === null || widget === void 0 ? void 0 : widget.id) {
354
- this._terminalsByPath.set(session.project_path, widget);
355
- this._wireTerminalDisposal(session.project_path, widget);
341
+ // via terminal:open. When claude exits, the tab closes. The launch
342
+ // RPC + the WebSocket-resize waiter on the server can take a few
343
+ // seconds, so show a modal spinner for visual feedback.
344
+ const spinner = this._showLaunchSpinner(`Opening ${this._lookupName(session)}...`);
345
+ try {
346
+ const launched = await requestAPI('launch-terminal', this._serverSettings, {
347
+ method: 'POST',
348
+ body: JSON.stringify({
349
+ project_path: session.project_path,
350
+ session_id: session.session_id,
351
+ dangerously_skip_permissions: forceDangerous || this._dangerouslySkip
352
+ })
353
+ });
354
+ const widget = await this._app.commands.execute('terminal:open', {
355
+ name: launched.terminal_name
356
+ });
357
+ if (widget === null || widget === void 0 ? void 0 : widget.id) {
358
+ this._terminalsByPath.set(session.project_path, widget);
359
+ this._wireTerminalDisposal(session.project_path, widget);
360
+ }
361
+ }
362
+ finally {
363
+ spinner.resolve();
356
364
  }
357
365
  }
358
366
  catch (err) {
@@ -407,6 +415,30 @@ export class ClaudeCodeSessionsWidget extends Widget {
407
415
  buttons: [Dialog.okButton({ label: 'OK' })]
408
416
  });
409
417
  }
418
+ /** Show a modal with a spinner while the terminal is being launched. The
419
+ * caller must dismiss it via ``.resolve()`` once the work is done.
420
+ */
421
+ _showLaunchSpinner(label) {
422
+ const body = new Widget();
423
+ body.node.className = 'jp-ClaudeSessionsPanel-launchOverlay';
424
+ const spinner = document.createElement('div');
425
+ spinner.className =
426
+ 'jp-claude-sessions-panel-spinner jp-ClaudeSessionsPanel-launchSpinner';
427
+ body.node.appendChild(spinner);
428
+ const text = document.createElement('div');
429
+ text.className = 'jp-ClaudeSessionsPanel-launchLabel';
430
+ text.textContent = label;
431
+ body.node.appendChild(text);
432
+ const dialog = new Dialog({
433
+ title: 'Opening Claude Code session',
434
+ body,
435
+ buttons: [Dialog.cancelButton({ label: 'Run in background' })]
436
+ });
437
+ // launch() returns a Promise we don't await - we resolve programmatically
438
+ // when the spawn completes (or errors).
439
+ void dialog.launch();
440
+ return dialog;
441
+ }
410
442
  _wireTerminalDisposal(projectPath, widget) {
411
443
  var _a;
412
444
  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.45",
3
+ "version": "1.0.50",
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/widget.ts CHANGED
@@ -415,26 +415,35 @@ export class ClaudeCodeSessionsWidget extends Widget {
415
415
  // as the pty's only process (no shell). Server-side endpoint calls
416
416
  // terminal_manager.create(shell_command=[claude, --resume, sid], cwd=...)
417
417
  // and returns the terminal name; we then attach JL's standard widget
418
- // via terminal:open. When claude exits, the tab closes.
419
- const launched = await requestAPI<ILaunchTerminalResponse>(
420
- 'launch-terminal',
421
- this._serverSettings,
422
- {
423
- method: 'POST',
424
- body: JSON.stringify({
425
- project_path: session.project_path,
426
- session_id: session.session_id,
427
- dangerously_skip_permissions:
428
- forceDangerous || this._dangerouslySkip
429
- })
430
- }
418
+ // via terminal:open. When claude exits, the tab closes. The launch
419
+ // RPC + the WebSocket-resize waiter on the server can take a few
420
+ // seconds, so show a modal spinner for visual feedback.
421
+ const spinner = this._showLaunchSpinner(
422
+ `Opening ${this._lookupName(session)}...`
431
423
  );
432
- const widget: any = await this._app.commands.execute('terminal:open', {
433
- name: launched.terminal_name
434
- });
435
- if (widget?.id) {
436
- this._terminalsByPath.set(session.project_path, widget);
437
- this._wireTerminalDisposal(session.project_path, widget);
424
+ try {
425
+ const launched = await requestAPI<ILaunchTerminalResponse>(
426
+ 'launch-terminal',
427
+ this._serverSettings,
428
+ {
429
+ method: 'POST',
430
+ body: JSON.stringify({
431
+ project_path: session.project_path,
432
+ session_id: session.session_id,
433
+ dangerously_skip_permissions:
434
+ forceDangerous || this._dangerouslySkip
435
+ })
436
+ }
437
+ );
438
+ const widget: any = await this._app.commands.execute('terminal:open', {
439
+ name: launched.terminal_name
440
+ });
441
+ if (widget?.id) {
442
+ this._terminalsByPath.set(session.project_path, widget);
443
+ this._wireTerminalDisposal(session.project_path, widget);
444
+ }
445
+ } finally {
446
+ spinner.resolve();
438
447
  }
439
448
  } catch (err) {
440
449
  this._showError(err);
@@ -493,6 +502,34 @@ export class ClaudeCodeSessionsWidget extends Widget {
493
502
  });
494
503
  }
495
504
 
505
+ /** Show a modal with a spinner while the terminal is being launched. The
506
+ * caller must dismiss it via ``.resolve()`` once the work is done.
507
+ */
508
+ private _showLaunchSpinner(label: string): Dialog<unknown> {
509
+ const body = new Widget();
510
+ body.node.className = 'jp-ClaudeSessionsPanel-launchOverlay';
511
+
512
+ const spinner = document.createElement('div');
513
+ spinner.className =
514
+ 'jp-claude-sessions-panel-spinner jp-ClaudeSessionsPanel-launchSpinner';
515
+ body.node.appendChild(spinner);
516
+
517
+ const text = document.createElement('div');
518
+ text.className = 'jp-ClaudeSessionsPanel-launchLabel';
519
+ text.textContent = label;
520
+ body.node.appendChild(text);
521
+
522
+ const dialog = new Dialog<unknown>({
523
+ title: 'Opening Claude Code session',
524
+ body,
525
+ buttons: [Dialog.cancelButton({ label: 'Run in background' })]
526
+ });
527
+ // launch() returns a Promise we don't await - we resolve programmatically
528
+ // when the spawn completes (or errors).
529
+ void dialog.launch();
530
+ return dialog;
531
+ }
532
+
496
533
  private _wireTerminalDisposal(projectPath: string, widget: any): void {
497
534
  if (!widget?.disposed?.connect) {
498
535
  return;
package/style/base.css CHANGED
@@ -237,6 +237,29 @@
237
237
  display: inline-block;
238
238
  }
239
239
 
240
+ .jp-ClaudeSessionsPanel-launchOverlay {
241
+ display: flex;
242
+ flex-direction: column;
243
+ align-items: center;
244
+ justify-content: center;
245
+ gap: 12px;
246
+ padding: 12px 8px;
247
+ min-width: 240px;
248
+ }
249
+
250
+ .jp-ClaudeSessionsPanel-launchSpinner {
251
+ width: 28px;
252
+ height: 28px;
253
+ border-width: 3px;
254
+ }
255
+
256
+ .jp-ClaudeSessionsPanel-launchLabel {
257
+ font-size: var(--jp-ui-font-size1);
258
+ color: var(--jp-ui-font-color1);
259
+ text-align: center;
260
+ word-break: break-word;
261
+ }
262
+
240
263
  .jp-ClaudeSessionsPanel-row.jp-mod-busy {
241
264
  opacity: 0.55;
242
265
  pointer-events: none;