xtralab 0.11.0 → 0.12.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 (162) hide show
  1. package/README.md +37 -229
  2. package/lib/agentSessions.d.ts +6 -2
  3. package/lib/askAgent/editorSelection.d.ts +53 -0
  4. package/lib/askAgent/editorSelection.js +95 -0
  5. package/lib/askAgent/icons.d.ts +7 -0
  6. package/lib/askAgent/icons.js +15 -0
  7. package/lib/askAgent/index.d.ts +32 -0
  8. package/lib/askAgent/index.js +741 -0
  9. package/lib/askAgent/popup.d.ts +83 -0
  10. package/lib/askAgent/popup.js +221 -0
  11. package/lib/askAgent/prompt.d.ts +24 -0
  12. package/lib/askAgent/prompt.js +116 -0
  13. package/lib/askAgent/queue.d.ts +93 -0
  14. package/lib/askAgent/queue.js +207 -0
  15. package/lib/askAgent/queuePanel.d.ts +76 -0
  16. package/lib/askAgent/queuePanel.js +188 -0
  17. package/lib/askAgent/targetPicker.d.ts +58 -0
  18. package/lib/askAgent/targetPicker.js +74 -0
  19. package/lib/askAgent/tokens.d.ts +124 -0
  20. package/lib/askAgent/tokens.js +13 -0
  21. package/lib/commandBar/index.d.ts +12 -8
  22. package/lib/commandBar/index.js +58 -41
  23. package/lib/editorBreadcrumbs/index.js +9 -4
  24. package/lib/editorBreadcrumbs/widget.d.ts +7 -7
  25. package/lib/editorBreadcrumbs/widget.js +6 -8
  26. package/lib/fileBrowser/commands.d.ts +2 -8
  27. package/lib/fileBrowser/commands.js +39 -30
  28. package/lib/fileBrowser/contents.d.ts +8 -3
  29. package/lib/fileBrowser/fileBrowser.d.ts +2 -1
  30. package/lib/fileBrowser/widget.d.ts +5 -9
  31. package/lib/fileBrowser/widget.js +1 -1
  32. package/lib/fileTypeIcons/index.js +5 -2
  33. package/lib/git/api.d.ts +0 -26
  34. package/lib/git/api.js +2 -44
  35. package/lib/git/askRequest.d.ts +23 -0
  36. package/lib/git/askRequest.js +90 -0
  37. package/lib/git/commands.d.ts +14 -2
  38. package/lib/git/commands.js +16 -13
  39. package/lib/git/diffModel.d.ts +1 -9
  40. package/lib/git/diffModel.js +1 -1
  41. package/lib/git/diffProvider.js +8 -3
  42. package/lib/git/diffSurface.d.ts +19 -2
  43. package/lib/git/diffSurface.js +64 -18
  44. package/lib/git/diffTheme.d.ts +0 -4
  45. package/lib/git/diffTheme.js +1 -1
  46. package/lib/git/diffWidget.d.ts +9 -0
  47. package/lib/git/diffWidget.js +38 -10
  48. package/lib/git/imageDiff.d.ts +17 -4
  49. package/lib/git/imageDiff.js +90 -30
  50. package/lib/git/index.js +7 -2
  51. package/lib/git/notebookDiff.d.ts +10 -67
  52. package/lib/git/notebookDiff.js +62 -69
  53. package/lib/git/tokens.d.ts +5 -2
  54. package/lib/highlight/index.js +12 -8
  55. package/lib/index.js +2 -0
  56. package/lib/launcher/agents.d.ts +11 -1
  57. package/lib/launcher/agents.js +26 -8
  58. package/lib/launcher/commands.d.ts +6 -2
  59. package/lib/launcher/dashboard.d.ts +8 -1
  60. package/lib/launcher/dashboard.js +39 -33
  61. package/lib/launcher/editorRegistry.js +5 -3
  62. package/lib/launcher/editors.d.ts +24 -7
  63. package/lib/launcher/editors.js +14 -5
  64. package/lib/launcher/icons.d.ts +0 -37
  65. package/lib/launcher/icons.js +11 -12
  66. package/lib/launcher/index.js +5 -2
  67. package/lib/launcher/invocation.d.ts +0 -15
  68. package/lib/launcher/invocation.js +1 -1
  69. package/lib/launcher/schemaDefaults.d.ts +3 -1
  70. package/lib/launcher/schemaDefaults.js +3 -1
  71. package/lib/mimeData.d.ts +7 -0
  72. package/lib/mimeData.js +16 -0
  73. package/lib/omnibox/index.d.ts +4 -0
  74. package/lib/omnibox/index.js +43 -2
  75. package/lib/omnibox/model.d.ts +43 -14
  76. package/lib/omnibox/model.js +117 -14
  77. package/lib/omnibox/recents.d.ts +65 -0
  78. package/lib/omnibox/recents.js +109 -0
  79. package/lib/omnibox/tokens.d.ts +6 -2
  80. package/lib/omnibox/widget.d.ts +23 -6
  81. package/lib/omnibox/widget.js +26 -7
  82. package/lib/showOutput/index.js +15 -18
  83. package/lib/terminals/agentTerminals.d.ts +63 -0
  84. package/lib/terminals/agentTerminals.js +157 -0
  85. package/lib/terminals/index.d.ts +7 -1
  86. package/lib/terminals/index.js +50 -30
  87. package/lib/terminals/model.d.ts +11 -2
  88. package/lib/terminals/model.js +27 -8
  89. package/lib/terminals/tokens.d.ts +66 -0
  90. package/lib/terminals/tokens.js +7 -0
  91. package/lib/terminals/widget.d.ts +3 -6
  92. package/lib/terminals/widget.js +1 -1
  93. package/lib/topBar/index.js +3 -3
  94. package/lib/walkthrough/index.js +14 -3
  95. package/lib/walkthrough/panel.d.ts +12 -4
  96. package/lib/walkthrough/panel.js +9 -3
  97. package/package.json +49 -51
  98. package/schema/command-bar.json +10 -0
  99. package/schema/launcher.json +4 -4
  100. package/schema/omnibox.json +15 -0
  101. package/src/agentSessions.ts +6 -2
  102. package/src/askAgent/editorSelection.ts +138 -0
  103. package/src/askAgent/icons.ts +16 -0
  104. package/src/askAgent/index.ts +915 -0
  105. package/src/askAgent/popup.tsx +407 -0
  106. package/src/askAgent/prompt.ts +144 -0
  107. package/src/askAgent/queue.ts +269 -0
  108. package/src/askAgent/queuePanel.tsx +418 -0
  109. package/src/askAgent/targetPicker.tsx +209 -0
  110. package/src/askAgent/tokens.ts +139 -0
  111. package/src/commandBar/index.ts +75 -49
  112. package/src/editorBreadcrumbs/index.ts +14 -4
  113. package/src/editorBreadcrumbs/widget.ts +11 -9
  114. package/src/fileBrowser/commands.ts +45 -31
  115. package/src/fileBrowser/contents.ts +7 -3
  116. package/src/fileBrowser/fileBrowser.tsx +1 -1
  117. package/src/fileBrowser/widget.tsx +5 -3
  118. package/src/fileTypeIcons/index.ts +6 -3
  119. package/src/git/api.ts +2 -61
  120. package/src/git/askRequest.ts +113 -0
  121. package/src/git/commands.ts +41 -14
  122. package/src/git/diffModel.ts +1 -1
  123. package/src/git/diffProvider.tsx +11 -3
  124. package/src/git/diffSurface.tsx +114 -22
  125. package/src/git/diffTheme.ts +1 -1
  126. package/src/git/diffWidget.tsx +54 -7
  127. package/src/git/imageDiff.tsx +169 -58
  128. package/src/git/index.ts +10 -2
  129. package/src/git/notebookDiff.tsx +91 -57
  130. package/src/git/tokens.ts +4 -2
  131. package/src/highlight/index.ts +12 -8
  132. package/src/index.ts +2 -0
  133. package/src/launcher/agents.ts +31 -7
  134. package/src/launcher/commands.ts +6 -2
  135. package/src/launcher/dashboard.tsx +86 -38
  136. package/src/launcher/editorRegistry.ts +10 -4
  137. package/src/launcher/editors.ts +34 -9
  138. package/src/launcher/icons.ts +11 -12
  139. package/src/launcher/index.ts +5 -2
  140. package/src/launcher/invocation.ts +1 -1
  141. package/src/launcher/schemaDefaults.ts +3 -1
  142. package/src/mimeData.ts +20 -0
  143. package/src/omnibox/index.ts +48 -1
  144. package/src/omnibox/model.ts +173 -33
  145. package/src/omnibox/recents.ts +155 -0
  146. package/src/omnibox/tokens.ts +6 -2
  147. package/src/omnibox/widget.tsx +54 -13
  148. package/src/showOutput/index.ts +20 -23
  149. package/src/terminals/agentTerminals.ts +238 -0
  150. package/src/terminals/index.ts +54 -30
  151. package/src/terminals/model.ts +28 -7
  152. package/src/terminals/tokens.ts +77 -0
  153. package/src/terminals/widget.tsx +4 -2
  154. package/src/topBar/index.ts +12 -6
  155. package/src/walkthrough/index.ts +19 -4
  156. package/src/walkthrough/panel.ts +15 -5
  157. package/style/askAgent.css +544 -0
  158. package/style/commandBar.css +12 -33
  159. package/style/git.css +72 -31
  160. package/style/index.css +1 -0
  161. package/style/index.js +2 -0
  162. package/style/topBar.css +4 -4
