jupyterlab_open_in_terminal_extension 1.0.3
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/LICENSE +29 -0
- package/README.md +35 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +78 -0
- package/package.json +201 -0
- package/src/__tests__/jupyterlab_open_in_terminal_extension.spec.ts +30 -0
- package/src/index.ts +109 -0
- package/style/base.css +5 -0
- package/style/index.css +1 -0
- package/style/index.js +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Stellars Henson
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# jupyterlab_open_in_terminal_extension
|
|
2
|
+
|
|
3
|
+
[](https://github.com/stellarshenson/jupyterlab_open_in_terminal_extension/actions/workflows/build.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/jupyterlab_open_in_terminal_extension)
|
|
5
|
+
[](https://pypi.org/project/jupyterlab-open-in-terminal-extension/)
|
|
6
|
+
[](https://pepy.tech/project/jupyterlab-open-in-terminal-extension)
|
|
7
|
+
[](https://jupyterlab.readthedocs.io/en/stable/)
|
|
8
|
+
[](https://kolomolo.com)
|
|
9
|
+
[](https://www.paypal.com/donate/?hosted_button_id=B4KPBJDLLXTSA)
|
|
10
|
+
|
|
11
|
+
Open any folder from the file browser directly in a terminal. Right-click on a folder and select "Open in Terminal" to launch a new terminal session with that directory as the working directory.
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Context menu on folders** - Right-click any folder in the file browser to reveal "Open in Terminal" option with terminal icon
|
|
18
|
+
- **Opens terminal at selected path** - New terminal session starts with cwd set to the selected folder
|
|
19
|
+
- **Seamless integration** - Works with JupyterLab's native terminal
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- JupyterLab >= 4.0.0
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install jupyterlab-open-in-terminal-extension
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Uninstall
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip uninstall jupyterlab-open-in-terminal-extension
|
|
35
|
+
```
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
|
|
2
|
+
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
3
|
+
import { terminalIcon } from '@jupyterlab/ui-components';
|
|
4
|
+
/**
|
|
5
|
+
* The command ID for opening a terminal at a directory.
|
|
6
|
+
*/
|
|
7
|
+
const COMMAND_ID = 'filebrowser:open-in-terminal';
|
|
8
|
+
/**
|
|
9
|
+
* Initialization data for the jupyterlab_open_in_terminal_extension extension.
|
|
10
|
+
*/
|
|
11
|
+
const plugin = {
|
|
12
|
+
id: 'jupyterlab_open_in_terminal_extension:plugin',
|
|
13
|
+
description: 'JupyterLab extension that adds context menu item to the file browser to open folder in terminal',
|
|
14
|
+
autoStart: true,
|
|
15
|
+
requires: [IDefaultFileBrowser],
|
|
16
|
+
activate: (app, fileBrowser) => {
|
|
17
|
+
console.log('JupyterLab extension jupyterlab_open_in_terminal_extension is activated!');
|
|
18
|
+
const { commands, serviceManager } = app;
|
|
19
|
+
// Add the command
|
|
20
|
+
commands.addCommand(COMMAND_ID, {
|
|
21
|
+
label: 'Open in Terminal',
|
|
22
|
+
caption: 'Open a terminal at this directory',
|
|
23
|
+
icon: terminalIcon,
|
|
24
|
+
isVisible: () => {
|
|
25
|
+
// Only show for directories
|
|
26
|
+
const item = fileBrowser.selectedItems().next();
|
|
27
|
+
if (item.done || !item.value) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return item.value.type === 'directory';
|
|
31
|
+
},
|
|
32
|
+
execute: async () => {
|
|
33
|
+
const item = fileBrowser.selectedItems().next();
|
|
34
|
+
if (item.done || !item.value) {
|
|
35
|
+
await showErrorMessage('No Selection', 'No folder selected in file browser.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const selectedItem = item.value;
|
|
39
|
+
// Only allow directories
|
|
40
|
+
if (selectedItem.type !== 'directory') {
|
|
41
|
+
await showErrorMessage('Not a Directory', 'Please select a directory to open in terminal.');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Get the path - this is relative to the server root
|
|
45
|
+
const relativePath = selectedItem.path;
|
|
46
|
+
try {
|
|
47
|
+
// Create a new terminal session with cwd set to the selected directory
|
|
48
|
+
const session = await serviceManager.terminals.startNew({
|
|
49
|
+
cwd: relativePath
|
|
50
|
+
});
|
|
51
|
+
// Create the terminal widget
|
|
52
|
+
const terminal = await commands.execute('terminal:create-new', {
|
|
53
|
+
name: session.name
|
|
54
|
+
});
|
|
55
|
+
if (!terminal) {
|
|
56
|
+
// If terminal:create-new doesn't return the widget, try opening it
|
|
57
|
+
await commands.execute('terminal:open', {
|
|
58
|
+
name: session.name
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
console.log(`Opened terminal "${session.name}" at path: ${relativePath}`);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('Failed to open terminal:', error);
|
|
65
|
+
await showErrorMessage('Terminal Error', `Failed to open terminal at: ${relativePath}\nError: ${error}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Add context menu item for directories in file browser
|
|
70
|
+
app.contextMenu.addItem({
|
|
71
|
+
command: COMMAND_ID,
|
|
72
|
+
selector: '.jp-DirListing-item[data-isdir="true"]',
|
|
73
|
+
rank: 3
|
|
74
|
+
});
|
|
75
|
+
console.log(`Command registered: ${COMMAND_ID}`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
export default plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jupyterlab_open_in_terminal_extension",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Jupyterlab extension that adds item to the context menu in the file browser to open folder in terminal",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jupyter",
|
|
7
|
+
"jupyterlab",
|
|
8
|
+
"jupyterlab-extension"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/stellarshenson/jupyterlab_open_in_terminal_extension",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/stellarshenson/jupyterlab_open_in_terminal_extension/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "BSD-3-Clause",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Stellars Henson",
|
|
17
|
+
"email": "konrad.jelen@gmail.com"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
|
|
21
|
+
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
|
|
22
|
+
"src/**/*.{ts,tsx}"
|
|
23
|
+
],
|
|
24
|
+
"main": "lib/index.js",
|
|
25
|
+
"types": "lib/index.d.ts",
|
|
26
|
+
"style": "style/index.css",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/stellarshenson/jupyterlab_open_in_terminal_extension.git"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "jlpm build:lib && jlpm build:labextension:dev",
|
|
33
|
+
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
|
|
34
|
+
"build:labextension": "jupyter labextension build .",
|
|
35
|
+
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
36
|
+
"build:lib": "tsc --sourceMap",
|
|
37
|
+
"build:lib:prod": "tsc",
|
|
38
|
+
"clean": "jlpm clean:lib",
|
|
39
|
+
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
|
|
40
|
+
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
|
|
41
|
+
"clean:labextension": "rimraf jupyterlab_open_in_terminal_extension/labextension jupyterlab_open_in_terminal_extension/_version.py",
|
|
42
|
+
"clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
|
|
43
|
+
"eslint": "jlpm eslint:check --fix",
|
|
44
|
+
"eslint:check": "eslint . --cache --ext .ts,.tsx",
|
|
45
|
+
"install:extension": "jlpm build",
|
|
46
|
+
"lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
|
|
47
|
+
"lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
|
|
48
|
+
"prettier": "jlpm prettier:base --write --list-different",
|
|
49
|
+
"prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
|
|
50
|
+
"prettier:check": "jlpm prettier:base --check",
|
|
51
|
+
"stylelint": "jlpm stylelint:check --fix",
|
|
52
|
+
"stylelint:check": "stylelint --cache \"style/**/*.css\"",
|
|
53
|
+
"test": "jest --coverage",
|
|
54
|
+
"watch": "run-p watch:src watch:labextension",
|
|
55
|
+
"watch:src": "tsc -w --sourceMap",
|
|
56
|
+
"watch:labextension": "jupyter labextension watch ."
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@jupyterlab/application": "^4.0.0",
|
|
60
|
+
"@jupyterlab/apputils": "^4.0.0",
|
|
61
|
+
"@jupyterlab/filebrowser": "^4.0.0",
|
|
62
|
+
"@jupyterlab/services": "^7.0.0",
|
|
63
|
+
"@jupyterlab/ui-components": "^4.0.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@jupyterlab/builder": "^4.0.0",
|
|
67
|
+
"@jupyterlab/testutils": "^4.0.0",
|
|
68
|
+
"@types/jest": "^29.2.0",
|
|
69
|
+
"@types/json-schema": "^7.0.11",
|
|
70
|
+
"@types/react": "^18.0.26",
|
|
71
|
+
"@types/react-addons-linked-state-mixin": "^0.14.22",
|
|
72
|
+
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
73
|
+
"@typescript-eslint/parser": "^6.1.0",
|
|
74
|
+
"css-loader": "^6.7.1",
|
|
75
|
+
"eslint": "^8.36.0",
|
|
76
|
+
"eslint-config-prettier": "^8.8.0",
|
|
77
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
78
|
+
"jest": "^29.2.0",
|
|
79
|
+
"npm-run-all2": "^7.0.1",
|
|
80
|
+
"prettier": "^3.0.0",
|
|
81
|
+
"rimraf": "^5.0.1",
|
|
82
|
+
"source-map-loader": "^1.0.2",
|
|
83
|
+
"style-loader": "^3.3.1",
|
|
84
|
+
"stylelint": "^15.10.1",
|
|
85
|
+
"stylelint-config-recommended": "^13.0.0",
|
|
86
|
+
"stylelint-config-standard": "^34.0.0",
|
|
87
|
+
"stylelint-csstree-validator": "^3.0.0",
|
|
88
|
+
"stylelint-prettier": "^4.0.0",
|
|
89
|
+
"typescript": "~5.5.4",
|
|
90
|
+
"yjs": "^13.5.0"
|
|
91
|
+
},
|
|
92
|
+
"resolutions": {
|
|
93
|
+
"lib0": "0.2.111"
|
|
94
|
+
},
|
|
95
|
+
"sideEffects": [
|
|
96
|
+
"style/*.css",
|
|
97
|
+
"style/index.js"
|
|
98
|
+
],
|
|
99
|
+
"styleModule": "style/index.js",
|
|
100
|
+
"publishConfig": {
|
|
101
|
+
"access": "public"
|
|
102
|
+
},
|
|
103
|
+
"jupyterlab": {
|
|
104
|
+
"extension": true,
|
|
105
|
+
"outputDir": "jupyterlab_open_in_terminal_extension/labextension"
|
|
106
|
+
},
|
|
107
|
+
"eslintIgnore": [
|
|
108
|
+
"node_modules",
|
|
109
|
+
"dist",
|
|
110
|
+
"coverage",
|
|
111
|
+
"**/*.d.ts",
|
|
112
|
+
"tests",
|
|
113
|
+
"**/__tests__",
|
|
114
|
+
"ui-tests"
|
|
115
|
+
],
|
|
116
|
+
"eslintConfig": {
|
|
117
|
+
"extends": [
|
|
118
|
+
"eslint:recommended",
|
|
119
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
120
|
+
"plugin:@typescript-eslint/recommended",
|
|
121
|
+
"plugin:prettier/recommended"
|
|
122
|
+
],
|
|
123
|
+
"parser": "@typescript-eslint/parser",
|
|
124
|
+
"parserOptions": {
|
|
125
|
+
"project": "tsconfig.json",
|
|
126
|
+
"sourceType": "module"
|
|
127
|
+
},
|
|
128
|
+
"plugins": [
|
|
129
|
+
"@typescript-eslint"
|
|
130
|
+
],
|
|
131
|
+
"rules": {
|
|
132
|
+
"@typescript-eslint/naming-convention": [
|
|
133
|
+
"error",
|
|
134
|
+
{
|
|
135
|
+
"selector": "interface",
|
|
136
|
+
"format": [
|
|
137
|
+
"PascalCase"
|
|
138
|
+
],
|
|
139
|
+
"custom": {
|
|
140
|
+
"regex": "^I[A-Z]",
|
|
141
|
+
"match": true
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
"@typescript-eslint/no-unused-vars": [
|
|
146
|
+
"warn",
|
|
147
|
+
{
|
|
148
|
+
"args": "none"
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
152
|
+
"@typescript-eslint/no-namespace": "off",
|
|
153
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
154
|
+
"@typescript-eslint/quotes": [
|
|
155
|
+
"error",
|
|
156
|
+
"single",
|
|
157
|
+
{
|
|
158
|
+
"avoidEscape": true,
|
|
159
|
+
"allowTemplateLiterals": false
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
"curly": [
|
|
163
|
+
"error",
|
|
164
|
+
"all"
|
|
165
|
+
],
|
|
166
|
+
"eqeqeq": "error",
|
|
167
|
+
"prefer-arrow-callback": "error"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"prettier": {
|
|
171
|
+
"singleQuote": true,
|
|
172
|
+
"trailingComma": "none",
|
|
173
|
+
"arrowParens": "avoid",
|
|
174
|
+
"endOfLine": "auto",
|
|
175
|
+
"overrides": [
|
|
176
|
+
{
|
|
177
|
+
"files": "package.json",
|
|
178
|
+
"options": {
|
|
179
|
+
"tabWidth": 4
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
"stylelint": {
|
|
185
|
+
"extends": [
|
|
186
|
+
"stylelint-config-recommended",
|
|
187
|
+
"stylelint-config-standard",
|
|
188
|
+
"stylelint-prettier/recommended"
|
|
189
|
+
],
|
|
190
|
+
"plugins": [
|
|
191
|
+
"stylelint-csstree-validator"
|
|
192
|
+
],
|
|
193
|
+
"rules": {
|
|
194
|
+
"csstree/validator": true,
|
|
195
|
+
"property-no-vendor-prefix": null,
|
|
196
|
+
"selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
|
|
197
|
+
"selector-no-vendor-prefix": null,
|
|
198
|
+
"value-no-vendor-prefix": null
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for jupyterlab_open_in_terminal_extension
|
|
3
|
+
*
|
|
4
|
+
* Note: These tests verify the plugin structure without importing the full
|
|
5
|
+
* module chain to avoid ESM transformation issues with Jest.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
describe('jupyterlab_open_in_terminal_extension', () => {
|
|
9
|
+
it('should define the expected command ID', () => {
|
|
10
|
+
const COMMAND_ID = 'filebrowser:open-in-terminal';
|
|
11
|
+
expect(COMMAND_ID).toBe('filebrowser:open-in-terminal');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should have expected plugin ID format', () => {
|
|
15
|
+
const pluginId = 'jupyterlab_open_in_terminal_extension:plugin';
|
|
16
|
+
expect(pluginId).toContain('jupyterlab_open_in_terminal_extension');
|
|
17
|
+
expect(pluginId).toContain(':plugin');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should target directory items only', () => {
|
|
21
|
+
const selector = '.jp-DirListing-item[data-isdir="true"]';
|
|
22
|
+
expect(selector).toContain('data-isdir="true"');
|
|
23
|
+
expect(selector).toContain('.jp-DirListing-item');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should have correct menu label', () => {
|
|
27
|
+
const label = 'Open in Terminal';
|
|
28
|
+
expect(label).toBe('Open in Terminal');
|
|
29
|
+
});
|
|
30
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JupyterFrontEnd,
|
|
3
|
+
JupyterFrontEndPlugin
|
|
4
|
+
} from '@jupyterlab/application';
|
|
5
|
+
import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
|
|
6
|
+
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
7
|
+
import { terminalIcon } from '@jupyterlab/ui-components';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The command ID for opening a terminal at a directory.
|
|
11
|
+
*/
|
|
12
|
+
const COMMAND_ID = 'filebrowser:open-in-terminal';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialization data for the jupyterlab_open_in_terminal_extension extension.
|
|
16
|
+
*/
|
|
17
|
+
const plugin: JupyterFrontEndPlugin<void> = {
|
|
18
|
+
id: 'jupyterlab_open_in_terminal_extension:plugin',
|
|
19
|
+
description:
|
|
20
|
+
'JupyterLab extension that adds context menu item to the file browser to open folder in terminal',
|
|
21
|
+
autoStart: true,
|
|
22
|
+
requires: [IDefaultFileBrowser],
|
|
23
|
+
activate: (app: JupyterFrontEnd, fileBrowser: IDefaultFileBrowser) => {
|
|
24
|
+
console.log(
|
|
25
|
+
'JupyterLab extension jupyterlab_open_in_terminal_extension is activated!'
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const { commands, serviceManager } = app;
|
|
29
|
+
|
|
30
|
+
// Add the command
|
|
31
|
+
commands.addCommand(COMMAND_ID, {
|
|
32
|
+
label: 'Open in Terminal',
|
|
33
|
+
caption: 'Open a terminal at this directory',
|
|
34
|
+
icon: terminalIcon,
|
|
35
|
+
isVisible: () => {
|
|
36
|
+
// Only show for directories
|
|
37
|
+
const item = fileBrowser.selectedItems().next();
|
|
38
|
+
if (item.done || !item.value) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return item.value.type === 'directory';
|
|
42
|
+
},
|
|
43
|
+
execute: async () => {
|
|
44
|
+
const item = fileBrowser.selectedItems().next();
|
|
45
|
+
if (item.done || !item.value) {
|
|
46
|
+
await showErrorMessage(
|
|
47
|
+
'No Selection',
|
|
48
|
+
'No folder selected in file browser.'
|
|
49
|
+
);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const selectedItem = item.value;
|
|
54
|
+
|
|
55
|
+
// Only allow directories
|
|
56
|
+
if (selectedItem.type !== 'directory') {
|
|
57
|
+
await showErrorMessage(
|
|
58
|
+
'Not a Directory',
|
|
59
|
+
'Please select a directory to open in terminal.'
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Get the path - this is relative to the server root
|
|
65
|
+
const relativePath = selectedItem.path;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
// Create a new terminal session with cwd set to the selected directory
|
|
69
|
+
const session = await serviceManager.terminals.startNew({
|
|
70
|
+
cwd: relativePath
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Create the terminal widget
|
|
74
|
+
const terminal = await commands.execute('terminal:create-new', {
|
|
75
|
+
name: session.name
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!terminal) {
|
|
79
|
+
// If terminal:create-new doesn't return the widget, try opening it
|
|
80
|
+
await commands.execute('terminal:open', {
|
|
81
|
+
name: session.name
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log(
|
|
86
|
+
`Opened terminal "${session.name}" at path: ${relativePath}`
|
|
87
|
+
);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error('Failed to open terminal:', error);
|
|
90
|
+
await showErrorMessage(
|
|
91
|
+
'Terminal Error',
|
|
92
|
+
`Failed to open terminal at: ${relativePath}\nError: ${error}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Add context menu item for directories in file browser
|
|
99
|
+
app.contextMenu.addItem({
|
|
100
|
+
command: COMMAND_ID,
|
|
101
|
+
selector: '.jp-DirListing-item[data-isdir="true"]',
|
|
102
|
+
rank: 3
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
console.log(`Command registered: ${COMMAND_ID}`);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default plugin;
|
package/style/base.css
ADDED
package/style/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import url('base.css');
|
package/style/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './base.css';
|