xtralab 0.10.0 → 0.11.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 +23 -9
- package/lib/commandBar/index.d.ts +14 -0
- package/lib/commandBar/index.js +114 -0
- package/lib/fileBrowser/fileBrowser.js +6 -1
- package/lib/highlight/index.d.ts +15 -0
- package/lib/highlight/index.js +214 -0
- package/lib/index.js +13 -1
- package/lib/launcher/agents.d.ts +7 -0
- package/lib/launcher/agents.js +24 -0
- package/lib/launcher/editorRegistry.js +5 -3
- package/lib/launcher/editors.d.ts +8 -0
- package/lib/launcher/editors.js +17 -0
- package/lib/launcher/index.js +4 -0
- package/lib/launcher/schemaDefaults.d.ts +15 -0
- package/lib/launcher/schemaDefaults.js +44 -0
- package/lib/omnibox/files.d.ts +7 -0
- package/lib/omnibox/files.js +50 -0
- package/lib/omnibox/index.d.ts +14 -0
- package/lib/omnibox/index.js +83 -0
- package/lib/omnibox/model.d.ts +45 -0
- package/lib/omnibox/model.js +142 -0
- package/lib/omnibox/tokens.d.ts +23 -0
- package/lib/omnibox/tokens.js +11 -0
- package/lib/omnibox/widget.d.ts +36 -0
- package/lib/omnibox/widget.js +161 -0
- package/lib/searchReplace/index.d.ts +25 -0
- package/lib/searchReplace/index.js +68 -0
- package/lib/showOutput/index.d.ts +17 -0
- package/lib/showOutput/index.js +154 -0
- package/lib/terminals/index.d.ts +4 -0
- package/lib/terminals/index.js +65 -16
- package/lib/terminals/model.d.ts +56 -3
- package/lib/terminals/model.js +261 -5
- package/lib/terminals/widget.d.ts +3 -1
- package/lib/terminals/widget.js +11 -3
- package/lib/walkthrough/index.d.ts +14 -0
- package/lib/walkthrough/index.js +162 -0
- package/lib/walkthrough/panel.d.ts +54 -0
- package/lib/walkthrough/panel.js +98 -0
- package/package.json +1 -1
- package/schema/launcher.json +3 -2
- package/schema/terminals.json +15 -0
- package/src/commandBar/index.ts +165 -0
- package/src/fileBrowser/fileBrowser.tsx +8 -1
- package/src/highlight/index.ts +251 -0
- package/src/index.ts +13 -1
- package/src/launcher/agents.ts +25 -0
- package/src/launcher/editorRegistry.ts +8 -4
- package/src/launcher/editors.ts +18 -0
- package/src/launcher/index.ts +4 -0
- package/src/launcher/schemaDefaults.ts +53 -0
- package/src/omnibox/files.ts +68 -0
- package/src/omnibox/index.ts +105 -0
- package/src/omnibox/model.ts +210 -0
- package/src/omnibox/tokens.ts +29 -0
- package/src/omnibox/widget.tsx +322 -0
- package/src/searchReplace/index.ts +86 -0
- package/src/showOutput/index.ts +199 -0
- package/src/terminals/index.ts +79 -16
- package/src/terminals/model.ts +304 -3
- package/src/terminals/widget.tsx +21 -4
- package/src/walkthrough/index.ts +202 -0
- package/src/walkthrough/panel.ts +152 -0
- package/style/commandBar.css +104 -0
- package/style/highlight.css +11 -0
- package/style/index.css +5 -0
- package/style/index.js +5 -0
- package/style/omnibox.css +120 -0
- package/style/showOutput.css +11 -0
- package/style/terminals.css +54 -10
- package/style/walkthrough.css +80 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ILabShell,
|
|
3
|
+
JupyterFrontEnd,
|
|
4
|
+
JupyterFrontEndPlugin
|
|
5
|
+
} from '@jupyterlab/application';
|
|
6
|
+
import { ICommandPalette } from '@jupyterlab/apputils';
|
|
7
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
8
|
+
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
9
|
+
import { tocIcon } from '@jupyterlab/ui-components';
|
|
10
|
+
|
|
11
|
+
import { IWalkthroughMedia, IWalkthroughStep, WalkthroughPanel } from './panel';
|
|
12
|
+
|
|
13
|
+
const PLUGIN_ID = 'xtralab:walkthrough';
|
|
14
|
+
|
|
15
|
+
/** Append a step to the read-only walkthrough panel. */
|
|
16
|
+
const WALKTHROUGH_COMMAND = 'xtralab:walkthrough';
|
|
17
|
+
|
|
18
|
+
const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
|
|
19
|
+
|
|
20
|
+
const PANEL_ID = 'xtralab-walkthrough';
|
|
21
|
+
|
|
22
|
+
/** Pull a `{ mimeType, data }` media object out of a JSON command argument. */
|
|
23
|
+
function readMedia(value: unknown): IWalkthroughMedia | undefined {
|
|
24
|
+
if (
|
|
25
|
+
value &&
|
|
26
|
+
typeof value === 'object' &&
|
|
27
|
+
!Array.isArray(value) &&
|
|
28
|
+
typeof (value as { mimeType?: unknown }).mimeType === 'string'
|
|
29
|
+
) {
|
|
30
|
+
const media = value as { mimeType: string; data?: unknown };
|
|
31
|
+
return { mimeType: media.mimeType, data: media.data as never };
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function toLine(value: unknown): number | undefined {
|
|
37
|
+
const n = Math.trunc(Number(value));
|
|
38
|
+
return Number.isFinite(n) && n >= 1 ? n : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Contribute `xtralab:walkthrough`: build a persistent, read-only walkthrough
|
|
43
|
+
* in the right side area instead of narrating only in the agent's chat.
|
|
44
|
+
*
|
|
45
|
+
* Each call appends a step (Markdown prose, an optional embedded visual, and an
|
|
46
|
+
* optional code reference). When a step names a file, the editor follows along
|
|
47
|
+
* (opening it full-width in the main area, no split) and its lines are
|
|
48
|
+
* highlighted; the step also keeps a button so the user can jump back to it
|
|
49
|
+
* later. The panel accumulates the whole tour beside the code, so the user can
|
|
50
|
+
* read at their own pace rather than chasing the chat.
|
|
51
|
+
*/
|
|
52
|
+
const plugin: JupyterFrontEndPlugin<void> = {
|
|
53
|
+
id: PLUGIN_ID,
|
|
54
|
+
description: 'Build a read-only guided walkthrough in the side panel.',
|
|
55
|
+
autoStart: true,
|
|
56
|
+
requires: [IRenderMimeRegistry, ILabShell],
|
|
57
|
+
optional: [ICommandPalette, ITranslator],
|
|
58
|
+
activate: (
|
|
59
|
+
app: JupyterFrontEnd,
|
|
60
|
+
rendermime: IRenderMimeRegistry,
|
|
61
|
+
labShell: ILabShell,
|
|
62
|
+
palette: ICommandPalette | null,
|
|
63
|
+
translator: ITranslator | null
|
|
64
|
+
): void => {
|
|
65
|
+
const { commands } = app;
|
|
66
|
+
const trans = (translator ?? nullTranslator).load('jupyterlab');
|
|
67
|
+
|
|
68
|
+
let panel: WalkthroughPanel | null = null;
|
|
69
|
+
|
|
70
|
+
const ensurePanel = (): WalkthroughPanel => {
|
|
71
|
+
if (!panel || panel.isDisposed) {
|
|
72
|
+
panel = new WalkthroughPanel({ rendermime, commands, trans });
|
|
73
|
+
panel.id = PANEL_ID;
|
|
74
|
+
panel.title.icon = tocIcon;
|
|
75
|
+
panel.title.label = trans.__('Walkthrough');
|
|
76
|
+
panel.title.caption = trans.__('Walkthrough');
|
|
77
|
+
// Closable so it gets a close button if the user drags it out of the
|
|
78
|
+
// side area into the main document area. Closing disposes it; the next
|
|
79
|
+
// xtralab:walkthrough call recreates it back in the side area.
|
|
80
|
+
panel.title.closable = true;
|
|
81
|
+
labShell.add(panel, 'right', { rank: 1000, type: 'Walkthrough' });
|
|
82
|
+
const created = panel;
|
|
83
|
+
created.disposed.connect(() => {
|
|
84
|
+
if (panel === created) {
|
|
85
|
+
panel = null;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return panel;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
commands.addCommand(WALKTHROUGH_COMMAND, {
|
|
93
|
+
label: trans.__('Add Walkthrough Step'),
|
|
94
|
+
caption: trans.__('Append a step to the read-only walkthrough panel'),
|
|
95
|
+
describedBy: {
|
|
96
|
+
args: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
title: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
description: 'Optional heading for the step.'
|
|
102
|
+
},
|
|
103
|
+
body: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
description:
|
|
106
|
+
'Markdown for the step. Renders code snippets, lists, math, and Mermaid diagrams from a ```mermaid fenced block.'
|
|
107
|
+
},
|
|
108
|
+
path: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description:
|
|
111
|
+
'Optional file the step is about, relative to the server root. The editor opens it (full width, no split) and highlights the lines, and the step keeps a button to jump back.'
|
|
112
|
+
},
|
|
113
|
+
line: {
|
|
114
|
+
type: 'number',
|
|
115
|
+
description: 'First line to highlight (1-indexed).'
|
|
116
|
+
},
|
|
117
|
+
endLine: {
|
|
118
|
+
type: 'number',
|
|
119
|
+
description: 'Last line to highlight (1-indexed, inclusive).'
|
|
120
|
+
},
|
|
121
|
+
media: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
description:
|
|
124
|
+
'Optional embedded visual, e.g. { "mimeType": "application/vnd.vegalite.v5+json", "data": <spec> }.',
|
|
125
|
+
properties: {
|
|
126
|
+
mimeType: { type: 'string' },
|
|
127
|
+
data: {}
|
|
128
|
+
},
|
|
129
|
+
required: ['mimeType', 'data']
|
|
130
|
+
},
|
|
131
|
+
reveal: {
|
|
132
|
+
type: 'boolean',
|
|
133
|
+
description:
|
|
134
|
+
'Whether to navigate and highlight the code as the step is added. Defaults to true.'
|
|
135
|
+
},
|
|
136
|
+
reset: {
|
|
137
|
+
type: 'boolean',
|
|
138
|
+
description:
|
|
139
|
+
'Clear the existing walkthrough before adding this step (start a new tour).'
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
execute: async args => {
|
|
145
|
+
const current = ensurePanel();
|
|
146
|
+
if (args['reset'] === true) {
|
|
147
|
+
current.clear();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const path =
|
|
151
|
+
typeof args['path'] === 'string' ? args['path'] : undefined;
|
|
152
|
+
const line = toLine(args['line']);
|
|
153
|
+
const endLine = toLine(args['endLine']);
|
|
154
|
+
const media = readMedia(args['media']);
|
|
155
|
+
const step: IWalkthroughStep = {
|
|
156
|
+
title: typeof args['title'] === 'string' ? args['title'] : undefined,
|
|
157
|
+
body: typeof args['body'] === 'string' ? args['body'] : undefined,
|
|
158
|
+
media,
|
|
159
|
+
path,
|
|
160
|
+
line,
|
|
161
|
+
endLine
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
if (
|
|
165
|
+
!step.title &&
|
|
166
|
+
!step.body &&
|
|
167
|
+
!step.media &&
|
|
168
|
+
!step.path &&
|
|
169
|
+
args['reset'] !== true
|
|
170
|
+
) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
'xtralab:walkthrough needs at least one of: title, body, media, path'
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Reveal the panel, then render the step into it.
|
|
177
|
+
labShell.activateById(current.id);
|
|
178
|
+
await current.addStep(step);
|
|
179
|
+
|
|
180
|
+
// Let the editor follow the newest step.
|
|
181
|
+
if (path && args['reveal'] !== false) {
|
|
182
|
+
await commands.execute(HIGHLIGHT_LINES_COMMAND, {
|
|
183
|
+
path,
|
|
184
|
+
line,
|
|
185
|
+
endLine
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return trans.__('Added walkthrough step');
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (palette) {
|
|
194
|
+
palette.addItem({
|
|
195
|
+
command: WALKTHROUGH_COMMAND,
|
|
196
|
+
category: trans.__('Other')
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export default plugin;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
2
|
+
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
3
|
+
import { fileIcon } from '@jupyterlab/ui-components';
|
|
4
|
+
import type { CommandRegistry } from '@lumino/commands';
|
|
5
|
+
import type { ReadonlyPartialJSONValue } from '@lumino/coreutils';
|
|
6
|
+
import type { Message } from '@lumino/messaging';
|
|
7
|
+
import { Panel, Widget } from '@lumino/widgets';
|
|
8
|
+
|
|
9
|
+
/** Command the jump links dispatch; keeps the panel decoupled from the plugin. */
|
|
10
|
+
const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
|
|
11
|
+
|
|
12
|
+
/** A rich visual embedded in a step (rendered through the rendermime registry). */
|
|
13
|
+
export interface IWalkthroughMedia {
|
|
14
|
+
mimeType: string;
|
|
15
|
+
data: ReadonlyPartialJSONValue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** One step of a walkthrough. Every field is optional, but a step needs at least one. */
|
|
19
|
+
export interface IWalkthroughStep {
|
|
20
|
+
title?: string;
|
|
21
|
+
body?: string;
|
|
22
|
+
media?: IWalkthroughMedia;
|
|
23
|
+
path?: string;
|
|
24
|
+
line?: number;
|
|
25
|
+
endLine?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export namespace WalkthroughPanel {
|
|
29
|
+
export interface IOptions {
|
|
30
|
+
rendermime: IRenderMimeRegistry;
|
|
31
|
+
commands: CommandRegistry;
|
|
32
|
+
trans: TranslationBundle;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A read-only, scrollable column of walkthrough steps for the side area.
|
|
38
|
+
*
|
|
39
|
+
* The agent appends steps as it narrates, so the explanation lives beside the
|
|
40
|
+
* code the user is looking at and persists for them to read at their own pace,
|
|
41
|
+
* rather than scrolling away in the chat. Each step renders Markdown (with code
|
|
42
|
+
* snippets, Mermaid, math), can embed a rich visual, and can offer a button
|
|
43
|
+
* that opens the referenced file and highlights its lines.
|
|
44
|
+
*/
|
|
45
|
+
export class WalkthroughPanel extends Panel {
|
|
46
|
+
constructor(options: WalkthroughPanel.IOptions) {
|
|
47
|
+
super();
|
|
48
|
+
this._rendermime = options.rendermime;
|
|
49
|
+
this._commands = options.commands;
|
|
50
|
+
this._trans = options.trans;
|
|
51
|
+
this.addClass('jp-xtralab-Walkthrough');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Remove every step, leaving an empty panel. */
|
|
55
|
+
clear(): void {
|
|
56
|
+
while (this.widgets.length > 0) {
|
|
57
|
+
this.widgets[0].dispose();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Dispose on close (e.g. the close button when the panel has been dragged to
|
|
63
|
+
* the main area) rather than leaving a detached, reusable widget behind. The
|
|
64
|
+
* next `xtralab:walkthrough` call recreates the panel in the side area.
|
|
65
|
+
*/
|
|
66
|
+
protected onCloseRequest(msg: Message): void {
|
|
67
|
+
super.onCloseRequest(msg);
|
|
68
|
+
this.dispose();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Append a step and scroll it into view. */
|
|
72
|
+
async addStep(step: IWalkthroughStep): Promise<void> {
|
|
73
|
+
const card = new Panel();
|
|
74
|
+
card.addClass('jp-xtralab-Walkthrough-step');
|
|
75
|
+
this.addWidget(card);
|
|
76
|
+
|
|
77
|
+
if (step.title) {
|
|
78
|
+
const heading = new Widget({ node: document.createElement('h3') });
|
|
79
|
+
heading.addClass('jp-xtralab-Walkthrough-stepTitle');
|
|
80
|
+
heading.node.textContent = step.title;
|
|
81
|
+
card.addWidget(heading);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (step.body) {
|
|
85
|
+
await this._render(card, 'text/markdown', step.body);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (step.media && typeof step.media.mimeType === 'string') {
|
|
89
|
+
await this._render(card, step.media.mimeType, step.media.data);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (step.path) {
|
|
93
|
+
card.addWidget(this._jumpButton(step.path, step.line, step.endLine));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Bring the newest step into view.
|
|
97
|
+
this.node.scrollTop = this.node.scrollHeight;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private async _render(
|
|
101
|
+
parent: Panel,
|
|
102
|
+
mimeType: string,
|
|
103
|
+
data: ReadonlyPartialJSONValue
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
const bundle = { [mimeType]: data };
|
|
106
|
+
const chosen = this._rendermime.preferredMimeType(bundle, 'any');
|
|
107
|
+
if (!chosen) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const renderer = this._rendermime.createRenderer(chosen);
|
|
111
|
+
renderer.addClass('jp-xtralab-Walkthrough-content');
|
|
112
|
+
parent.addWidget(renderer);
|
|
113
|
+
await renderer.renderModel(
|
|
114
|
+
this._rendermime.createModel({ data: bundle, trusted: true })
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private _jumpButton(path: string, line?: number, endLine?: number): Widget {
|
|
119
|
+
const node = document.createElement('div');
|
|
120
|
+
node.className = 'jp-xtralab-Walkthrough-jumpRow';
|
|
121
|
+
const where = line ? `${path}:${line}` : path;
|
|
122
|
+
|
|
123
|
+
const button = document.createElement('button');
|
|
124
|
+
button.type = 'button';
|
|
125
|
+
button.className = 'jp-xtralab-Walkthrough-jump';
|
|
126
|
+
button.title = this._trans.__('Open %1 in the editor', where);
|
|
127
|
+
|
|
128
|
+
const icon = fileIcon.element({
|
|
129
|
+
tag: 'span',
|
|
130
|
+
className: 'jp-xtralab-Walkthrough-jumpIcon'
|
|
131
|
+
});
|
|
132
|
+
const label = document.createElement('span');
|
|
133
|
+
label.className = 'jp-xtralab-Walkthrough-jumpLabel';
|
|
134
|
+
label.textContent = where;
|
|
135
|
+
button.appendChild(icon);
|
|
136
|
+
button.appendChild(label);
|
|
137
|
+
node.appendChild(button);
|
|
138
|
+
|
|
139
|
+
button.addEventListener('click', () => {
|
|
140
|
+
void this._commands.execute(HIGHLIGHT_LINES_COMMAND, {
|
|
141
|
+
path,
|
|
142
|
+
line,
|
|
143
|
+
endLine
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
return new Widget({ node });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private _rendermime: IRenderMimeRegistry;
|
|
150
|
+
private _commands: CommandRegistry;
|
|
151
|
+
private _trans: TranslationBundle;
|
|
152
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Centered command bar in the top area (xtralab:command-bar). */
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The bar is an overlay centered over the whole top panel rather than a flex
|
|
5
|
+
* item, so its position does not shift with the (variable) width of the menu
|
|
6
|
+
* bar on its left or the sidebar toggle button on its right. `#jp-top-panel`
|
|
7
|
+
* is a `.lm-Widget`, which Lumino renders `position: relative`, so this
|
|
8
|
+
* absolute box is centered against the full width of the top bar.
|
|
9
|
+
*
|
|
10
|
+
* `pointer-events: none` here (with `auto` restored on the pill below) keeps
|
|
11
|
+
* only the pill clickable: the transparent strips above and below it let
|
|
12
|
+
* clicks fall through to whatever sits beneath the overlay.
|
|
13
|
+
*
|
|
14
|
+
* The `z-index` must clear JupyterLab core's settings-driven "Topbar toolbar"
|
|
15
|
+
* (`#jp-top-bar`, added to the top area at rank 900), which is a `.jp-Toolbar`
|
|
16
|
+
* at `z-index: 8` that grows to fill the center and paints an opaque
|
|
17
|
+
* `--jp-toolbar-background`. Sitting one above it lets the centered pill show
|
|
18
|
+
* through; that toolbar is empty by default, so the overlay covers blank
|
|
19
|
+
* space rather than hiding any toolbar items.
|
|
20
|
+
*/
|
|
21
|
+
.jp-xtralab-CommandBar {
|
|
22
|
+
position: absolute;
|
|
23
|
+
top: 0;
|
|
24
|
+
bottom: 0;
|
|
25
|
+
left: 50%;
|
|
26
|
+
z-index: 9;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
transform: translateX(-50%);
|
|
30
|
+
pointer-events: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.jp-xtralab-CommandBar-button {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex: 0 0 auto;
|
|
36
|
+
gap: 6px;
|
|
37
|
+
align-items: center;
|
|
38
|
+
width: clamp(160px, 16vw, 220px);
|
|
39
|
+
height: 20px;
|
|
40
|
+
padding: 0 8px;
|
|
41
|
+
margin: 0;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
font-family: var(--jp-ui-font-family);
|
|
44
|
+
font-size: var(--jp-ui-font-size1);
|
|
45
|
+
color: var(--jp-ui-font-color2);
|
|
46
|
+
text-align: left;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
|
|
49
|
+
/* The theme's input-field fill, so the pill reads as a search box and tracks
|
|
50
|
+
each theme's input styling across light and dark (a flat layout color is
|
|
51
|
+
near-black in some dark themes). */
|
|
52
|
+
background: var(--jp-input-background);
|
|
53
|
+
border: var(--jp-border-width) solid var(--jp-border-color1);
|
|
54
|
+
border-radius: 10px;
|
|
55
|
+
appearance: none;
|
|
56
|
+
pointer-events: auto;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.jp-xtralab-CommandBar-button:hover {
|
|
60
|
+
color: var(--jp-ui-font-color1);
|
|
61
|
+
border-color: var(--jp-border-color0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.jp-xtralab-CommandBar-button:focus-visible {
|
|
65
|
+
outline: var(--jp-border-width) solid var(--jp-brand-color1);
|
|
66
|
+
outline-offset: 1px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.jp-xtralab-CommandBar-icon {
|
|
70
|
+
display: flex;
|
|
71
|
+
flex: 0 0 auto;
|
|
72
|
+
align-items: center;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.jp-xtralab-CommandBar-icon svg {
|
|
76
|
+
width: 14px;
|
|
77
|
+
height: 14px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* The built-in JupyterLab search icon (@jupyterlab/ui-components) paints with a
|
|
81
|
+
fixed `jp-icon3` theme gray; override it to currentColor so the magnifier
|
|
82
|
+
tracks the pill's text color and brightens with the label on hover. */
|
|
83
|
+
.jp-xtralab-CommandBar-button .jp-icon3[fill] {
|
|
84
|
+
fill: currentcolor;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.jp-xtralab-CommandBar-label {
|
|
88
|
+
flex: 1 1 auto;
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
text-overflow: ellipsis;
|
|
91
|
+
white-space: nowrap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Keyboard-shortcut hint, pushed to the trailing edge by the growing label. */
|
|
95
|
+
.jp-xtralab-CommandBar-shortcut {
|
|
96
|
+
flex: 0 0 auto;
|
|
97
|
+
margin-left: 6px;
|
|
98
|
+
padding: 1px 4px;
|
|
99
|
+
font-size: var(--jp-ui-font-size0);
|
|
100
|
+
line-height: 1.2;
|
|
101
|
+
color: var(--jp-ui-font-color3);
|
|
102
|
+
border: var(--jp-border-width) solid var(--jp-border-color1);
|
|
103
|
+
border-radius: 4px;
|
|
104
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Line highlight applied by `xtralab:highlight-lines`. The class lands on the
|
|
3
|
+
* CodeMirror line wrapper (`.cm-line`), so the tint spans the full editor width
|
|
4
|
+
* and a left accent bar marks the run. The translucent background keeps the
|
|
5
|
+
* code legible on both light and dark themes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
.jp-xtralab-highlightLine {
|
|
9
|
+
background-color: rgb(250 204 21 / 18%);
|
|
10
|
+
box-shadow: inset 2px 0 0 0 var(--jp-brand-color1);
|
|
11
|
+
}
|
package/style/index.css
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
@import url('base.css');
|
|
2
|
+
@import url('commandBar.css');
|
|
2
3
|
@import url('editorBreadcrumbs.css');
|
|
3
4
|
@import url('git.css');
|
|
5
|
+
@import url('highlight.css');
|
|
4
6
|
@import url('launcher.css');
|
|
7
|
+
@import url('omnibox.css');
|
|
8
|
+
@import url('showOutput.css');
|
|
5
9
|
@import url('sidebar.css');
|
|
6
10
|
@import url('tabs.css');
|
|
7
11
|
@import url('terminals.css');
|
|
8
12
|
@import url('topBar.css');
|
|
13
|
+
@import url('walkthrough.css');
|
package/style/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import './base.css';
|
|
2
|
+
import './commandBar.css';
|
|
2
3
|
import './git.css';
|
|
4
|
+
import './highlight.css';
|
|
3
5
|
import './launcher.css';
|
|
6
|
+
import './omnibox.css';
|
|
7
|
+
import './showOutput.css';
|
|
4
8
|
import './sidebar.css';
|
|
5
9
|
import './tabs.css';
|
|
6
10
|
import './terminals.css';
|
|
7
11
|
import './topBar.css';
|
|
12
|
+
import './walkthrough.css';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* Omnibox overlay (xtralab:omnibox): a centered search box over the shell. */
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The widget node is attached to `document.body` (outside the shell) and only
|
|
5
|
+
* carries theme variables via `jp-ThemedContainer`; the React tree renders the
|
|
6
|
+
* fixed full-screen overlay below it.
|
|
7
|
+
*/
|
|
8
|
+
.jp-xtralab-Omnibox {
|
|
9
|
+
position: absolute;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Transparent full-viewport layer that centers the panel and catches
|
|
13
|
+
click-outside to dismiss. */
|
|
14
|
+
.jp-xtralab-Omnibox-overlay {
|
|
15
|
+
position: fixed;
|
|
16
|
+
inset: 0;
|
|
17
|
+
z-index: 10001;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: flex-start;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.jp-xtralab-Omnibox-panel {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
width: min(640px, 92vw);
|
|
27
|
+
max-height: 60vh;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
|
|
30
|
+
/* Drop just below the top bar so it reads as a command-center dropdown. */
|
|
31
|
+
margin-top: calc(var(--jp-private-menubar-height, 28px) + 4px);
|
|
32
|
+
background: var(--jp-layout-color1);
|
|
33
|
+
border: var(--jp-border-width) solid var(--jp-border-color1);
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
box-shadow: var(--jp-elevation-z6);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.jp-xtralab-Omnibox-input {
|
|
39
|
+
padding: 11px 14px;
|
|
40
|
+
font-family: var(--jp-ui-font-family);
|
|
41
|
+
font-size: var(--jp-ui-font-size2);
|
|
42
|
+
color: var(--jp-ui-font-color0);
|
|
43
|
+
background: transparent;
|
|
44
|
+
border: none;
|
|
45
|
+
border-bottom: var(--jp-border-width) solid var(--jp-border-color2);
|
|
46
|
+
outline: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.jp-xtralab-Omnibox-input::placeholder {
|
|
50
|
+
color: var(--jp-ui-font-color3);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.jp-xtralab-Omnibox-results {
|
|
54
|
+
padding: 4px 0;
|
|
55
|
+
overflow-y: auto;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.jp-xtralab-Omnibox-sectionHeader {
|
|
59
|
+
padding: 6px 14px 2px;
|
|
60
|
+
font-size: var(--jp-ui-font-size0);
|
|
61
|
+
font-weight: 600;
|
|
62
|
+
color: var(--jp-ui-font-color2);
|
|
63
|
+
text-transform: uppercase;
|
|
64
|
+
letter-spacing: 0.04em;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.jp-xtralab-Omnibox-item {
|
|
68
|
+
display: flex;
|
|
69
|
+
gap: 8px;
|
|
70
|
+
align-items: center;
|
|
71
|
+
padding: 5px 14px;
|
|
72
|
+
font-size: var(--jp-ui-font-size1);
|
|
73
|
+
color: var(--jp-ui-font-color1);
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.jp-xtralab-Omnibox-item.jp-mod-active {
|
|
78
|
+
background: var(--jp-layout-color2);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.jp-xtralab-Omnibox-itemIcon {
|
|
82
|
+
display: flex;
|
|
83
|
+
flex: 0 0 auto;
|
|
84
|
+
align-items: center;
|
|
85
|
+
width: 16px;
|
|
86
|
+
height: 16px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.jp-xtralab-Omnibox-itemIcon svg {
|
|
90
|
+
width: 16px;
|
|
91
|
+
height: 16px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.jp-xtralab-Omnibox-itemLabel {
|
|
95
|
+
flex: 1 1 auto;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
white-space: nowrap;
|
|
98
|
+
text-overflow: ellipsis;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.jp-xtralab-Omnibox-match {
|
|
102
|
+
font-weight: 600;
|
|
103
|
+
color: var(--jp-ui-font-color0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.jp-xtralab-Omnibox-itemCaption {
|
|
107
|
+
flex: 0 1 auto;
|
|
108
|
+
max-width: 45%;
|
|
109
|
+
overflow: hidden;
|
|
110
|
+
font-size: var(--jp-ui-font-size0);
|
|
111
|
+
color: var(--jp-ui-font-color3);
|
|
112
|
+
white-space: nowrap;
|
|
113
|
+
text-overflow: ellipsis;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.jp-xtralab-Omnibox-empty {
|
|
117
|
+
padding: 14px;
|
|
118
|
+
font-size: var(--jp-ui-font-size1);
|
|
119
|
+
color: var(--jp-ui-font-color2);
|
|
120
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The panel `xtralab:show` renders into. It holds a single rendered output and
|
|
3
|
+
* scrolls when that output is taller (or wider) than the panel, which matters
|
|
4
|
+
* most in the narrow side area.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.jp-xtralab-ShowPanel {
|
|
8
|
+
overflow: auto;
|
|
9
|
+
padding: 8px;
|
|
10
|
+
background: var(--jp-layout-color1);
|
|
11
|
+
}
|