package/README.md CHANGED
@@ -1,261 +1,69 @@
1
1
  ![xtralab-logo](./logo.png)
2
2
 
3
- An opinionated JupyterLab meta-package for use with coding agents.
3
+ An opinionated JupyterLab meta-package for coding agents.
4
4
 
5
- It bundles a curated set of JupyterLab extensions, a path-first file browser,
6
- rich git diffs for text, notebooks, and images, an agent launcher, and a
7
- quieter default workspace.
5
+ xtralab reshapes JupyterLab around terminal coding agents. It bundles a curated
6
+ set of extensions, a path-first file browser, rich git diffs, an agent launcher,
7
+ a Model Context Protocol server that lets agents drive the app, and a quieter
8
+ set of defaults. It builds on [`ajlab`](https://github.com/jtpio/ajlab), the
9
+ agent-ready JupyterLab base.
8
10
 
9
- ![xtralab screenshot](./screenshot.png)
11
+ ![The xtralab workspace: path-first file browser, agent launcher, a side-by-side git diff, and a running Claude Code session](./images/hero.webp)
12
+
13
+ The [documentation](https://jtpio.github.io/xtralab/) covers everything in
14
+ detail: [installation](https://jtpio.github.io/xtralab/installation/), a
15
+ [tour of the features](https://jtpio.github.io/xtralab/getting-started/),
16
+ [connecting agents over MCP](https://jtpio.github.io/xtralab/agents/mcp/),
17
+ [agent skills](https://jtpio.github.io/xtralab/agents/skills/), and the
18
+ [desktop app](https://jtpio.github.io/xtralab/desktop/).
10
19
 
11
20
  ## Install
12
21
 
13
22
  ```bash
14
23
  pip install xtralab
15
- ```
16
-
17
- ## Usage
18
-
19
- ### As a JupyterLab package
20
-
21
- Once installed, start JupyterLab the usual way:
22
-
23
- ```bash
24
24
  jupyter lab
25
25
  ```
26
26
 
27
- ### As a desktop app
28
-
29
- A standalone Electron build is also available, packaged as a DMG on macOS and
30
- an AppImage on Linux. Each tagged release ships installers on the
31
- [releases page](https://github.com/jtpio/xtralab/releases/latest), and builds
32
- from `main` are uploaded as workflow artifacts on the
33
- [Actions tab](https://github.com/jtpio/xtralab/actions). See
34
- [CONTRIBUTING.md](./CONTRIBUTING.md) for the desktop app architecture and local
35
- build instructions.
36
-
37
- On macOS the build is not notarized yet (it has no Apple Developer ID), so
38
- Gatekeeper blocks it on first launch, and agent notifications fall back to
39
- `osascript`, appearing as "Script Editor". In the meantime you can self-sign it
40
- for free: this lets it launch and gives native notifications instead, branded as
41
- xtralab and clickable to jump to the terminal that fired them. Create a
42
- code-signing certificate once in Keychain Access (Certificate Assistant, Create
43
- a Certificate, with Identity Type "Self Signed Root" and Certificate Type "Code
44
- Signing") named `xtralab-selfsign`, then sign the app you copied out of the DMG:
45
-
46
- ```bash
47
- codesign --force --deep --sign "xtralab-selfsign" --timestamp=none "/Applications/xtralab.app"
48
- xattr -dr com.apple.quarantine "/Applications/xtralab.app"
49
- ```
50
-
51
- macOS prompts once to let `codesign` use the key; choose Always Allow. None of
52
- this will be needed once the app ships with a Developer ID signature.
53
-
54
- ## What's included
27
+ A standalone desktop app (DMG on macOS, AppImage on Linux) ships with every
28
+ tagged release on the
29
+ [releases page](https://github.com/jtpio/xtralab/releases/latest); see the
30
+ [desktop app docs](https://jtpio.github.io/xtralab/desktop/) for the
31
+ Gatekeeper notes and how it manages projects and kernels.
55
32
 
56
- xtralab builds on [`ajlab`](https://github.com/jtpio/ajlab), the agent-ready
57
- JupyterLab base. On top of it, xtralab pulls in JupyterLab 4.6+ with
58
- `jupyterlab-git`, `jupyterlab-lsp`, `jupyterlab-quickopen`,
59
- `jupyterlab-search-replace`, `jupyterlab-vim`, and a set of light and dark
60
- themes (`jupyterlab-cursor-light`, `jupyterlab-cursor-dark`,
61
- `jupyterlab-day`, `jupyterlab-night`). See
62
- [`pyproject.toml`](./pyproject.toml) for the full list and pinned versions.
33
+ ## Highlights
63
34
 
64
- The bundled xtralab labextension then adds:
35
+ ![The xtralab launcher](./images/launcher.webp)
65
36
 
66
- - A path-first file browser in the left sidebar, with `@pierre/trees`
67
- file-type icons that also carry over to document tabs and the `jupyterlab-git`
68
- panel.
69
- - Rich git diffs in the `jupyterlab-git` panel, using `@pierre/diffs` for text
70
- and notebooks and a 2-up / swipe / onion-skin view for images.
71
- - An agent launcher with a prompt box, buttons for the agents installed on your
72
- machine (Claude, Codex, Antigravity, Copilot, Goose, OpenCode, Kiro, Mistral
73
- Vibe), a collapsible list of changed files, and an Open row for a terminal,
74
- notebook, console, or your terminal editor (Neovim or Vim).
75
- - A Terminals panel listing the running terminal sessions, each badged with the
76
- agent or editor detected inside it. Open terminal tabs in the main area carry
77
- the same icon.
78
- - Sidebar toggle buttons in the top bar for the left and right areas.
37
+ The launcher starts any agent installed on your machine, with an optional
38
+ prompt and a **Changes** list that jumps straight into a side-by-side git diff
39
+ you can edit in place. Select code in an editor, a notebook, or a diff to send
40
+ a prompt about it to a new or already-running agent, or queue prompts while
41
+ you read. One omnibox searches files and commands, the Terminals panel shows
42
+ which agent runs where, and quieter defaults keep the workspace calm. The
43
+ [documentation](https://jtpio.github.io/xtralab/) walks through each of these.
79
44
 
80
- xtralab also ships a set of opinionated defaults: unused UI is hidden for a
81
- quieter workspace, the activity bar sits at the top, and autocompletion,
82
- continuous LSP hinting, code folding, and gitignore-aware quick open are on by
83
- default. See [the bundled labconfig overrides](./jupyter-config/labconfig) for
84
- the full set.
85
-
86
- ## Connecting agents to Jupyter (MCP)
45
+ ## Connecting agents to JupyterLab (MCP)
87
46
 
88
47
  xtralab runs a [Model Context Protocol][mcp-spec] server inside JupyterLab,
89
- provided by [`jupyter-server-mcp`][mcp]. To let a coding agent drive JupyterLab
90
- through it, register the bundled `jupyter-server-mcp-proxy` console script with
91
- the agent:
48
+ provided by [`jupyter-server-mcp`][mcp], so an agent can open files, run
49
+ cells, and read notebooks. Register the bundled proxy from a terminal inside
50
+ xtralab:
92
51
 
93
52
  ```bash
94
53
  claude mcp add jupyter -- jupyter-server-mcp-proxy
95
- codex mcp add jupyter -- jupyter-server-mcp-proxy
96
- copilot mcp add jupyter -- jupyter-server-mcp-proxy
97
54
  ```
98
55
 
99
- Run this from a terminal inside xtralab, so the proxy inherits the server
100
- environment and discovers the running server automatically. No port is needed,
101
- and a single registration keeps working across restarts. The launcher also
102
- surfaces these commands with copy buttons, filtered to the agents you have
103
- installed.
104
-
105
- See the [`jupyter-server-mcp` README][mcp] for other MCP clients.
56
+ The launcher surfaces the matching command for every agent you have
57
+ installed, and the [MCP docs page](https://jtpio.github.io/xtralab/agents/mcp/)
58
+ covers the details.
106
59
 
107
60
  [mcp]: https://github.com/jupyter-ai-contrib/jupyter-server-mcp
108
61
  [mcp-spec]: https://modelcontextprotocol.io
109
62
 
110
- ## Agent skills
111
-
112
- xtralab ships [Agent Skills][skills] at [`agent-skill/`](./agent-skill) that
113
- teach any coding agent how to work with the app:
114
-
115
- - **customize-jupyterlab** turns plain-English requests like "hide the status
116
- bar", "change the theme", or "add a language server" into the right
117
- config-file edits.
118
- - **guided-code-walkthrough** drives the running app over the MCP command
119
- bridge (see the section above) to open files, jump to and highlight specific
120
- lines (`xtralab:highlight-lines`), and build a read-only Walkthrough panel of
121
- prose, snippets, diagrams, and clickable code links beside the editor
122
- (`xtralab:walkthrough`, no notebook or kernel needed), so the agent can _show_
123
- you something at your own pace instead of only describing it in chat.
124
-
125
- The same SKILL.md files work with Claude Code, Codex CLI, Gemini CLI, GitHub
126
- Copilot, and other tools that read the Agent Skills format.
127
-
128
- For Claude Code, this repository doubles as a one-plugin marketplace. Inside a
129
- Claude Code session:
130
-
131
- ```text
132
- /plugin marketplace add jtpio/xtralab
133
- /plugin install xtralab-skills@xtralab
134
- ```
135
-
136
- For agents that read from `~/.agents/skills/`, such as Codex CLI or Gemini CLI,
137
- clone and copy the skill directories:
138
-
139
- ```bash
140
- git clone --depth=1 https://github.com/jtpio/xtralab.git /tmp/xtralab
141
- mkdir -p ~/.agents/skills
142
- cp -r /tmp/xtralab/agent-skill/skills/* ~/.agents/skills/
143
- ```
144
-
145
- See [`agent-skill/README.md`](./agent-skill/README.md) for additional install
146
- paths, the list of supported agents, and what the skill knows.
147
-
148
- [skills]: https://agentskills.io
149
-
150
- ## Language servers
151
-
152
- xtralab ships [`jupyterlab-lsp`][lsp] with two language servers pre-registered:
153
-
154
- - Python, using [`ty`](https://github.com/astral-sh/ty), which is bundled and
155
- works out of the box.
156
- - TypeScript and JavaScript, using `typescript-language-server`, which you
157
- install yourself:
158
-
159
- ```bash
160
- npm install -g typescript-language-server typescript
161
- ```
162
-
163
- Restart JupyterLab afterwards to pick it up.
164
-
165
- To enable another server (bash, yaml, json, pyright, and more), install its
166
- binary and drop a JSON spec into a `jupyter_server_config.d/` directory. For
167
- `pip install xtralab`, run `jupyter --paths` to find one (typically
168
- `~/.jupyter/jupyter_server_config.d/`). For the desktop app, use
169
- `~/Library/Application Support/xtralab/jupyter/config/jupyter_server_config.d/`
170
- on macOS or `~/.config/xtralab/jupyter/config/jupyter_server_config.d/` on
171
- Linux.
172
-
173
- ```json
174
- {
175
- "LanguageServerManager": {
176
- "language_servers": {
177
- "bash-language-server": {
178
- "version": 2,
179
- "argv": ["bash-language-server", "start"],
180
- "languages": ["bash", "sh"],
181
- "mime_types": ["text/x-sh", "application/x-sh"],
182
- "display_name": "bash-language-server"
183
- }
184
- }
185
- }
186
- }
187
- ```
188
-
189
- See the [`jupyterlab-lsp` documentation][lsp-config] for the full spec.
190
-
191
- [lsp]: https://github.com/jupyter-lsp/jupyterlab-lsp
192
- [lsp-config]: https://jupyterlab-lsp.readthedocs.io/en/latest/Configuring.html
193
-
194
- ## Customizing the launcher
195
-
196
- Open `Settings → Settings Editor → xtralab launcher` to override, hide, or add
197
- launcher entries. The editor shows xtralab's full built-in list as the default
198
- for both the agents and editors settings, so you can read every shipped entry
199
- and copy one into your user preferences to tweak in place. Both lists merge with
200
- xtralab's defaults by `id`, so an override only needs the `id` plus the fields
201
- you want to change.
202
-
203
- ### Agents
204
-
205
- Edit the `agents` array:
206
-
207
- ```jsonc
208
- {
209
- "agents": [
210
- // Hide an agent
211
- { "id": "kiro", "enabled": false },
212
-
213
- // Override an agent's command (e.g. point Claude at a shell alias)
214
- { "id": "claude", "command": "cl", "requireAvailable": false },
215
-
216
- // Add a new agent; promptArgs: [] appends the prompt as a positional arg
217
- { "id": "aider", "label": "Aider", "command": "aider", "promptArgs": [] }
218
- ]
219
- }
220
- ```
221
-
222
- Fields: `id` (required), `label`, `caption`, `command`, `promptArgs` (how the
223
- prompt is spliced: `[]` for positional, `["--flag"]` for flagged, `null` to opt
224
- out), `iconSvg`, `rank`, `enabled`, `requireAvailable`.
225
-
226
- ### Editors
227
-
228
- The **Open** section's terminal-editor tile (Neovim, falling back to Vim) is
229
- configured the same way through an `editors` array:
230
-
231
- ```jsonc
232
- {
233
- "editors": [
234
- // Hide Neovim so the tile falls back to Vim (or disappears if Vim
235
- // isn't installed either)
236
- { "id": "nvim", "enabled": false },
237
-
238
- // Add Helix, preferred over the built-ins
239
- {
240
- "id": "helix",
241
- "label": "Helix",
242
- "command": "hx",
243
- "rank": -1,
244
- "iconSvg": "<svg>…</svg>"
245
- }
246
- ]
247
- }
248
- ```
249
-
250
- The launcher shows a single tile: the first editor, by `rank`, whose `command`
251
- is on `PATH`. Disable both built-ins (`nvim`, `vim`) to remove the tile
252
- entirely. Fields: `id` (required), `label`, `caption`, `command`, `iconSvg`,
253
- `rank`, `enabled`, `requireAvailable`.
254
-
255
63
  ## Contributing
256
64
 
257
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for the development setup, the Electron
258
- desktop app architecture, and the build pipeline.
65
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the development setup, the
66
+ desktop app architecture, and the documentation sources in [`docs/`](./docs).
259
67
 
260
68
  ## License
261
69
 
@@ -26,9 +26,13 @@ export interface IAgentSessions {
26
26
  * this ran).
27
27
  */
28
28
  get(sessionName: string): string | null;
29
- /** Record that a session was launched as the given agent command. */
29
+ /**
30
+ * Record that a session was launched as the given agent command.
31
+ */
30
32
  set(sessionName: string, command: string): void;
31
- /** Forget a session — called when it shuts down so the map stays bounded. */
33
+ /**
34
+ * Forget a session — called when it shuts down so the map stays bounded.
35
+ */
32
36
  delete(sessionName: string): void;
33
37
  /**
34
38
  * Emitted with the affected session name whenever a record is added or
@@ -0,0 +1,53 @@
1
+ import type { JupyterFrontEnd } from '@jupyterlab/application';
2
+ import type { EditorView } from '@codemirror/view';
3
+ import type { Widget } from '@lumino/widgets';
4
+ import type { IAskAgentRequest } from './tokens';
5
+ /**
6
+ * A CodeMirror view a selection can be asked about, plus the context needed
7
+ * to describe it to an agent: the document path and, for notebooks, which
8
+ * cell the view belongs to.
9
+ */
10
+ interface IEditorTarget {
11
+ view: EditorView;
12
+ path: string;
13
+ cell?: {
14
+ index: number;
15
+ type: string;
16
+ };
17
+ }
18
+ /**
19
+ * Resolve the CodeMirror view a selection in `widget` would live in, or
20
+ * `null` when the widget holds no such editor. Two widget shapes are
21
+ * recognized:
22
+ *
23
+ * - a notebook panel, where the active cell carries the editor and
24
+ * `activeCellIndex` names its 0-based position in the nbformat `cells`
25
+ * array. A rendered markdown cell keeps its (hidden) editor, but a
26
+ * selection in the rendered HTML never passes {@link domSelectionInView},
27
+ * so no pill appears there; and
28
+ * - a document widget whose content is a `FileEditor`. The wrapper is not
29
+ * checked with `instanceof DocumentWidget`: `@jupyterlab/docregistry` is
30
+ * not a core singleton, so another copy of the class could be in play.
31
+ * `FileEditor` and `NotebookPanel` come from singleton packages.
32
+ */
33
+ export declare function resolveEditorTarget(widget: Widget | null): IEditorTarget | null;
34
+ /**
35
+ * Whether the current DOM selection lives inside `view`. Guards the
36
+ * selection-change listener against selections made elsewhere (a terminal,
37
+ * a sidebar, another notebook cell) while the widget owning `view` happens
38
+ * to be the shell's current widget.
39
+ */
40
+ export declare function domSelectionInView(view: EditorView, selection: Selection): boolean;
41
+ /**
42
+ * Build an ask-agent request from the current selection in the shell's
43
+ * current widget — a file editor or the active notebook cell — or return
44
+ * `null` when that widget holds no CodeMirror document editor.
45
+ *
46
+ * With `allowEmpty`, a collapsed selection falls back to the cursor's line
47
+ * (used by the command/shortcut path so it works without a mouse
48
+ * selection); otherwise an empty selection yields `null`.
49
+ */
50
+ export declare function editorAskRequest(app: JupyterFrontEnd, options?: {
51
+ allowEmpty?: boolean;
52
+ }): IAskAgentRequest | null;
53
+ export {};
@@ -0,0 +1,95 @@
1
+ import { CodeMirrorEditor } from '@jupyterlab/codemirror';
2
+ import { FileEditor } from '@jupyterlab/fileeditor';
3
+ import { NotebookPanel } from '@jupyterlab/notebook';
4
+ /**
5
+ * Resolve the CodeMirror view a selection in `widget` would live in, or
6
+ * `null` when the widget holds no such editor. Two widget shapes are
7
+ * recognized:
8
+ *
9
+ * - a notebook panel, where the active cell carries the editor and
10
+ * `activeCellIndex` names its 0-based position in the nbformat `cells`
11
+ * array. A rendered markdown cell keeps its (hidden) editor, but a
12
+ * selection in the rendered HTML never passes {@link domSelectionInView},
13
+ * so no pill appears there; and
14
+ * - a document widget whose content is a `FileEditor`. The wrapper is not
15
+ * checked with `instanceof DocumentWidget`: `@jupyterlab/docregistry` is
16
+ * not a core singleton, so another copy of the class could be in play.
17
+ * `FileEditor` and `NotebookPanel` come from singleton packages.
18
+ */
19
+ export function resolveEditorTarget(widget) {
20
+ if (widget instanceof NotebookPanel) {
21
+ const cell = widget.content.activeCell;
22
+ if (cell !== null && cell.editor instanceof CodeMirrorEditor) {
23
+ return {
24
+ view: cell.editor.editor,
25
+ path: widget.context.path,
26
+ cell: { index: widget.content.activeCellIndex, type: cell.model.type }
27
+ };
28
+ }
29
+ return null;
30
+ }
31
+ const content = widget === null || widget === void 0 ? void 0 : widget.content;
32
+ if (content instanceof FileEditor &&
33
+ content.editor instanceof CodeMirrorEditor) {
34
+ return { view: content.editor.editor, path: content.context.path };
35
+ }
36
+ return null;
37
+ }
38
+ /**
39
+ * Whether the current DOM selection lives inside `view`. Guards the
40
+ * selection-change listener against selections made elsewhere (a terminal,
41
+ * a sidebar, another notebook cell) while the widget owning `view` happens
42
+ * to be the shell's current widget.
43
+ */
44
+ export function domSelectionInView(view, selection) {
45
+ var _a;
46
+ const node = selection.anchorNode;
47
+ const element = node instanceof Element ? node : ((_a = node === null || node === void 0 ? void 0 : node.parentElement) !== null && _a !== void 0 ? _a : null);
48
+ return element !== null && view.dom.contains(element);
49
+ }
50
+ /**
51
+ * Build an ask-agent request from the current selection in the shell's
52
+ * current widget — a file editor or the active notebook cell — or return
53
+ * `null` when that widget holds no CodeMirror document editor.
54
+ *
55
+ * With `allowEmpty`, a collapsed selection falls back to the cursor's line
56
+ * (used by the command/shortcut path so it works without a mouse
57
+ * selection); otherwise an empty selection yields `null`.
58
+ */
59
+ export function editorAskRequest(app, options = {}) {
60
+ const target = resolveEditorTarget(app.shell.currentWidget);
61
+ if (target === null) {
62
+ return null;
63
+ }
64
+ const { view, path, cell } = target;
65
+ const { state } = view;
66
+ const main = state.selection.main;
67
+ if (main.empty && options.allowEmpty !== true) {
68
+ return null;
69
+ }
70
+ const startLine = state.doc.lineAt(main.from).number;
71
+ let endLine;
72
+ let text;
73
+ if (main.empty) {
74
+ endLine = startLine;
75
+ text = state.doc.lineAt(main.from).text;
76
+ }
77
+ else {
78
+ // A selection that ends exactly at the start of a line (the common
79
+ // "drag over whole lines" gesture) should not count that line.
80
+ const endPos = main.to === state.doc.lineAt(main.to).from ? main.to - 1 : main.to;
81
+ endLine = state.doc.lineAt(endPos).number;
82
+ text = state.sliceDoc(main.from, main.to);
83
+ }
84
+ // Anchor the popup at the selection head (where the cursor ended up).
85
+ // `coordsAtPos` returns `null` for positions scrolled out of view.
86
+ const coords = view.coordsAtPos(main.head);
87
+ const anchor = coords
88
+ ? new DOMRect(coords.left, coords.top, Math.max(1, coords.right - coords.left), Math.max(1, coords.bottom - coords.top))
89
+ : null;
90
+ const context = { path, startLine, endLine, text };
91
+ if (cell !== undefined) {
92
+ context.cell = cell;
93
+ }
94
+ return { context, anchor };
95
+ }
@@ -0,0 +1,7 @@
1
+ import { LabIcon } from '@jupyterlab/ui-components';
2
+ /**
3
+ * Four-point sparkle used by the ask-agent affordances (selection pill,
4
+ * command palette entry). Drawn in-repo; the `jp-icon3` class lets the
5
+ * active theme recolor the glyph like other JupyterLab UI icons.
6
+ */
7
+ export declare const askAgentIcon: LabIcon;
@@ -0,0 +1,15 @@
1
+ import { LabIcon } from '@jupyterlab/ui-components';
2
+ /**
3
+ * Four-point sparkle used by the ask-agent affordances (selection pill,
4
+ * command palette entry). Drawn in-repo; the `jp-icon3` class lets the
5
+ * active theme recolor the glyph like other JupyterLab UI icons.
6
+ */
7
+ export const askAgentIcon = new LabIcon({
8
+ name: 'xtralab:ask-agent',
9
+ svgstr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
10
+ <g class="jp-icon3" fill="#616161">
11
+ <path d="M6 1l1 4 4 1-4 1-1 4-1-4-4-1 4-1 1-4z"/>
12
+ <path d="M12 9l.7 2.3L15 12l-2.3.7L12 15l-.7-2.3L9 12l2.3-.7L12 9z"/>
13
+ </g>
14
+ </svg>`
15
+ });
@@ -0,0 +1,32 @@
1
+ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ import { IAskAgent } from './tokens';
3
+ /**
4
+ * Select code in a file editor (or pick diff lines) and prompt a coding
5
+ * agent about it.
6
+ *
7
+ * The plugin watches the document selection: a non-empty selection inside a
8
+ * CodeMirror file editor or notebook cell grows a small floating "Ask agent"
9
+ * pill next to the selection. Clicking it (or running `xtralab:ask-agent`,
10
+ * bound to Accel+. in editors and notebooks) opens a popup where the user
11
+ * types an instruction, picks one of the launcher's agents and picks where
12
+ * the prompt goes; submitting either starts that agent in a fresh terminal
13
+ * via `xtralab:start-agent:<id>`, or pastes the prompt into an agent already
14
+ * running in one of the open terminal sessions (via `IAgentTerminals`; the
15
+ * agent CLIs queue prompts that arrive while they are busy). Sends to a
16
+ * running agent stay in the background — focus never leaves the editor, and
17
+ * a success toast offers to open the terminal. Either way the prompt embeds
18
+ * the file path, cell index for notebooks, line range and selected snippet.
19
+ *
20
+ * The popup's Queue button (or Accel+Enter) defers the send instead: the
21
+ * comment lands in a persistent queue, stamped with the popup's selected
22
+ * destination — every queued prompt is independent and may aim at a
23
+ * different agent or session. A right-sidebar panel reviews the queue
24
+ * (edit, retarget, remove) and flushes it in one go, combining prompts that
25
+ * share a destination into a single numbered message. The queue survives
26
+ * page reloads.
27
+ *
28
+ * The same popup is provided on the `IAskAgent` token so the git diff
29
+ * viewers can open it for a selected diff line range.
30
+ */
31
+ declare const plugin: JupyterFrontEndPlugin<IAskAgent>;
32
+ export default plugin;