xtralab 0.6.0 → 0.7.0
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/README.md +30 -0
- package/lib/launcher/dashboard.js +20 -5
- package/lib/topBar/index.js +12 -0
- package/package.json +1 -1
- package/src/launcher/dashboard.tsx +22 -6
- package/src/topBar/index.ts +13 -0
package/README.md
CHANGED
|
@@ -82,6 +82,36 @@ See the [`jupyter-server-mcp` README][mcp] for other MCP clients.
|
|
|
82
82
|
[mcp]: https://github.com/jupyter-ai-contrib/jupyter-server-mcp
|
|
83
83
|
[mcp-spec]: https://modelcontextprotocol.io
|
|
84
84
|
|
|
85
|
+
## Agent skill
|
|
86
|
+
|
|
87
|
+
xtralab ships an [Agent Skills][skills] entry at [`agent-skill/`](./agent-skill)
|
|
88
|
+
that teaches any coding agent how to customize JupyterLab from plain-English
|
|
89
|
+
requests like "hide the status bar", "change the theme", or "add a language
|
|
90
|
+
server". The same SKILL.md file works with Claude Code, Codex CLI, Gemini CLI,
|
|
91
|
+
GitHub Copilot, and other tools that read the Agent Skills format.
|
|
92
|
+
|
|
93
|
+
For Claude Code, this repository doubles as a one-plugin marketplace. Inside a
|
|
94
|
+
Claude Code session:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
/plugin marketplace add jtpio/xtralab
|
|
98
|
+
/plugin install xtralab-skills@xtralab
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For agents that read from `~/.agents/skills/`, such as Codex CLI or Gemini CLI,
|
|
102
|
+
clone and copy the skill directory:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git clone --depth=1 https://github.com/jtpio/xtralab.git /tmp/xtralab
|
|
106
|
+
mkdir -p ~/.agents/skills
|
|
107
|
+
cp -r /tmp/xtralab/agent-skill/skills/customize-jupyterlab ~/.agents/skills/
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
See [`agent-skill/README.md`](./agent-skill/README.md) for additional install
|
|
111
|
+
paths, the list of supported agents, and what the skill knows.
|
|
112
|
+
|
|
113
|
+
[skills]: https://agentskills.io
|
|
114
|
+
|
|
85
115
|
## Language servers
|
|
86
116
|
|
|
87
117
|
xtralab ships [`jupyterlab-lsp`][lsp] with two language servers pre-registered:
|
|
@@ -91,13 +91,28 @@ function LauncherDashboardComponent(props) {
|
|
|
91
91
|
}
|
|
92
92
|
}, [commands, cwd, onAgentLaunch]);
|
|
93
93
|
const launchNotebook = React.useCallback(async () => {
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
// Open a fresh notebook with no kernel attached — equivalent to clicking
|
|
95
|
+
// "No Kernel" in the Select Kernel dialog. `notebook:create-new` always
|
|
96
|
+
// routes through that dialog when no kernelName is given, so drive the
|
|
97
|
+
// underlying docmanager steps directly and pass `shouldStart: false` to
|
|
98
|
+
// suppress kernel selection. The user picks a kernel from the notebook
|
|
99
|
+
// toolbar when they're ready.
|
|
100
|
+
const file = (await commands.execute('docmanager:new-untitled', {
|
|
101
|
+
path: cwd,
|
|
102
|
+
type: 'notebook'
|
|
103
|
+
}));
|
|
104
|
+
if (!file) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const result = (await commands.execute('docmanager:open', {
|
|
108
|
+
path: file.path,
|
|
109
|
+
factory: 'Notebook',
|
|
110
|
+
kernelPreference: { shouldStart: false, canStart: true }
|
|
99
111
|
}));
|
|
100
112
|
if (result) {
|
|
113
|
+
// Match `notebook:create-new`'s post-open marker so the first manual
|
|
114
|
+
// save offers a rename instead of overwriting the auto-generated name.
|
|
115
|
+
result.isUntitled = true;
|
|
101
116
|
onAgentLaunch(result);
|
|
102
117
|
}
|
|
103
118
|
}, [commands, cwd, onAgentLaunch]);
|
package/lib/topBar/index.js
CHANGED
|
@@ -52,6 +52,18 @@ const plugin = {
|
|
|
52
52
|
activate: (app, labShell, translator) => {
|
|
53
53
|
const { commands } = app;
|
|
54
54
|
const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
|
|
55
|
+
// The upstream toggle-{left,right}-area commands derive `isEnabled` from
|
|
56
|
+
// `!labShell.isEmpty(side)` but don't fire `commandChanged` when widgets
|
|
57
|
+
// move in or out of a side area. `CommandToolbarButton` only re-renders on
|
|
58
|
+
// `commandChanged` for its own id, so the buttons need an explicit notify
|
|
59
|
+
// whenever the shell layout shifts — `layoutModified` covers each side
|
|
60
|
+
// handler's `_updated` (add/remove, expand/collapse), keeping both
|
|
61
|
+
// `isEnabled` and `isToggled` in sync.
|
|
62
|
+
labShell.layoutModified.connect(() => {
|
|
63
|
+
for (const spec of BUTTONS) {
|
|
64
|
+
commands.notifyCommandChanged(spec.command);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
55
67
|
// Defer to `app.restored` so the upstream toggle commands are already in
|
|
56
68
|
// the registry: `CommandToolbarButton` renders nothing until its command
|
|
57
69
|
// exists and only re-renders on `commandChanged` of type `changed` /
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xtralab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "An opinionated JupyterLab meta-package that bundles a curated set of extensions, ships a path-first file browser, renders jupyterlab-git's text, notebook and image diffs with its own viewers, and applies a quieter default workspace configuration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -3,6 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import type { CommandRegistry } from '@lumino/commands';
|
|
4
4
|
import type { ReadonlyPartialJSONObject } from '@lumino/coreutils';
|
|
5
5
|
import { Poll } from '@lumino/polling';
|
|
6
|
+
import type { IDocumentWidget } from '@jupyterlab/docregistry';
|
|
6
7
|
import {
|
|
7
8
|
LabIcon,
|
|
8
9
|
ReactWidget,
|
|
@@ -193,13 +194,28 @@ function LauncherDashboardComponent(
|
|
|
193
194
|
}, [commands, cwd, onAgentLaunch]);
|
|
194
195
|
|
|
195
196
|
const launchNotebook = React.useCallback(async () => {
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
// Open a fresh notebook with no kernel attached — equivalent to clicking
|
|
198
|
+
// "No Kernel" in the Select Kernel dialog. `notebook:create-new` always
|
|
199
|
+
// routes through that dialog when no kernelName is given, so drive the
|
|
200
|
+
// underlying docmanager steps directly and pass `shouldStart: false` to
|
|
201
|
+
// suppress kernel selection. The user picks a kernel from the notebook
|
|
202
|
+
// toolbar when they're ready.
|
|
203
|
+
const file = (await commands.execute('docmanager:new-untitled', {
|
|
204
|
+
path: cwd,
|
|
205
|
+
type: 'notebook'
|
|
206
|
+
})) as { path: string } | undefined;
|
|
207
|
+
if (!file) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const result = (await commands.execute('docmanager:open', {
|
|
211
|
+
path: file.path,
|
|
212
|
+
factory: 'Notebook',
|
|
213
|
+
kernelPreference: { shouldStart: false, canStart: true }
|
|
214
|
+
})) as IDocumentWidget | undefined;
|
|
202
215
|
if (result) {
|
|
216
|
+
// Match `notebook:create-new`'s post-open marker so the first manual
|
|
217
|
+
// save offers a rename instead of overwriting the auto-generated name.
|
|
218
|
+
result.isUntitled = true;
|
|
203
219
|
onAgentLaunch(result);
|
|
204
220
|
}
|
|
205
221
|
}, [commands, cwd, onAgentLaunch]);
|
package/src/topBar/index.ts
CHANGED
|
@@ -86,6 +86,19 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
|
|
86
86
|
const { commands } = app;
|
|
87
87
|
const trans = (translator ?? nullTranslator).load('jupyterlab');
|
|
88
88
|
|
|
89
|
+
// The upstream toggle-{left,right}-area commands derive `isEnabled` from
|
|
90
|
+
// `!labShell.isEmpty(side)` but don't fire `commandChanged` when widgets
|
|
91
|
+
// move in or out of a side area. `CommandToolbarButton` only re-renders on
|
|
92
|
+
// `commandChanged` for its own id, so the buttons need an explicit notify
|
|
93
|
+
// whenever the shell layout shifts — `layoutModified` covers each side
|
|
94
|
+
// handler's `_updated` (add/remove, expand/collapse), keeping both
|
|
95
|
+
// `isEnabled` and `isToggled` in sync.
|
|
96
|
+
labShell.layoutModified.connect(() => {
|
|
97
|
+
for (const spec of BUTTONS) {
|
|
98
|
+
commands.notifyCommandChanged(spec.command);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
89
102
|
// Defer to `app.restored` so the upstream toggle commands are already in
|
|
90
103
|
// the registry: `CommandToolbarButton` renders nothing until its command
|
|
91
104
|
// exists and only re-renders on `commandChanged` of type `changed` /
|