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
@@ -0,0 +1,124 @@
1
+ import { Token } from '@lumino/coreutils';
2
+ /**
3
+ * The command id that opens the ask-agent popup for the current text-editor
4
+ * selection. Registered by `xtralab:ask-agent`; exposed here so menus and
5
+ * other plugins can reference it without importing the plugin internals.
6
+ */
7
+ export declare const ASK_AGENT_COMMAND = "xtralab:ask-agent";
8
+ /**
9
+ * A code location (and optionally the selected source text) that a prompt
10
+ * should point an agent at. The structured fields are composed into the
11
+ * final prompt by the ask-agent plugin so every entry point — editor
12
+ * selection, diff line selection — produces consistently worded prompts.
13
+ */
14
+ export interface IAskAgentContext {
15
+ /**
16
+ * Path of the file the selection belongs to, relative to {@link cwd} (or
17
+ * to the server root when no cwd is given) so the launched agent can open
18
+ * it directly.
19
+ */
20
+ path: string;
21
+ /**
22
+ * Server-relative directory the agent's terminal starts in. Omit to start
23
+ * in the server root. Diff selections pass the git repository root here so
24
+ * the agent can run git commands right away.
25
+ */
26
+ cwd?: string;
27
+ /**
28
+ * The notebook cell the selection lives in, when {@link path} is a
29
+ * notebook. `index` is the cell's 0-based position in the nbformat
30
+ * `cells` array; `type` is the nbformat cell type (`code`, `markdown`,
31
+ * `raw`). When set, {@link startLine}/{@link endLine} count within the
32
+ * cell's source rather than within a file.
33
+ */
34
+ cell?: {
35
+ index: number;
36
+ type: string;
37
+ };
38
+ /**
39
+ * First selected line, 1-indexed and inclusive. Omit when no line range is
40
+ * known (the prompt then only references the file).
41
+ */
42
+ startLine?: number;
43
+ /**
44
+ * Last selected line, 1-indexed and inclusive. Defaults to
45
+ * {@link startLine} when omitted.
46
+ */
47
+ endLine?: number;
48
+ /**
49
+ * Whether {@link startLine}/{@link endLine} index the file's current
50
+ * working-tree content. `false` for diff selections taken from another
51
+ * revision (the old side, or a challenger that is not the working tree),
52
+ * whose numbers must not be used to highlight the working file. Omitted
53
+ * means `true`.
54
+ */
55
+ linesInWorkingFile?: boolean;
56
+ /**
57
+ * The selected source text. May be empty when only a location is known;
58
+ * the prompt then relies on the path and line range alone.
59
+ */
60
+ text: string;
61
+ /**
62
+ * Extra agent-facing locator appended after the line range in the prompt,
63
+ * e.g. `the old side (INDEX) of the git diff`. Written in English because
64
+ * it is consumed by the agent, not shown in the UI.
65
+ */
66
+ location?: string;
67
+ /**
68
+ * Short user-facing tag shown in the popup header next to the file name,
69
+ * e.g. "old version". Translated, unlike {@link location}. Omit when the
70
+ * range reads naturally as the file's current content.
71
+ */
72
+ note?: string;
73
+ }
74
+ /**
75
+ * Where a submitted prompt goes: a fresh terminal started with the chosen
76
+ * agent's command, or an existing terminal session whose running agent
77
+ * receives the prompt in its input box (queued by the agent itself when it
78
+ * is busy).
79
+ */
80
+ export type AskAgentTarget = {
81
+ kind: 'new';
82
+ agentId: string;
83
+ } | {
84
+ kind: 'session';
85
+ name: string;
86
+ };
87
+ /**
88
+ * A request to open the ask-agent popup: what code to prompt about and
89
+ * where to place the popup on screen.
90
+ */
91
+ export interface IAskAgentRequest {
92
+ /**
93
+ * The code location and selection the prompt is about.
94
+ */
95
+ context: IAskAgentContext;
96
+ /**
97
+ * Viewport rectangle the popup anchors to (typically the selection end or
98
+ * the button that was clicked). `null` positions the popup near the top
99
+ * center of the viewport.
100
+ */
101
+ anchor: DOMRect | null;
102
+ }
103
+ /**
104
+ * The ask-agent popup: a small floating prompt box that sends the given
105
+ * code selection, plus the user's typed instruction, to one of the
106
+ * configured coding agents — in a fresh terminal, or into an agent already
107
+ * running in an existing one.
108
+ */
109
+ export interface IAskAgent {
110
+ /**
111
+ * Open the popup for `request`, replacing any popup already open.
112
+ */
113
+ open(request: IAskAgentRequest): void;
114
+ /**
115
+ * Close the popup if it is open.
116
+ */
117
+ close(): void;
118
+ }
119
+ /**
120
+ * DI token for {@link IAskAgent}. Provided by `xtralab:ask-agent` and
121
+ * consumed — optionally, so diffs still render when the plugin is disabled —
122
+ * by the git diff plugins to offer "ask an agent about these lines".
123
+ */
124
+ export declare const IAskAgent: Token<IAskAgent>;
@@ -0,0 +1,13 @@
1
+ import { Token } from '@lumino/coreutils';
2
+ /**
3
+ * The command id that opens the ask-agent popup for the current text-editor
4
+ * selection. Registered by `xtralab:ask-agent`; exposed here so menus and
5
+ * other plugins can reference it without importing the plugin internals.
6
+ */
7
+ export const ASK_AGENT_COMMAND = 'xtralab:ask-agent';
8
+ /**
9
+ * DI token for {@link IAskAgent}. Provided by `xtralab:ask-agent` and
10
+ * consumed — optionally, so diffs still render when the plugin is disabled —
11
+ * by the git diff plugins to offer "ask an agent about these lines".
12
+ */
13
+ export const IAskAgent = new Token('xtralab:IAskAgent', 'A floating prompt box that sends the current code selection to a coding agent running in a terminal.');
@@ -1,14 +1,18 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  /**
3
- * Add a centered, search-bar-styled command bar to the top area. Clicking it
4
- * opens the omnibox — a launcher overlay that fuzzy-searches files and
5
- * commands and routes a typed prompt to an agent (the leading sidebar toggle
6
- * and the menu bar sit to its left, the trailing sidebar toggle to its right;
7
- * the bar is centered over the whole panel as an overlay, so it stays put
8
- * regardless of their widths).
3
+ * Contribute a search-bar-styled command bar to JupyterLab's top bar toolbar.
4
+ * Clicking it opens the omnibox — a launcher overlay that fuzzy-searches files
5
+ * and commands and routes a typed prompt to an agent.
9
6
  *
10
- * The bar is only added when the omnibox is available — the `IOmnibox` token
11
- * is provided by `xtralab:omnibox` — so it never appears with nothing to open.
7
+ * The pill is a regular settings-driven toolbar item: this plugin registers a
8
+ * widget factory for it on the `IToolbarWidgetRegistry` and declares its rank
9
+ * in schema/command-bar.json, so users can move or disable it from the Top
10
+ * Bar settings like any other toolbar item.
11
+ *
12
+ * The factory is only registered when the omnibox is available — the
13
+ * `IOmnibox` token is provided by `xtralab:omnibox` — so the pill never
14
+ * appears with nothing to open (without a factory, the toolbar item resolves
15
+ * to an empty command button that renders nothing).
12
16
  */
