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
@@ -19,7 +19,7 @@ const BUTTONS = [
19
19
  icon: leftSidebarIcon,
20
20
  caption: trans => trans.__('Toggle left sidebar'),
21
21
  rank: -1,
22
- sideClass: 'xtralab-TopBarButton-left'
22
+ sideClass: 'jp-xtralab-TopBarButton-left'
23
23
  },
24
24
  {
25
25
  id: 'xtralab-toggle-right-sidebar',
@@ -27,7 +27,7 @@ const BUTTONS = [
27
27
  icon: rightSidebarIcon,
28
28
  caption: trans => trans.__('Toggle right sidebar'),
29
29
  rank: 1000,
30
- sideClass: 'xtralab-TopBarButton-right'
30
+ sideClass: 'jp-xtralab-TopBarButton-right'
31
31
  }
32
32
  ];
33
33
  /**
@@ -88,7 +88,7 @@ const plugin = {
88
88
  noFocusOnClick: true
89
89
  });
90
90
  button.id = spec.id;
91
- button.addClass('xtralab-TopBarButton');
91
+ button.addClass('jp-xtralab-TopBarButton');
92
92
  button.addClass(spec.sideClass);
93
93
  labShell.add(button, 'top', { rank: spec.rank });
94
94
  }
@@ -3,20 +3,31 @@ import { ICommandPalette } from '@jupyterlab/apputils';
3
3
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
4
4
  import { ITranslator, nullTranslator } from '@jupyterlab/translation';
5
5
  import { tocIcon } from '@jupyterlab/ui-components';
6
+ import { coerceData } from '../mimeData';
6
7
  import { WalkthroughPanel } from './panel';
7
8
  const PLUGIN_ID = 'xtralab:walkthrough';
8
- /** Append a step to the read-only walkthrough panel. */
9
+ /**
10
+ * Append a step to the read-only walkthrough panel.
11
+ */
9
12
  const WALKTHROUGH_COMMAND = 'xtralab:walkthrough';
10
13
  const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
11
14
  const PANEL_ID = 'xtralab-walkthrough';
12
- /** Pull a `{ mimeType, data }` media object out of a JSON command argument. */
15
+ /**
16
+ * Pull a `{ mimeType, data }` media object out of a JSON command argument.
17
+ */
13
18
  function readMedia(value) {
14
19
  if (value &&
15
20
  typeof value === 'object' &&
16
21
  !Array.isArray(value) &&
17
22
  typeof value.mimeType === 'string') {
18
23
  const media = value;
19
- return { mimeType: media.mimeType, data: media.data };
24
+ if (media.data === undefined) {
25
+ return undefined;
26
+ }
27
+ return {
28
+ mimeType: media.mimeType,
29
+ data: coerceData(media.mimeType, media.data)
30
+ };
20
31
  }
21
32
  return undefined;
22
33
  }
@@ -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.0",
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
+ }
@@ -8,7 +8,7 @@
8
8
  "properties": {
9
9
  "agents": {
10
10
  "title": "Agents",
11
- "description": "Override or extend the built-in list of agents shown on the launcher. Each entry must include an `id`. When the id matches a built-in (claude, codex, antigravity, copilot, goose, opencode, kiro, mistral-vibe), the fields you set override the corresponding defaults; leave a field out to keep the default. New ids define brand-new cards. Set `enabled: false` to hide a default. Set `requireAvailable: false` for shell aliases that should always show even though they aren't on PATH. The full built-in list is shown as the default below, so you can copy an entry and tweak it in place.",
11
+ "description": "Override or extend the built-in list of agents shown on the launcher. Each entry must include an `id`. When the id matches a built-in (claude, codex, antigravity, copilot, goose, opencode, kiro, mistral-vibe), the fields you set override the corresponding defaults; leave a field out to keep the default. New ids define brand-new cards. Set `enabled: false` to hide a default. Changing a built-in's `command` (for example to a shell alias like `ccm`) keeps the card visible even though the alias isn't on PATH; use `requireAvailable: false` only when you want to skip the check without changing the command. The full built-in list is shown as the default below, so you can copy an entry and tweak it in place.",
12
12
  "type": "array",
13
13
  "default": [],
14
14
  "items": {
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "requireAvailable": {
56
56
  "title": "Require available",
57
- "description": "When false, the launcher skips the availability check for this entry (useful for shell aliases that aren't on PATH but resolve in an interactive shell).",
57
+ "description": "When false, the launcher skips the availability check for this entry (useful for shell aliases that aren't on PATH but resolve in an interactive shell). Overriding a built-in agent's command turns this off automatically, so an aliased command still shows; set it explicitly to force the check on or off.",
58
58
  "type": "boolean",
59
59
  "default": true
60
60
  },
@@ -69,7 +69,7 @@
69
69
  },
70
70
  "editors": {
71
71
  "title": "Editors",
72
- "description": "Override or extend the terminal editors offered in the launcher's Open section (and recognized in the Terminals panel). Each entry must include an `id`. When the id matches a built-in (nvim, vim), the fields you set override the corresponding defaults; leave a field out to keep the default. New ids define additional editors. The launcher shows a single tile — the first editor, by `rank`, whose `command` is on PATH (Neovim before Vim by default). Set `enabled: false` to hide a built-in; disable both to remove the tile entirely. Set `requireAvailable: false` for a shell alias that should show even though it isn't on PATH. The full built-in list is shown as the default below, so you can copy an entry and tweak it in place.",
72
+ "description": "Override or extend the terminal editors offered in the launcher's Open section (and recognized in the Terminals panel). Each entry must include an `id`. When the id matches a built-in (nvim, vim), the fields you set override the corresponding defaults; leave a field out to keep the default. New ids define additional editors. The launcher shows a single tile — the first editor, by `rank`, whose `command` is on PATH (Neovim before Vim by default). Set `enabled: false` to hide a built-in; disable both to remove the tile entirely. Changing a built-in's `command` (for example to a shell alias) keeps the tile visible even though the alias isn't on PATH; use `requireAvailable: false` only when you want to skip the check without changing the command. The full built-in list is shown as the default below, so you can copy an entry and tweak it in place.",
73
73
  "type": "array",
74
74
  "default": [],
75
75
  "items": {
@@ -115,7 +115,7 @@
115
115
  },
116
116
  "requireAvailable": {
117
117
  "title": "Require available",
118
- "description": "When false, the launcher skips the availability check for this entry (useful for a shell alias that isn't on PATH but resolves in an interactive shell).",
118
+ "description": "When false, the launcher skips the availability check for this entry (useful for a shell alias that isn't on PATH but resolves in an interactive shell). Overriding a built-in editor's command turns this off automatically, so an aliased command still shows; set it explicitly to force the check on or off.",
119
119
  "type": "boolean",
120
120
  "default": true
121
121
  }
@@ -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
+ });