xtralab 0.11.1 → 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 (159) hide show
  1. package/README.md +37 -231
  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.js +3 -4
  57. package/lib/launcher/commands.d.ts +6 -2
  58. package/lib/launcher/dashboard.d.ts +8 -1
  59. package/lib/launcher/dashboard.js +39 -33
  60. package/lib/launcher/editorRegistry.js +5 -3
  61. package/lib/launcher/editors.d.ts +15 -5
  62. package/lib/launcher/icons.d.ts +0 -37
  63. package/lib/launcher/icons.js +11 -12
  64. package/lib/launcher/index.js +2 -1
  65. package/lib/launcher/invocation.d.ts +0 -15
  66. package/lib/launcher/invocation.js +1 -1
  67. package/lib/launcher/schemaDefaults.d.ts +3 -1
  68. package/lib/launcher/schemaDefaults.js +3 -1
  69. package/lib/mimeData.d.ts +7 -0
  70. package/lib/mimeData.js +16 -0
  71. package/lib/omnibox/index.d.ts +4 -0
  72. package/lib/omnibox/index.js +43 -2
  73. package/lib/omnibox/model.d.ts +43 -14
  74. package/lib/omnibox/model.js +117 -14
  75. package/lib/omnibox/recents.d.ts +65 -0
  76. package/lib/omnibox/recents.js +109 -0
  77. package/lib/omnibox/tokens.d.ts +6 -2
  78. package/lib/omnibox/widget.d.ts +23 -6
  79. package/lib/omnibox/widget.js +26 -7
  80. package/lib/showOutput/index.js +15 -18
  81. package/lib/terminals/agentTerminals.d.ts +63 -0
  82. package/lib/terminals/agentTerminals.js +157 -0
  83. package/lib/terminals/index.d.ts +7 -1
  84. package/lib/terminals/index.js +50 -30
  85. package/lib/terminals/model.d.ts +11 -2
  86. package/lib/terminals/model.js +27 -8
  87. package/lib/terminals/tokens.d.ts +66 -0
  88. package/lib/terminals/tokens.js +7 -0
  89. package/lib/terminals/widget.d.ts +3 -6
  90. package/lib/terminals/widget.js +1 -1
  91. package/lib/topBar/index.js +3 -3
  92. package/lib/walkthrough/index.js +14 -3
  93. package/lib/walkthrough/panel.d.ts +12 -4
  94. package/lib/walkthrough/panel.js +9 -3
  95. package/package.json +49 -51
  96. package/schema/command-bar.json +10 -0
  97. package/schema/omnibox.json +15 -0
  98. package/src/agentSessions.ts +6 -2
  99. package/src/askAgent/editorSelection.ts +138 -0
  100. package/src/askAgent/icons.ts +16 -0
  101. package/src/askAgent/index.ts +915 -0
  102. package/src/askAgent/popup.tsx +407 -0
  103. package/src/askAgent/prompt.ts +144 -0
  104. package/src/askAgent/queue.ts +269 -0
  105. package/src/askAgent/queuePanel.tsx +418 -0
  106. package/src/askAgent/targetPicker.tsx +209 -0
  107. package/src/askAgent/tokens.ts +139 -0
  108. package/src/commandBar/index.ts +75 -49
  109. package/src/editorBreadcrumbs/index.ts +14 -4
  110. package/src/editorBreadcrumbs/widget.ts +11 -9
  111. package/src/fileBrowser/commands.ts +45 -31
  112. package/src/fileBrowser/contents.ts +7 -3
  113. package/src/fileBrowser/fileBrowser.tsx +1 -1
  114. package/src/fileBrowser/widget.tsx +5 -3
  115. package/src/fileTypeIcons/index.ts +6 -3
  116. package/src/git/api.ts +2 -61
  117. package/src/git/askRequest.ts +113 -0
  118. package/src/git/commands.ts +41 -14
  119. package/src/git/diffModel.ts +1 -1
  120. package/src/git/diffProvider.tsx +11 -3
  121. package/src/git/diffSurface.tsx +114 -22
  122. package/src/git/diffTheme.ts +1 -1
  123. package/src/git/diffWidget.tsx +54 -7
  124. package/src/git/imageDiff.tsx +169 -58
  125. package/src/git/index.ts +10 -2
  126. package/src/git/notebookDiff.tsx +91 -57
  127. package/src/git/tokens.ts +4 -2
  128. package/src/highlight/index.ts +12 -8
  129. package/src/index.ts +2 -0
  130. package/src/launcher/agents.ts +3 -4
  131. package/src/launcher/commands.ts +6 -2
  132. package/src/launcher/dashboard.tsx +86 -38
  133. package/src/launcher/editorRegistry.ts +10 -4
  134. package/src/launcher/editors.ts +15 -5
  135. package/src/launcher/icons.ts +11 -12
  136. package/src/launcher/index.ts +2 -1
  137. package/src/launcher/invocation.ts +1 -1
  138. package/src/launcher/schemaDefaults.ts +3 -1
  139. package/src/mimeData.ts +20 -0
  140. package/src/omnibox/index.ts +48 -1
  141. package/src/omnibox/model.ts +173 -33
  142. package/src/omnibox/recents.ts +155 -0
  143. package/src/omnibox/tokens.ts +6 -2
  144. package/src/omnibox/widget.tsx +54 -13
  145. package/src/showOutput/index.ts +20 -23
  146. package/src/terminals/agentTerminals.ts +238 -0
  147. package/src/terminals/index.ts +54 -30
  148. package/src/terminals/model.ts +28 -7
  149. package/src/terminals/tokens.ts +77 -0
  150. package/src/terminals/widget.tsx +4 -2
  151. package/src/topBar/index.ts +12 -6
  152. package/src/walkthrough/index.ts +19 -4
  153. package/src/walkthrough/panel.ts +15 -5
  154. package/style/askAgent.css +544 -0
  155. package/style/commandBar.css +12 -33
  156. package/style/git.css +72 -31
  157. package/style/index.css +1 -0
  158. package/style/index.js +2 -0
  159. package/style/topBar.css +4 -4
