jupyterlab_claude_code_extension 1.1.14 → 1.1.16

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.js CHANGED
@@ -346,7 +346,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
346
346
  // via terminal:open. When claude exits, the tab closes. The launch
347
347
  // RPC + the WebSocket-resize waiter on the server can take a few
348
348
  // seconds, so show a modal spinner for visual feedback.
349
- const spinner = this._showLaunchSpinner(`Opening ${this._lookupName(session)}...`);
349
+ const spinner = this._showLaunchSpinner();
350
350
  try {
351
351
  const launched = await requestAPI('launch-terminal', this._serverSettings, {
352
352
  method: 'POST',
@@ -455,17 +455,13 @@ export class ClaudeCodeSessionsWidget extends Widget {
455
455
  * caller must dismiss it via ``.dispose()`` once the work is done - the
456
456
  * dialog has no buttons so ``.resolve()`` would be a no-op.
457
457
  */
458
- _showLaunchSpinner(label) {
458
+ _showLaunchSpinner() {
459
459
  const body = new Widget();
460
460
  body.node.className = 'jp-ClaudeSessionsPanel-launchOverlay';
461
461
  const spinner = document.createElement('div');
462
462
  spinner.className =
463
463
  'jp-claude-sessions-panel-spinner jp-ClaudeSessionsPanel-launchSpinner';
464
464
  body.node.appendChild(spinner);
465
- const text = document.createElement('div');
466
- text.className = 'jp-ClaudeSessionsPanel-launchLabel';
467
- text.textContent = label;
468
- body.node.appendChild(text);
469
465
  const dialog = new Dialog({
470
466
  title: 'Opening Claude Code session',
471
467
  body,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_claude_code_extension",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
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",
@@ -1,3 +1,10 @@
1
+ declare const __dirname: string;
2
+ declare function require(name: string): any;
3
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
4
+ const fs: { readFileSync: (p: string, enc: string) => string } = require('fs');
5
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
6
+ const path: { join: (...args: string[]) => string } = require('path');
7
+
1
8
  import type { ISession } from '../types';
2
9
 
3
10
  const session = (over: Partial<ISession> = {}): ISession => ({
@@ -61,3 +68,29 @@ describe('session sorting', () => {
61
68
  expect(recent[9].project_path).toBe('p15');
62
69
  });
63
70
  });
71
+
72
+ /**
73
+ * Regression guard for 1.1.13 -> 1.1.14: the launch-spinner Dialog is
74
+ * constructed with ``buttons: []`` so ``Dialog.resolve()`` has no button
75
+ * to "click" and silently no-ops, leaving the modal stuck over the
76
+ * panel. The dismiss call in ``_doResumeInTerminal``'s ``finally`` MUST
77
+ * be ``spinner.dispose()``. These are source-level invariants - cheap,
78
+ * deterministic, and exactly the contract that broke in 1.1.13.
79
+ */
80
+ describe('launch spinner dismiss contract', () => {
81
+ const widgetSrc: string = fs.readFileSync(
82
+ path.join(__dirname, '..', 'widget.ts'),
83
+ 'utf-8'
84
+ );
85
+
86
+ it('_doResumeInTerminal dismisses spinner via dispose(), not resolve()', () => {
87
+ expect(widgetSrc).toMatch(/spinner\.dispose\(\)/);
88
+ expect(widgetSrc).not.toMatch(/spinner\.resolve\(\)/);
89
+ });
90
+
91
+ it('_showLaunchSpinner constructs Dialog with buttons:[]', () => {
92
+ // The empty buttons array is the reason resolve() no-ops; if this
93
+ // ever becomes non-empty, the dismiss call can revisit resolve().
94
+ expect(widgetSrc).toMatch(/_showLaunchSpinner[\s\S]*?buttons:\s*\[\]/);
95
+ });
96
+ });
package/src/widget.ts CHANGED
@@ -431,9 +431,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
431
431
  // via terminal:open. When claude exits, the tab closes. The launch
432
432
  // RPC + the WebSocket-resize waiter on the server can take a few
433
433
  // seconds, so show a modal spinner for visual feedback.
434
- const spinner = this._showLaunchSpinner(
435
- `Opening ${this._lookupName(session)}...`
436
- );
434
+ const spinner = this._showLaunchSpinner();
437
435
  try {
438
436
  const launched = await requestAPI<ILaunchTerminalResponse>(
439
437
  'launch-terminal',
@@ -548,7 +546,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
548
546
  * caller must dismiss it via ``.dispose()`` once the work is done - the
549
547
  * dialog has no buttons so ``.resolve()`` would be a no-op.
550
548
  */
551
- private _showLaunchSpinner(label: string): Dialog<unknown> {
549
+ private _showLaunchSpinner(): Dialog<unknown> {
552
550
  const body = new Widget();
553
551
  body.node.className = 'jp-ClaudeSessionsPanel-launchOverlay';
554
552
 
@@ -557,11 +555,6 @@ export class ClaudeCodeSessionsWidget extends Widget {
557
555
  'jp-claude-sessions-panel-spinner jp-ClaudeSessionsPanel-launchSpinner';
558
556
  body.node.appendChild(spinner);
559
557
 
560
- const text = document.createElement('div');
561
- text.className = 'jp-ClaudeSessionsPanel-launchLabel';
562
- text.textContent = label;
563
- body.node.appendChild(text);
564
-
565
558
  const dialog = new Dialog<unknown>({
566
559
  title: 'Opening Claude Code session',
567
560
  body,
package/style/base.css CHANGED
@@ -256,13 +256,6 @@
256
256
  border-width: 3px;
257
257
  }
258
258
 
259
- .jp-ClaudeSessionsPanel-launchLabel {
260
- font-size: var(--jp-ui-font-size1);
261
- color: var(--jp-ui-font-color1);
262
- text-align: center;
263
- word-break: break-word;
264
- }
265
-
266
259
  .jp-ClaudeSessionsPanel-row.jp-mod-busy {
267
260
  opacity: 0.55;
268
261
  pointer-events: none;