jupyterlab_claude_code_extension 1.0.58 → 1.0.60

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
@@ -73,6 +73,10 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
73
73
  private _lookupName;
74
74
  private _buildRowTooltip;
75
75
  private _displayPath;
76
+ /** Path relative to the JupyterLab server root (``''`` for the root
77
+ * itself), or ``null`` when the folder lies outside the root - in which
78
+ * case the file browser has no way to address it. */
79
+ private _pathUnderRoot;
76
80
  private _formatRelativeTime;
77
81
  private _setRefreshSpinning;
78
82
  private _setActiveRow;
package/lib/widget.js CHANGED
@@ -1,4 +1,5 @@
1
- import { Dialog, showDialog } from '@jupyterlab/apputils';
1
+ import { Clipboard, Dialog, Notification, showDialog } from '@jupyterlab/apputils';
2
+ import { folderIcon, terminalIcon } from '@jupyterlab/ui-components';
2
3
  import { CommandRegistry } from '@lumino/commands';
3
4
  import { Menu, Widget } from '@lumino/widgets';
4
5
  import { requestAPI } from './request';
@@ -745,6 +746,21 @@ export class ClaudeCodeSessionsWidget extends Widget {
745
746
  }
746
747
  return absolute;
747
748
  }
749
+ /** Path relative to the JupyterLab server root (``''`` for the root
750
+ * itself), or ``null`` when the folder lies outside the root - in which
751
+ * case the file browser has no way to address it. */
752
+ _pathUnderRoot(absolute) {
753
+ if (!this._rootDir) {
754
+ return null;
755
+ }
756
+ if (absolute === this._rootDir) {
757
+ return '';
758
+ }
759
+ if (absolute.startsWith(this._rootDir + '/')) {
760
+ return absolute.slice(this._rootDir.length + 1);
761
+ }
762
+ return null;
763
+ }
748
764
  _formatRelativeTime(epochMs) {
749
765
  if (!epochMs) {
750
766
  return 'unknown';
@@ -796,7 +812,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
796
812
  }
797
813
  });