@@ -4,12 +4,16 @@ import type { CommandRegistry } from '@lumino/commands';
4
4
  import type { ReadonlyPartialJSONValue } from '@lumino/coreutils';
5
5
  import type { Message } from '@lumino/messaging';
6
6
  import { Panel } from '@lumino/widgets';
7
- /** A rich visual embedded in a step (rendered through the rendermime registry). */
7
+ /**
8
+ * A rich visual embedded in a step (rendered through the rendermime registry).
9
+ */
8
10
  export interface IWalkthroughMedia {
9
11
  mimeType: string;
10
12
  data: ReadonlyPartialJSONValue;
11
13
  }
12
- /** One step of a walkthrough. Every field is optional, but a step needs at least one. */
14
+ /**
15
+ * One step of a walkthrough. Every field is optional, but a step needs at least one.
16
+ */
13
17
  export interface IWalkthroughStep {
14
18
  title?: string;
15
19
  body?: string;
@@ -36,7 +40,9 @@ export declare namespace WalkthroughPanel {
36
40
  */
37
41
  export declare class WalkthroughPanel extends Panel {
38
42
  constructor(options: WalkthroughPanel.IOptions);
39
- /** Remove every step, leaving an empty panel. */
43
+ /**
44
+ * Remove every step, leaving an empty panel.
45
+ */
40
46
  clear(): void;
41
47
  /**
42
48
  * Dispose on close (e.g. the close button when the panel has been dragged to
@@ -44,7 +50,9 @@ export declare class WalkthroughPanel extends Panel {
44
50
  * next `xtralab:walkthrough` call recreates the panel in the side area.
45
51
  */
46
52
  protected onCloseRequest(msg: Message): void;
47
- /** Append a step and scroll it into view. */
53
+ /**
54
+ * Append a step and scroll it into view.
55
+ */
48
56
  addStep(step: IWalkthroughStep): Promise<void>;
49
57
  private _render;
50
58
  private _jumpButton;
@@ -1,6 +1,8 @@
1
1
  import { fileIcon } from '@jupyterlab/ui-components';
2
2
  import { Panel, Widget } from '@lumino/widgets';
3
- /** Command the jump links dispatch; keeps the panel decoupled from the plugin. */
3
+ /**
4
+ * Command the jump links dispatch; keeps the panel decoupled from the plugin.
5
+ */
4
6
  const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
5
7
  /**
6
8
  * A read-only, scrollable column of walkthrough steps for the side area.
@@ -19,7 +21,9 @@ export class WalkthroughPanel extends Panel {
19
21
  this._trans = options.trans;
20
22
  this.addClass('jp-xtralab-Walkthrough');
21
23
  }
22
- /** Remove every step, leaving an empty panel. */
24
+ /**
25
+ * Remove every step, leaving an empty panel.
26
+ */
23
27
  clear() {
24
28
  while (this.widgets.length > 0) {
25
29
  this.widgets[0].dispose();
@@ -34,7 +38,9 @@ export class WalkthroughPanel extends Panel {
34
38
  super.onCloseRequest(msg);
35
39
  this.dispose();
36
40
  }
37
- /** Append a step and scroll it into view. */
41
+ /**
42
+ * Append a step and scroll it into view.
43
+ */
38
44
  async addStep(step) {
39
45
  const card = new Panel();
40
46
  card.addClass('jp-xtralab-Walkthrough-step');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xtralab",
3
- "version": "0.11.1",
3
+ "version": "0.12.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",
@@ -58,65 +58,61 @@
58
58
  "watch:labextension": "jupyter-builder watch ."
59
59
  },
60
60
  "dependencies": {
61
- "@codemirror/language": "^6.12.1",
62
- "@codemirror/state": "^6.5.4",
63
- "@codemirror/view": "^6.39.14",
64
- "@jupyterlab/application": "^4.6.0-beta.0",
65
- "@jupyterlab/apputils": "^4.7.0-beta.0",
66
- "@jupyterlab/codemirror": "^4.6.0-beta.0",
67
- "@jupyterlab/coreutils": "^6.6.0-beta.0",
68
- "@jupyterlab/docmanager": "^4.6.0-beta.0",
69
- "@jupyterlab/docregistry": "^4.6.0-beta.0",
70
- "@jupyterlab/git": "^0.54.0-a1",
71
- "@jupyterlab/launcher": "^4.6.0-beta.0",
72
- "@jupyterlab/mainmenu": "^4.6.0-beta.0",
73
- "@jupyterlab/nbformat": "^4.6.0-beta.0",
74
- "@jupyterlab/outputarea": "^4.6.0-beta.0",
75
- "@jupyterlab/rendermime": "^4.6.0-beta.0",
76
- "@jupyterlab/services": "^7.6.0-beta.0",
77
- "@jupyterlab/settingregistry": "^4.6.0-beta.0",
78
- "@jupyterlab/terminal": "^4.6.0-beta.0",
79
- "@jupyterlab/translation": "^4.6.0-beta.0",
80
- "@jupyterlab/ui-components": "^4.6.0-beta.0",
81
- "@lumino/algorithm": "^2.0.0",
82
- "@lumino/commands": "^2.0.0",
83
- "@lumino/coreutils": "^2.0.0",
84
- "@lumino/disposable": "^2.0.0",
85
- "@lumino/dragdrop": "^2.0.0",
86
- "@lumino/messaging": "^2.0.0",
61
+ "@codemirror/language": "^6.12.3",
62
+ "@codemirror/state": "^6.7.0",
63
+ "@codemirror/view": "^6.43.2",
64
+ "@jupyterlab/application": "^4.6.0",
65
+ "@jupyterlab/apputils": "^4.7.0",
66
+ "@jupyterlab/codemirror": "^4.6.0",
67
+ "@jupyterlab/coreutils": "^6.6.0",
68
+ "@jupyterlab/docmanager": "^4.6.0",
69
+ "@jupyterlab/docregistry": "^4.6.0",
70
+ "@jupyterlab/fileeditor": "^4.6.0",
71
+ "@jupyterlab/git": "^0.54.0",
72
+ "@jupyterlab/mainmenu": "^4.6.0",
73
+ "@jupyterlab/nbformat": "^4.6.0",
74
+ "@jupyterlab/notebook": "^4.6.0",
75
+ "@jupyterlab/outputarea": "^4.6.0",
76
+ "@jupyterlab/rendermime": "^4.6.0",
77
+ "@jupyterlab/services": "^7.6.0",
78
+ "@jupyterlab/settingregistry": "^4.6.0",
79
+ "@jupyterlab/statedb": "^4.6.0",
80
+ "@jupyterlab/terminal": "^4.6.0",
81
+ "@jupyterlab/translation": "^4.6.0",
82
+ "@jupyterlab/ui-components": "^4.6.0",
83
+ "@lumino/algorithm": "^2.0.4",
84
+ "@lumino/commands": "^2.3.3",
85
+ "@lumino/coreutils": "^2.2.2",
86
+ "@lumino/disposable": "^2.1.5",
87
+ "@lumino/dragdrop": "^2.1.8",
88
+ "@lumino/messaging": "^2.0.4",
87
89
  "@lumino/polling": "^2.1.5",
88
- "@lumino/signaling": "^2.0.0",
89
- "@lumino/widgets": "^2.0.0",
90
- "@pierre/diffs": "^1.2.7",
90
+ "@lumino/signaling": "^2.1.5",
91
+ "@lumino/widgets": "^2.8.0",
92
+ "@pierre/diffs": "^1.2.11",
91
93
  "@pierre/trees": "1.0.0-beta.4",
92
94
  "detect-indent": "^7.0.2",
93
95
  "ignore": "^5.3.2",
94
- "react": "^18.2.0",
95
- "react-dom": "^18.2.0"
96
+ "react": "^18.3.1"
96
97
  },
97
98
  "devDependencies": {
98
- "@jupyter/builder": "^1.0.0-beta.1",
99
- "@types/json-schema": "^7.0.11",
100
- "@types/react": "^18.0.26",
101
- "@types/react-addons-linked-state-mixin": "^0.14.22",
102
- "@typescript-eslint/eslint-plugin": "^6.1.0",
103
- "@typescript-eslint/parser": "^6.1.0",
104
- "css-loader": "^6.7.1",
105
- "eslint": "^8.36.0",
106
- "eslint-config-prettier": "^8.8.0",
107
- "eslint-plugin-prettier": "^5.0.0",
108
- "npm-run-all2": "^7.0.1",
109
- "prettier": "^3.0.0",
110
- "rimraf": "^5.0.1",
111
- "source-map-loader": "^1.0.2",
112
- "style-loader": "^3.3.1",
113
- "stylelint": "^15.10.1",
99
+ "@jupyter/builder": "^1.0.2",
100
+ "@types/react": "^18.3.31",
101
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
102
+ "@typescript-eslint/parser": "^6.21.0",
103
+ "eslint": "^8.57.1",
104
+ "eslint-config-prettier": "^8.10.2",
105
+ "eslint-plugin-prettier": "^5.5.6",
106
+ "npm-run-all2": "^7.0.2",
107
+ "prettier": "^3.8.4",
108
+ "rimraf": "^5.0.10",
109
+ "stylelint": "^15.11.0",
114
110
  "stylelint-config-recommended": "^13.0.0",
115
111
  "stylelint-config-standard": "^34.0.0",
116
- "stylelint-csstree-validator": "^3.0.0",
117
- "stylelint-prettier": "^4.0.0",
112
+ "stylelint-csstree-validator": "^3.1.0",
113
+ "stylelint-prettier": "^4.1.0",
118
114
  "typescript": "~5.5.4",
119
- "yjs": "^13.5.0"
115
+ "yjs": "^13.6.31"
120
116
  },
121
117
  "pnpm": {
122
118
  "ignoredBuiltDependencies": [
@@ -153,6 +149,8 @@
153
149
  "dist",
154
150
  "coverage",
155
151
  "**/*.d.ts",
152
+ "docs",
153
+ "ui-tests",
156
154
  "desktop/out",
157
155
  "desktop/dist",
158
156
  "desktop/python/wheels",
@@ -0,0 +1,10 @@
1
+ {
2
+ "title": "xtralab Command Bar",
3
+ "description": "The search-bar-styled pill in the top bar that opens the omnibox.",
4
+ "jupyter.lab.toolbars": {
5
+ "TopBar": [{ "name": "omnibox", "rank": 100 }]
6
+ },
7
+ "type": "object",
8
+ "properties": {},
9
+ "additionalProperties": false
10
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "title": "xtralab Omnibox",
3
+ "description": "The launcher overlay that searches files and commands and prompts agents.",
4
+ "type": "object",
5
+ "properties": {
6
+ "maxNumberRecents": {
7
+ "type": "integer",
8
+ "title": "Recent items number",
9
+ "description": "Number of recently used commands and files (each) the omnibox remembers and shows at the top before you type a query. Set to 0 to disable recent items.",
10
+ "default": 5,
11
+ "minimum": 0
12
+ }
13
+ },
14
+ "additionalProperties": false
15
+ }
@@ -28,10 +28,14 @@ export interface IAgentSessions {
28
28
  */
29
29
  get(sessionName: string): string | null;
30
30
 
31
- /** Record that a session was launched as the given agent command. */
31
+ /**
32
+ * Record that a session was launched as the given agent command.
33
+ */
32
34
  set(sessionName: string, command: string): void;
33
35
 
34
- /** Forget a session — called when it shuts down so the map stays bounded. */
36
+ /**
37
+ * Forget a session — called when it shuts down so the map stays bounded.
38
+ */
35
39
  delete(sessionName: string): void;
36
40
 
37
41
  /**
@@ -0,0 +1,138 @@
1
+ import type { JupyterFrontEnd } from '@jupyterlab/application';
2
+ import { CodeMirrorEditor } from '@jupyterlab/codemirror';
3
+ import { FileEditor } from '@jupyterlab/fileeditor';
4
+ import { NotebookPanel } from '@jupyterlab/notebook';
5
+ import type { EditorView } from '@codemirror/view';
6
+ import type { Widget } from '@lumino/widgets';
7
+
8
+ import type { IAskAgentContext, IAskAgentRequest } from './tokens';
9
+
10
+ /**
11
+ * A CodeMirror view a selection can be asked about, plus the context needed
12
+ * to describe it to an agent: the document path and, for notebooks, which
13
+ * cell the view belongs to.
14
+ */
15
+ interface IEditorTarget {
16
+ view: EditorView;
17
+ path: string;
18
+ cell?: {
19
+ index: number;
20
+ type: string;
21
+ };
22
+ }
23
+
24
+ /**
25
+ * Resolve the CodeMirror view a selection in `widget` would live in, or
26
+ * `null` when the widget holds no such editor. Two widget shapes are
27
+ * recognized:
28
+ *
29
+ * - a notebook panel, where the active cell carries the editor and
30
+ * `activeCellIndex` names its 0-based position in the nbformat `cells`
31
+ * array. A rendered markdown cell keeps its (hidden) editor, but a
32
+ * selection in the rendered HTML never passes {@link domSelectionInView},
33
+ * so no pill appears there; and
34
+ * - a document widget whose content is a `FileEditor`. The wrapper is not
35
+ * checked with `instanceof DocumentWidget`: `@jupyterlab/docregistry` is
36
+ * not a core singleton, so another copy of the class could be in play.
37
+ * `FileEditor` and `NotebookPanel` come from singleton packages.
38
+ */
39
+ export function resolveEditorTarget(
40
+ widget: Widget | null
41
+ ): IEditorTarget | null {
42
+ if (widget instanceof NotebookPanel) {
43
+ const cell = widget.content.activeCell;
44
+ if (cell !== null && cell.editor instanceof CodeMirrorEditor) {
45
+ return {
46
+ view: cell.editor.editor,
47
+ path: widget.context.path,
48
+ cell: { index: widget.content.activeCellIndex, type: cell.model.type }
49
+ };
50
+ }
51
+ return null;
52
+ }
53
+
54
+ const content = (widget as { content?: unknown } | null)?.content;
55
+ if (
56
+ content instanceof FileEditor &&
57
+ content.editor instanceof CodeMirrorEditor
58
+ ) {
59
+ return { view: content.editor.editor, path: content.context.path };
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ /**
66
+ * Whether the current DOM selection lives inside `view`. Guards the
67
+ * selection-change listener against selections made elsewhere (a terminal,
68
+ * a sidebar, another notebook cell) while the widget owning `view` happens
69
+ * to be the shell's current widget.
70
+ */
71
+ export function domSelectionInView(
72
+ view: EditorView,
73
+ selection: Selection
74
+ ): boolean {
75
+ const node = selection.anchorNode;
76
+ const element =
77
+ node instanceof Element ? node : (node?.parentElement ?? null);
78
+ return element !== null && view.dom.contains(element);
79
+ }
80
+
81
+ /**
82
+ * Build an ask-agent request from the current selection in the shell's
83
+ * current widget — a file editor or the active notebook cell — or return
84
+ * `null` when that widget holds no CodeMirror document editor.
85
+ *
86
+ * With `allowEmpty`, a collapsed selection falls back to the cursor's line
87
+ * (used by the command/shortcut path so it works without a mouse
88
+ * selection); otherwise an empty selection yields `null`.
89
+ */
90
+ export function editorAskRequest(
91
+ app: JupyterFrontEnd,
92
+ options: { allowEmpty?: boolean } = {}
93
+ ): IAskAgentRequest | null {
94
+ const target = resolveEditorTarget(app.shell.currentWidget);
95
+ if (target === null) {
96
+ return null;
97
+ }
98
+
99
+ const { view, path, cell } = target;
100
+ const { state } = view;
101
+ const main = state.selection.main;
102
+ if (main.empty && options.allowEmpty !== true) {
103
+ return null;
104
+ }
105
+
106
+ const startLine = state.doc.lineAt(main.from).number;
107
+ let endLine: number;
108
+ let text: string;
109
+ if (main.empty) {
110
+ endLine = startLine;
111
+ text = state.doc.lineAt(main.from).text;
112
+ } else {
113
+ // A selection that ends exactly at the start of a line (the common
114
+ // "drag over whole lines" gesture) should not count that line.
115
+ const endPos =
116
+ main.to === state.doc.lineAt(main.to).from ? main.to - 1 : main.to;
117
+ endLine = state.doc.lineAt(endPos).number;
118
+ text = state.sliceDoc(main.from, main.to);
119
+ }
120
+
121
+ // Anchor the popup at the selection head (where the cursor ended up).
122
+ // `coordsAtPos` returns `null` for positions scrolled out of view.
123
+ const coords = view.coordsAtPos(main.head);
124
+ const anchor = coords
125
+ ? new DOMRect(
126
+ coords.left,
127
+ coords.top,
128
+ Math.max(1, coords.right - coords.left),
129
+ Math.max(1, coords.bottom - coords.top)
130
+ )
131
+ : null;
132
+
133
+ const context: IAskAgentContext = { path, startLine, endLine, text };
134
+ if (cell !== undefined) {
135
+ context.cell = cell;
136
+ }
137
+ return { context, anchor };
138
+ }
@@ -0,0 +1,16 @@
1
+ import { LabIcon } from '@jupyterlab/ui-components';
2
+
3
+ /**
4
+ * Four-point sparkle used by the ask-agent affordances (selection pill,
5
+ * command palette entry). Drawn in-repo; the `jp-icon3` class lets the
6
+ * active theme recolor the glyph like other JupyterLab UI icons.
7
+ */
8
+ export const askAgentIcon = new LabIcon({
9
+ name: 'xtralab:ask-agent',
10
+ svgstr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
11
+ <g class="jp-icon3" fill="#616161">
12
+ <path d="M6 1l1 4 4 1-4 1-1 4-1-4-4-1 4-1 1-4z"/>
13
+ <path d="M12 9l.7 2.3L15 12l-2.3.7L12 15l-.7-2.3L9 12l2.3-.7L12 9z"/>
14
+ </g>
15
+ </svg>`
16
+ });