13
17
  declare const plugin: JupyterFrontEndPlugin<void>;
14
18
  export default plugin;
@@ -1,4 +1,4 @@
1
- import { ILabShell } from '@jupyterlab/application';
1
+ import { IToolbarWidgetRegistry } from '@jupyterlab/apputils';
2
2
  import { ITranslator, nullTranslator } from '@jupyterlab/translation';
3
3
  import { searchIcon } from '@jupyterlab/ui-components';
4
4
  import { CommandRegistry } from '@lumino/commands';
@@ -6,15 +6,22 @@ import { Widget } from '@lumino/widgets';
6
6
  import { IOmnibox, OMNIBOX_OPEN_COMMAND } from '../omnibox/tokens';
7
7
  const PLUGIN_ID = 'xtralab:command-bar';
8
8
  /**
9
- * A search-bar-styled launcher for the `top` area. It holds no text input of
10
- * its own: clicking the pill (or pressing Enter/Space while it is focused)
11
- * runs `onActivate`, which opens the omnibox.
12
- *
13
- * The widget's root is a transparent, full-height wrapper centered as an
14
- * overlay over the whole top panel (style/commandBar.css). The wrapper
15
- * ignores pointer events so only the inner pill is clickable and the empty
16
- * strips above and below it let clicks fall through to whatever sits beneath
17
- * the overlay, such as the menu bar.
9
+ * Factory name of JupyterLab's settings-driven top bar toolbar (`#jp-top-bar`,
10
+ * built by `@jupyterlab/application-extension:top-bar`).
11
+ */
12
+ const TOPBAR_FACTORY = 'TopBar';
13
+ /**
14
+ * Name of the pill item within that toolbar. Its placement is declared under
15
+ * `jupyter.lab.toolbars` in this plugin's settings schema
16
+ * (schema/command-bar.json): the core toolbar ships a spacer at rank 50, so
17
+ * the pill's higher rank lands it at the trailing end of the toolbar, next to
18
+ * the right-sidebar toggle button.
19
+ */
20
+ const ITEM_NAME = 'omnibox';
21
+ /**
22
+ * A search-bar-styled launcher pill. It holds no text input of its own:
23
+ * clicking it (or pressing Enter/Space while it is focused) runs `onActivate`,
24
+ * which opens the omnibox.
18
25
  */