798
814
  this._commands.addCommand('claude-code-sessions:resume', {
799
- label: 'Resume in Terminal',
815
+ label: 'Resume',
800
816
  execute: () => {
801
817
  if (this._activeSession) {
802
818
  void this._resumeInTerminal(this._activeSession);
@@ -812,6 +828,53 @@ export class ClaudeCodeSessionsWidget extends Widget {
812
828
  }
813
829
  }
814
830
  });
831
+ this._commands.addCommand('claude-code-sessions:open-terminal', {
832
+ label: 'Open Terminal',
833
+ icon: terminalIcon,
834
+ execute: () => {
835
+ if (!this._activeSession) {
836
+ return;
837
+ }
838
+ // JupyterLab's built-in command - spawns a fresh pty with the user's
839
+ // shell at the given cwd. No claude, no waiter wrapper, no reuse;
840
+ // for when the user wants a plain shell at the project folder.
841
+ void this._app.commands.execute('terminal:create-new', {
842
+ cwd: this._activeSession.project_path
843
+ });
844
+ }
845
+ });
846
+ this._commands.addCommand('claude-code-sessions:show-in-filebrowser', {
847
+ label: 'Show in File Browser',
848
+ icon: folderIcon,
849
+ execute: () => {
850
+ if (!this._activeSession) {
851
+ return;
852
+ }
853
+ const rel = this._pathUnderRoot(this._activeSession.project_path);
854
+ if (rel === null) {
855
+ // The file browser can only navigate within the JupyterLab server
856
+ // root; a project folder outside it has no addressable path there.
857
+ Notification.warning('Folder is outside the JupyterLab root - the file browser cannot show it.', { autoClose: 4000 });
858
+ return;
859
+ }
860
+ // JL's built-in command navigates the default file browser to the
861
+ // path and reveals the browser panel.
862
+ void this._app.commands.execute('filebrowser:go-to-path', {
863
+ path: rel
864
+ });
865
+ }
866
+ });
867
+ this._commands.addCommand('claude-code-sessions:copy-path', {
868
+ label: 'Copy Path',
869
+ execute: () => {
870
+ if (!this._activeSession) {
871
+ return;
872
+ }
873
+ const path = this._activeSession.project_path;
874
+ Clipboard.copyToSystem(path);
875
+ Notification.success(`Copied: ${path}`, { autoClose: 2000 });
876
+ }
877
+ });
815
878
  this._commands.addCommand('claude-code-sessions:remove', {
816
879
  label: 'Remove from Claude',
817
880
  icon: removeIcon,
@@ -827,9 +890,16 @@ export class ClaudeCodeSessionsWidget extends Widget {
827
890
  this._contextMenu.addItem({
828
891
  command: 'claude-code-sessions:resume-dangerous'
829
892
  });
893
+ this._contextMenu.addItem({
894
+ command: 'claude-code-sessions:open-terminal'
895
+ });
896
+ this._contextMenu.addItem({
897
+ command: 'claude-code-sessions:show-in-filebrowser'
898
+ });
830
899
  this._contextMenu.addItem({
831
900
  command: 'claude-code-sessions:toggle-favourite'
832
901
  });
902
+ this._contextMenu.addItem({ command: 'claude-code-sessions:copy-path' });
833
903
  this._contextMenu.addItem({ type: 'separator' });
834
904
  this._contextMenu.addItem({ command: 'claude-code-sessions:remove' });
835
905
  this._contextMenu.aboutToClose.connect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_claude_code_extension",
3
- "version": "1.0.58",
3
+ "version": "1.0.60",
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
@@ -1,7 +1,13 @@
1
1
  import { JupyterFrontEnd } from '@jupyterlab/application';
2
- import { Dialog, showDialog } from '@jupyterlab/apputils';
2
+ import {
3
+ Clipboard,
4
+ Dialog,
5
+ Notification,
6
+ showDialog
7
+ } from '@jupyterlab/apputils';
3
8
  import { ServerConnection } from '@jupyterlab/services';
4
9
  import { ITerminalTracker } from '@jupyterlab/terminal';
10
+ import { folderIcon, terminalIcon } from '@jupyterlab/ui-components';
5
11
  import { CommandRegistry } from '@lumino/commands';
6
12
  import { Menu, Widget } from '@lumino/widgets';
7
13
  import { Message } from '@lumino/messaging';
@@ -874,6 +880,22 @@ export class ClaudeCodeSessionsWidget extends Widget {
874
880
  return absolute;
875
881
  }
876
882
 
883
+ /** Path relative to the JupyterLab server root (``''`` for the root
884
+ * itself), or ``null`` when the folder lies outside the root - in which
885
+ * case the file browser has no way to address it. */
886
+ private _pathUnderRoot(absolute: string): string | null {
887
+ if (!this._rootDir) {
888
+ return null;
889
+ }
890
+ if (absolute === this._rootDir) {
891
+ return '';
892
+ }
893
+ if (absolute.startsWith(this._rootDir + '/')) {
894
+ return absolute.slice(this._rootDir.length + 1);
895
+ }
896
+ return null;
897
+ }
898
+
877
899
  private _formatRelativeTime(epochMs: number): string {
878
900
  if (!epochMs) {
879
901
  return 'unknown';
@@ -929,7 +951,7 @@ export class ClaudeCodeSessionsWidget extends Widget {
929
951
  });
930
952
 
931
953
  this._commands.addCommand('claude-code-sessions:resume', {
932
- label: 'Resume in Terminal',
954
+ label: 'Resume',
933
955
  execute: () => {
934
956
  if (this._activeSession) {
935
957
  void this._resumeInTerminal(this._activeSession);
@@ -947,6 +969,59 @@ export class ClaudeCodeSessionsWidget extends Widget {
947
969
  }
948
970
  });
949
971
 
972
+ this._commands.addCommand('claude-code-sessions:open-terminal', {
973
+ label: 'Open Terminal',
974
+ icon: terminalIcon,
975
+ execute: () => {
976
+ if (!this._activeSession) {
977
+ return;
978
+ }
979
+ // JupyterLab's built-in command - spawns a fresh pty with the user's
980
+ // shell at the given cwd. No claude, no waiter wrapper, no reuse;
981
+ // for when the user wants a plain shell at the project folder.
982
+ void this._app.commands.execute('terminal:create-new', {
983
+ cwd: this._activeSession.project_path
984
+ });
985
+ }
986
+ });
987
+
988
+ this._commands.addCommand('claude-code-sessions:show-in-filebrowser', {
989
+ label: 'Show in File Browser',
990
+ icon: folderIcon,
991
+ execute: () => {
992
+ if (!this._activeSession) {
993
+ return;
994
+ }
995
+ const rel = this._pathUnderRoot(this._activeSession.project_path);
996
+ if (rel === null) {
997
+ // The file browser can only navigate within the JupyterLab server
998
+ // root; a project folder outside it has no addressable path there.
999
+ Notification.warning(
1000
+ 'Folder is outside the JupyterLab root - the file browser cannot show it.',
1001
+ { autoClose: 4000 }
1002
+ );
1003
+ return;
1004
+ }
1005
+ // JL's built-in command navigates the default file browser to the
1006
+ // path and reveals the browser panel.
1007
+ void this._app.commands.execute('filebrowser:go-to-path', {
1008
+ path: rel
1009
+ });
1010
+ }
1011
+ });
1012
+
1013
+ this._commands.addCommand('claude-code-sessions:copy-path', {
1014
+ label: 'Copy Path',
1015
+ execute: () => {
1016
+ if (!this._activeSession) {
1017
+ return;
1018
+ }
1019
+ const path = this._activeSession.project_path;
1020
+ Clipboard.copyToSystem(path);
1021
+ Notification.success(`Copied: ${path}`, { autoClose: 2000 });
1022
+ }
1023
+ });
1024
+
950
1025
  this._commands.addCommand('claude-code-sessions:remove', {
951
1026
  label: 'Remove from Claude',
952
1027
  icon: removeIcon,
@@ -963,9 +1038,16 @@ export class ClaudeCodeSessionsWidget extends Widget {
963
1038
  this._contextMenu.addItem({
964
1039
  command: 'claude-code-sessions:resume-dangerous'
965
1040
  });
1041
+ this._contextMenu.addItem({
1042
+ command: 'claude-code-sessions:open-terminal'
1043
+ });
1044
+ this._contextMenu.addItem({
1045
+ command: 'claude-code-sessions:show-in-filebrowser'
1046
+ });
966
1047
  this._contextMenu.addItem({
967
1048
  command: 'claude-code-sessions:toggle-favourite'
968
1049
  });
1050
+ this._contextMenu.addItem({ command: 'claude-code-sessions:copy-path' });
969
1051
  this._contextMenu.addItem({ type: 'separator' });
970
1052
  this._contextMenu.addItem({ command: 'claude-code-sessions:remove' });
971
1053