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.
Files changed (71) hide show
  1. package/README.md +23 -9
  2. package/lib/commandBar/index.d.ts +14 -0
  3. package/lib/commandBar/index.js +114 -0
  4. package/lib/fileBrowser/fileBrowser.js +6 -1
  5. package/lib/highlight/index.d.ts +15 -0
  6. package/lib/highlight/index.js +214 -0
  7. package/lib/index.js +13 -1
  8. package/lib/launcher/agents.d.ts +7 -0
  9. package/lib/launcher/agents.js +24 -0
  10. package/lib/launcher/editorRegistry.js +5 -3
  11. package/lib/launcher/editors.d.ts +8 -0
  12. package/lib/launcher/editors.js +17 -0
  13. package/lib/launcher/index.js +4 -0
  14. package/lib/launcher/schemaDefaults.d.ts +15 -0
  15. package/lib/launcher/schemaDefaults.js +44 -0
  16. package/lib/omnibox/files.d.ts +7 -0
  17. package/lib/omnibox/files.js +50 -0
  18. package/lib/omnibox/index.d.ts +14 -0
  19. package/lib/omnibox/index.js +83 -0
  20. package/lib/omnibox/model.d.ts +45 -0
  21. package/lib/omnibox/model.js +142 -0
  22. package/lib/omnibox/tokens.d.ts +23 -0
  23. package/lib/omnibox/tokens.js +11 -0
  24. package/lib/omnibox/widget.d.ts +36 -0
  25. package/lib/omnibox/widget.js +161 -0
  26. package/lib/searchReplace/index.d.ts +25 -0
  27. package/lib/searchReplace/index.js +68 -0
  28. package/lib/showOutput/index.d.ts +17 -0
  29. package/lib/showOutput/index.js +154 -0
  30. package/lib/terminals/index.d.ts +4 -0
  31. package/lib/terminals/index.js +65 -16
  32. package/lib/terminals/model.d.ts +56 -3
  33. package/lib/terminals/model.js +261 -5
  34. package/lib/terminals/widget.d.ts +3 -1
  35. package/lib/terminals/widget.js +11 -3
  36. package/lib/walkthrough/index.d.ts +14 -0
  37. package/lib/walkthrough/index.js +162 -0
  38. package/lib/walkthrough/panel.d.ts +54 -0
  39. package/lib/walkthrough/panel.js +98 -0
  40. package/package.json +1 -1
  41. package/schema/launcher.json +3 -2
  42. package/schema/terminals.json +15 -0
  43. package/src/commandBar/index.ts +165 -0
  44. package/src/fileBrowser/fileBrowser.tsx +8 -1
  45. package/src/highlight/index.ts +251 -0
  46. package/src/index.ts +13 -1
  47. package/src/launcher/agents.ts +25 -0
  48. package/src/launcher/editorRegistry.ts +8 -4
  49. package/src/launcher/editors.ts +18 -0
  50. package/src/launcher/index.ts +4 -0
  51. package/src/launcher/schemaDefaults.ts +53 -0
  52. package/src/omnibox/files.ts +68 -0
  53. package/src/omnibox/index.ts +105 -0
  54. package/src/omnibox/model.ts +210 -0
  55. package/src/omnibox/tokens.ts +29 -0
  56. package/src/omnibox/widget.tsx +322 -0
  57. package/src/searchReplace/index.ts +86 -0
  58. package/src/showOutput/index.ts +199 -0
  59. package/src/terminals/index.ts +79 -16
  60. package/src/terminals/model.ts +304 -3
  61. package/src/terminals/widget.tsx +21 -4
  62. package/src/walkthrough/index.ts +202 -0
  63. package/src/walkthrough/panel.ts +152 -0
  64. package/style/commandBar.css +104 -0
  65. package/style/highlight.css +11 -0
  66. package/style/index.css +5 -0
  67. package/style/index.js +5 -0
  68. package/style/omnibox.css +120 -0
  69. package/style/showOutput.css +11 -0
  70. package/style/terminals.css +54 -10
  71. 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
+ }