19
26
  class CommandBar extends Widget {
20
27
  constructor(options) {
@@ -30,9 +37,9 @@ class CommandBar extends Widget {
30
37
  }
31
38
  }
32
39
  onAfterAttach() {
33
- // The pill restores `pointer-events: auto`, so the click bubbles from it
34
- // up to the root node listened on here; keyboard Enter/Space on the
35
- // focused button raises the same synthetic click.
40
+ // The click bubbles from the inner pill button up to the root node
41
+ // listened on here; keyboard Enter/Space on the focused button raises the
42
+ // same synthetic click.
36
43
  this.node.addEventListener('click', this);
37
44
  }
38
45
  onBeforeDetach() {
@@ -41,6 +48,10 @@ class CommandBar extends Widget {
41
48
  }
42
49
  (function (CommandBar) {
43
50
  function createNode(label, caption, shortcut) {
51
+ // The root is a plain wrapper: as a toolbar item it receives the core
52
+ // `.jp-Toolbar > .jp-Toolbar-item` sizing (full height, centered flex),
53
+ // which would otherwise stretch the pill itself; the button inside keeps
54
+ // its own compact pill height.
44
55
  const wrapper = document.createElement('div');
45
56
  const button = document.createElement('button');
46
57
  button.type = 'button';
@@ -70,45 +81,51 @@ class CommandBar extends Widget {
70
81
  CommandBar.createNode = createNode;
71
82
  })(CommandBar || (CommandBar = {}));
72
83
  /**
73
- * Add a centered, search-bar-styled command bar to the top area. Clicking it
74
- * opens the omnibox — a launcher overlay that fuzzy-searches files and
75
- * commands and routes a typed prompt to an agent (the leading sidebar toggle
76
- * and the menu bar sit to its left, the trailing sidebar toggle to its right;
77
- * the bar is centered over the whole panel as an overlay, so it stays put
78
- * regardless of their widths).
84
+ * Contribute a search-bar-styled command bar to JupyterLab's top bar toolbar.
85
+ * Clicking it opens the omnibox — a launcher overlay that fuzzy-searches files
86
+ * and commands and routes a typed prompt to an agent.
87
+ *
88
+ * The pill is a regular settings-driven toolbar item: this plugin registers a
89
+ * widget factory for it on the `IToolbarWidgetRegistry` and declares its rank
90
+ * in schema/command-bar.json, so users can move or disable it from the Top
91
+ * Bar settings like any other toolbar item.
79
92
  *
80
- * The bar is only added when the omnibox is available — the `IOmnibox` token
81
- * is provided by `xtralab:omnibox` — so it never appears with nothing to open.
93
+ * The factory is only registered when the omnibox is available — the
94
+ * `IOmnibox` token is provided by `xtralab:omnibox` — so the pill never
95
+ * appears with nothing to open (without a factory, the toolbar item resolves
96
+ * to an empty command button that renders nothing).
82
97
  */
83
98
  const plugin = {
84
99
  id: PLUGIN_ID,
85
- description: 'Add a centered command bar to the top area that opens the omnibox.',
100
+ description: 'Add a command bar to the top bar toolbar that opens the omnibox.',
86
101
  autoStart: true,
87
- requires: [ILabShell],
102
+ requires: [IToolbarWidgetRegistry],
88
103
  optional: [IOmnibox, ITranslator],
89
- activate: (app, labShell, omnibox, translator) => {
104
+ activate: (app, toolbarRegistry, omnibox, translator) => {
90
105
  if (!omnibox) {
91
106
  return;
92
107
  }
93
108
  const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
94
- // Show the omnibox's keyboard shortcut as a hint in the pill, derived from
95
- // the live binding (registered by xtralab:omnibox, which activates first as
96
- // the IOmnibox provider) so it stays correct and uses the platform's
97
- // modifier symbols. Empty when no binding is registered.
98
- const binding = app.commands.keyBindings.find(keyBinding => keyBinding.command === OMNIBOX_OPEN_COMMAND);
99
- const shortcut = binding
100
- ? CommandRegistry.formatKeystroke(binding.keys)
101
- : '';
102
- const widget = new CommandBar({
103
- label: trans.__('Search…'),
104
- caption: trans.__('Search files and commands, or ask an agent'),
105
- shortcut,
106
- onActivate: () => {
107
- omnibox.open();
108
- }
109
+ toolbarRegistry.addFactory(TOPBAR_FACTORY, ITEM_NAME, () => {
110
+ // Show the omnibox's keyboard shortcut as a hint in the pill, derived
111
+ // from the live binding (registered by xtralab:omnibox, which activates
112
+ // first as the IOmnibox provider) so it stays correct and uses the
113
+ // platform's modifier symbols. Empty when no binding is registered.
114
+ const binding = app.commands.keyBindings.find(keyBinding => keyBinding.command === OMNIBOX_OPEN_COMMAND);
115
+ const shortcut = binding
116
+ ? CommandRegistry.formatKeystroke(binding.keys)
117
+ : '';
118
+ const widget = new CommandBar({
119
+ label: trans.__('Search…'),
120
+ caption: trans.__('Search files and commands, or ask an agent'),
121
+ shortcut,
122
+ onActivate: () => {
123
+ omnibox.open();
124
+ }
125
+ });
126
+ widget.id = 'xtralab-command-bar';
127
+ return widget;
109
128
  });
110
- widget.id = 'xtralab-command-bar';
111
- labShell.add(widget, 'top', { rank: 500 });
112
129
  }
113
130
  };
114
131
  export default plugin;
@@ -1,16 +1,19 @@
1
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
1
2
  import { DisposableDelegate } from '@lumino/disposable';
2
3
  import { EditorBreadcrumbs } from './widget';
3
4
  const PLUGIN_ID = 'xtralab:editor-breadcrumbs';
4
5
  const EDITOR_FACTORY = 'Editor';
5
6
  const TOOLBAR_ITEM_NAME = 'xtralab-editor-breadcrumbs';
6
7
  class EditorBreadcrumbsExtension {
7
- constructor(commands) {
8
+ constructor(commands, trans) {
8
9
  this._commands = commands;
10
+ this._trans = trans;
9
11
  }
10
12
  createNew(widget, context) {
11
13
  const breadcrumbs = new EditorBreadcrumbs({
12
14
  context,
13
- commands: this._commands
15
+ commands: this._commands,
16
+ trans: this._trans
14
17
  });
15
18
  const added = widget.toolbar.insertItem(0, TOOLBAR_ITEM_NAME, breadcrumbs);
16
19
  if (!added) {
@@ -34,8 +37,10 @@ const plugin = {
34
37
  id: PLUGIN_ID,
35
38
  description: 'Display the active file path in text editor toolbars.',
36
39
  autoStart: true,
37
- activate: (app) => {
38
- app.docRegistry.addWidgetExtension(EDITOR_FACTORY, new EditorBreadcrumbsExtension(app.commands));
40
+ optional: [ITranslator],
41
+ activate: (app, translator) => {
42
+ const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
43
+ app.docRegistry.addWidgetExtension(EDITOR_FACTORY, new EditorBreadcrumbsExtension(app.commands, trans));
39
44
  }
40
45
  };
41
46
  export default plugin;
@@ -1,17 +1,15 @@
1
1
  import type { DocumentRegistry } from '@jupyterlab/docregistry';
2
+ import type { TranslationBundle } from '@jupyterlab/translation';
2
3
  import type { CommandRegistry } from '@lumino/commands';
3
4
  import { Widget } from '@lumino/widgets';
4
- export interface IEditorBreadcrumbsOptions {
5
+ interface IEditorBreadcrumbsOptions {
5
6
  context: DocumentRegistry.IContext<DocumentRegistry.IModel>;
6
7
  commands: CommandRegistry;
8
+ trans: TranslationBundle;
7
9
  }
8
10
  /**
9
- * Read-only breadcrumb path for file editor toolbars.
10
- *
11
- * The upstream `@jupyterlab/filebrowser` BreadCrumbs widget is coupled to
12
- * FileBrowserModel navigation, drag/drop, and path editing. If that code
13
- * becomes reusable upstream, replace this local display-only renderer with
14
- * the shared implementation.
11
+ * Read-only breadcrumb path for file editor toolbars: renders the open
12
+ * file's path as clickable segments, each dispatching the reveal command.
15
13
  */
16
14
  export declare class EditorBreadcrumbs extends Widget {
17
15
  constructor(options: IEditorBreadcrumbsOptions);
@@ -29,6 +27,8 @@ export declare class EditorBreadcrumbs extends Widget {
29
27
  private _createSeparator;
30
28
  private _context;
31
29
  private _commands;
30
+ private _trans;
32
31
  private _container;
33
32
  private _content;
34
33
  }
34
+ export {};
@@ -15,20 +15,17 @@ const SEPARATOR_CLASS = 'jp-BreadCrumbs-separator';
15
15
  */
16
16
  const REVEAL_COMMAND = 'xtralab:reveal-path';
17
17
  /**
18
- * Read-only breadcrumb path for file editor toolbars.
19
- *
20
- * The upstream `@jupyterlab/filebrowser` BreadCrumbs widget is coupled to
21
- * FileBrowserModel navigation, drag/drop, and path editing. If that code
22
- * becomes reusable upstream, replace this local display-only renderer with
23
- * the shared implementation.
18
+ * Read-only breadcrumb path for file editor toolbars: renders the open
19
+ * file's path as clickable segments, each dispatching the reveal command.
24
20
  */
25
21
  export class EditorBreadcrumbs extends Widget {
26
22
  constructor(options) {
27
23
  super({ node: document.createElement('nav') });
28
24
  this._context = options.context;
29
25
  this._commands = options.commands;
26
+ this._trans = options.trans;
30
27
  this.addClass(EDITOR_BREADCRUMBS_CLASS);
31
- this.node.setAttribute('aria-label', 'Editor file path');
28
+ this.node.setAttribute('aria-label', this._trans.__('Editor file path'));
32
29
  this._container = document.createElement('span');
33
30
  this._container.className = BREADCRUMBS_CONTAINER_CLASS;
34
31
  this._content = document.createElement('span');
@@ -85,7 +82,8 @@ export class EditorBreadcrumbs extends Widget {
85
82
  const item = rootIcon.element({
86
83
  className: ROOT_CLASS,
87
84
  tag: 'span',
88
- title: PageConfig.getOption('serverRoot') || 'Jupyter Server Root',
85
+ title: PageConfig.getOption('serverRoot') ||
86
+ this._trans.__('Jupyter Server Root'),
89
87
  stylesheet: 'breadCrumb'
90
88
  });
91
89
  item.dataset.path = '/';
@@ -25,14 +25,7 @@ export declare namespace CommandIDs {
25
25
  const revealPath = "xtralab:reveal-path";
26
26
  const revealInFileTree = "xtralab:reveal-in-file-tree";
27
27
  }
28
- /**
29
- * Submenu id used to host the dynamically populated "Open With" entries.
30
- * Distinct from the core file browser's `jp-contextmenu-open-with` so the
31
- * core's populator does not try to fill ours with items derived from the
32
- * default file browser's selection.
33
- */
34
- export declare const OPEN_WITH_SUBMENU_ID = "jp-contextmenu-xtralab-open-with";
35
- export interface IRegisterCommandsOptions {
28
+ interface IRegisterCommandsOptions {
36
29
  app: JupyterFrontEnd;
37
30
  browser: IXtralabFileBrowser;
38
31
  docManager: IDocumentManager;
@@ -70,3 +63,4 @@ export declare function populateToolbar(opts: {
70
63
  app: JupyterFrontEnd;
71
64
  browser: IXtralabFileBrowser;
72
65
  }): void;
66
+ export {};
@@ -33,7 +33,7 @@ export var CommandIDs;
33
33
  * core's populator does not try to fill ours with items derived from the
34
34
  * default file browser's selection.
35
35
  */
36
- export const OPEN_WITH_SUBMENU_ID = 'jp-contextmenu-xtralab-open-with';
36
+ const OPEN_WITH_SUBMENU_ID = 'jp-contextmenu-xtralab-open-with';
37
37
  /**
38
38
  * Resolve the path of the item the user right-clicked. We prefer the
39
39
  * contextmenu event's target — that is how `app.contextMenu` resolves which
@@ -82,7 +82,9 @@ function getOpenPaths(app, browser) {
82
82
  }
83
83
  return candidates.filter(path => !path.endsWith('/'));
84
84
  }
85
- /** True iff there is an actionable target for a command on the right-click. */
85
+ /**
86
+ * True iff there is an actionable target for a command on the right-click.
87
+ */
86
88
  function hasTarget(app, browser) {
87
89
  return getTargetPath(app, browser) !== undefined;
88
90
  }
@@ -193,7 +195,10 @@ export function registerCommands(opts) {
193
195
  const { commands } = app;
194
196
  const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
195
197
  commands.addCommand(CommandIDs.open, {
196
- label: args => { var _a, _b; return ((_b = (_a = args.label) !== null && _a !== void 0 ? _a : args.factory) !== null && _b !== void 0 ? _b : 'Open'); },
198
+ label: args => {
199
+ var _a, _b;
200
+ return ((_b = (_a = args.label) !== null && _a !== void 0 ? _a : args.factory) !== null && _b !== void 0 ? _b : trans.__('Open'));
201
+ },
197
202
  icon: args => {
198
203
  var _a;
199
204
  const factoryName = args.factory;
@@ -219,7 +224,7 @@ export function registerCommands(opts) {
219
224
  }
220
225
  });
221
226
  commands.addCommand(CommandIDs.openBrowserTab, {
222
- label: 'Open in New Browser Tab',
227
+ label: trans.__('Open in New Browser Tab'),
223
228
  icon: fileIcon.bindprops({ stylesheet: 'menuItem' }),
224
229
  isEnabled: () => getTargetKind(app) === 'file',
225
230
  isVisible: () => getTargetKind(app) === 'file',
@@ -234,7 +239,7 @@ export function registerCommands(opts) {
234
239
  }
235
240
  });
236
241
  commands.addCommand(CommandIDs.rename, {
237
- label: 'Rename…',
242
+ label: trans.__('Rename…'),
238
243
  icon: editIcon.bindprops({ stylesheet: 'menuItem' }),
239
244
  mnemonic: 0,
240
245
  isEnabled: () => hasTarget(app, browser),
@@ -247,10 +252,10 @@ export function registerCommands(opts) {
247
252
  const serverPath = toServerPath(targetPath);
248
253
  const oldName = PathExt.basename(serverPath);
249
254
  const result = await InputDialog.getText({
250
- title: 'Rename',
251
- label: 'Enter a new name',
255
+ title: trans.__('Rename'),
256
+ label: trans.__('Enter a new name'),
252
257
  text: oldName,
253
- okLabel: 'Rename'
258
+ okLabel: trans.__('Rename')
254
259
  });
255
260
  const newName = (_a = result.value) === null || _a === void 0 ? void 0 : _a.trim();
256
261
  if (result.button.accept !== true ||
@@ -265,14 +270,14 @@ export function registerCommands(opts) {
265
270
  browser.refresh();
266
271
  }
267
272
  catch (err) {
268
- await showErrorMessage('Rename failed', err);
273
+ await showErrorMessage(trans.__('Rename failed'), err);
269
274
  }
270
275
  }
271
276
  });
272
277
  commands.addCommand(CommandIDs.del, {
273
278
  label: () => PageConfig.getOption('delete_to_trash') === 'true'
274
- ? 'Move to Trash'
275
- : 'Delete',
279
+ ? trans.__('Move to Trash')
280
+ : trans.__('Delete'),
276
281
  icon: closeIcon.bindprops({ stylesheet: 'menuItem' }),
277
282
  mnemonic: 0,
278
283
  isEnabled: () => hasTarget(app, browser),
@@ -284,11 +289,15 @@ export function registerCommands(opts) {
284
289
  const serverPath = toServerPath(targetPath);
285
290
  const trashing = PageConfig.getOption('delete_to_trash') === 'true';
286
291
  const result = await showDialog({
287
- title: trashing ? 'Move to Trash' : 'Delete',
288
- body: `Are you sure you want to permanently delete: ${serverPath}?`,
292
+ title: trashing ? trans.__('Move to Trash') : trans.__('Delete'),
293
+ body: trashing
294
+ ? trans.__('Are you sure you want to move to trash: %1?', serverPath)
295
+ : trans.__('Are you sure you want to permanently delete: %1?', serverPath),
289
296
  buttons: [
290
297
  Dialog.cancelButton(),
291
- Dialog.warnButton({ label: trashing ? 'Move to Trash' : 'Delete' })
298
+ Dialog.warnButton({
299
+ label: trashing ? trans.__('Move to Trash') : trans.__('Delete')
300
+ })
292
301
  ]
293
302
  });
294
303
  if (result.button.accept !== true) {
@@ -299,12 +308,12 @@ export function registerCommands(opts) {
299
308
  browser.refresh();
300
309
  }
301
310
  catch (err) {
302
- await showErrorMessage('Delete failed', err);
311
+ await showErrorMessage(trans.__('Delete failed'), err);
303
312
  }
304
313
  }
305
314
  });
306
315
  commands.addCommand(CommandIDs.duplicate, {
307
- label: 'Duplicate',
316
+ label: trans.__('Duplicate'),
308
317
  icon: copyIcon.bindprops({ stylesheet: 'menuItem' }),
309
318
  isEnabled: () => getTargetKind(app) === 'file',
310
319
  isVisible: () => getTargetKind(app) === 'file',
@@ -320,12 +329,12 @@ export function registerCommands(opts) {
320
329
  browser.notifyPathAdded(toCanonicalPath(created));
321
330
  }
322
331
  catch (err) {
323
- await showErrorMessage('Duplicate failed', err);
332
+ await showErrorMessage(trans.__('Duplicate failed'), err);
324
333
  }
325
334
  }
326
335
  });
327
336
  commands.addCommand(CommandIDs.copyPath, {
328
- label: 'Copy Path',
337
+ label: trans.__('Copy Path'),
329
338
  icon: fileIcon.bindprops({ stylesheet: 'menuItem' }),
330
339
  isEnabled: () => hasTarget(app, browser),
331
340
  execute: async () => {
@@ -344,7 +353,7 @@ export function registerCommands(opts) {
344
353
  }
345
354
  });
346
355
  commands.addCommand(CommandIDs.download, {
347
- label: 'Download',
356
+ label: trans.__('Download'),
348
357
  icon: downloadIcon.bindprops({ stylesheet: 'menuItem' }),
349
358
  isEnabled: () => getTargetKind(app) === 'file',
350
359
  isVisible: () => getTargetKind(app) === 'file',
@@ -364,21 +373,21 @@ export function registerCommands(opts) {
364
373
  document.body.removeChild(anchor);
365
374
  }
366
375
  catch (err) {
367
- await showErrorMessage('Download failed', err);
376
+ await showErrorMessage(trans.__('Download failed'), err);
368
377
  }
369
378
  }
370
379
  });
371
380
  commands.addCommand(CommandIDs.refresh, {
372
- label: 'Refresh File List',
373
- caption: 'Refresh the file browser',
381
+ label: trans.__('Refresh File List'),
382
+ caption: trans.__('Refresh the file browser'),
374
383
  icon: refreshIcon.bindprops({ stylesheet: 'menuItem' }),
375
384
  execute: () => {
376
385
  browser.refresh();
377
386
  }
378
387
  });
379
388
  commands.addCommand(CommandIDs.collapseAll, {
380
- label: 'Collapse All Folders',
381
- caption: 'Collapse all folders in the file browser',
389
+ label: trans.__('Collapse All Folders'),
390
+ caption: trans.__('Collapse all folders in the file browser'),
382
391
  icon: collapseAllIcon.bindprops({ stylesheet: 'menuItem' }),
383
392
  execute: () => {
384
393
  browser.collapseAll();
@@ -393,7 +402,7 @@ export function registerCommands(opts) {
393
402
  // there is no tree row for the root itself, so the browser is asked
394
403
  // to scroll back to the top and clear its selection instead.
395
404
  commands.addCommand(CommandIDs.revealPath, {
396
- label: 'Reveal in File Browser',
405
+ label: trans.__('Reveal in File Browser'),
397
406
  execute: (args) => {
398
407
  var _a;
399
408
  const path = (_a = args.path) !== null && _a !== void 0 ? _a : '';
@@ -422,8 +431,8 @@ export function registerCommands(opts) {
422
431
  }
423
432
  });
424
433
  commands.addCommand(CommandIDs.createNewDirectory, {
425
- label: 'New Folder',
426
- caption: 'Create a new folder',
434
+ label: trans.__('New Folder'),
435
+ caption: trans.__('Create a new folder'),
427
436
  icon: newFolderIcon.bindprops({ stylesheet: 'menuItem' }),
428
437
  execute: async () => {
429
438
  const cwd = getWorkingDirectory(browser);
@@ -435,13 +444,13 @@ export function registerCommands(opts) {
435
444
  browser.notifyPathAdded(toCanonicalPath(created));
436
445
  }
437
446
  catch (err) {
438
- await showErrorMessage('Could not create folder', err);
447
+ await showErrorMessage(trans.__('Could not create folder'), err);
439
448
  }
440
449
  }
441
450
  });
442
451
  commands.addCommand(CommandIDs.newLauncher, {
443
- label: 'New Launcher',
444
- caption: 'Open a new launcher',
452
+ label: trans.__('New Launcher'),
453
+ caption: trans.__('Open a new launcher'),
445
454
  // Skip the `menuItem` bindprops the other commands use: this command
446
455
  // lives on the toolbar, where the launcher-extension's blue button
447
456
  // styling expects the raw `jp-icon3` paths from `addIcon` so it can
@@ -15,10 +15,14 @@ export declare function toCanonicalPath(child: Contents.IModel): string;
15
15
  * paths so the value can be sent to the Jupyter contents API.
16
16
  */
17
17
  export declare function toServerPath(canonicalPath: string): string;
18
- export interface IListedDirectory {
19
- /** Canonical paths for every immediate child of the requested directory. */
18
+ interface IListedDirectory {
19
+ /**
20
+ * Canonical paths for every immediate child of the requested directory.
21
+ */
20
22
  paths: string[];
21
- /** Subset of {@link IListedDirectory.paths} that are directories. */
23
+ /**
24
+ * Subset of {@link IListedDirectory.paths} that are directories.
25
+ */
22
26
  subdirectories: string[];
23
27
  }
24
28
  /**
@@ -26,3 +30,4 @@ export interface IListedDirectory {
26
30
  * and return them as canonical `@pierre/trees` paths.
27
31
  */
28
32
  export declare function listDirectory(manager: Contents.IManager, serverPath: string): Promise<IListedDirectory>;
33
+ export {};