xtralab 0.4.0 → 0.6.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 (96) hide show
  1. package/README.md +110 -73
  2. package/lib/agentSessions.d.ts +50 -0
  3. package/lib/agentSessions.js +45 -0
  4. package/lib/fileBrowser/index.js +5 -1
  5. package/lib/git/commands.d.ts +12 -13
  6. package/lib/git/commands.js +7 -28
  7. package/lib/git/diffProvider.d.ts +8 -0
  8. package/lib/git/diffProvider.js +314 -0
  9. package/lib/git/diffSurface.d.ts +115 -0
  10. package/lib/git/diffSurface.js +342 -0
  11. package/lib/git/diffWidget.d.ts +8 -13
  12. package/lib/git/diffWidget.js +65 -311
  13. package/lib/git/imageDiff.d.ts +23 -0
  14. package/lib/git/imageDiff.js +256 -0
  15. package/lib/git/index.d.ts +10 -8
  16. package/lib/git/index.js +37 -52
  17. package/lib/index.d.ts +4 -2
  18. package/lib/index.js +14 -8
  19. package/lib/launcher/agents.d.ts +1 -1
  20. package/lib/launcher/agents.js +22 -13
  21. package/lib/launcher/commands.d.ts +32 -6
  22. package/lib/launcher/commands.js +81 -57
  23. package/lib/launcher/dashboard.d.ts +15 -0
  24. package/lib/launcher/dashboard.js +119 -5
  25. package/lib/launcher/editorRegistry.d.ts +43 -0
  26. package/lib/launcher/editorRegistry.js +96 -0
  27. package/lib/launcher/editors.d.ts +78 -0
  28. package/lib/launcher/editors.js +110 -0
  29. package/lib/launcher/icons.d.ts +25 -6
  30. package/lib/launcher/icons.js +116 -19
  31. package/lib/launcher/index.d.ts +2 -22
  32. package/lib/launcher/index.js +48 -25
  33. package/lib/launcher/registry.d.ts +21 -0
  34. package/lib/launcher/registry.js +28 -0
  35. package/lib/launcher/tokens.d.ts +41 -0
  36. package/lib/launcher/tokens.js +17 -0
  37. package/lib/terminals/detection.d.ts +11 -0
  38. package/lib/terminals/detection.js +50 -0
  39. package/lib/terminals/index.d.ts +27 -0
  40. package/lib/terminals/index.js +194 -0
  41. package/lib/terminals/model.d.ts +191 -0
  42. package/lib/terminals/model.js +418 -0
  43. package/lib/terminals/widget.d.ts +72 -0
  44. package/lib/terminals/widget.js +93 -0
  45. package/lib/topBar/icons.d.ts +13 -0
  46. package/lib/topBar/icons.js +27 -0
  47. package/lib/topBar/index.d.ts +16 -0
  48. package/lib/topBar/index.js +86 -0
  49. package/package.json +52 -35
  50. package/schema/launcher.json +56 -1
  51. package/src/agentSessions.ts +94 -0
  52. package/src/fileBrowser/index.ts +5 -1
  53. package/src/git/commands.ts +12 -38
  54. package/src/git/diffProvider.tsx +462 -0
  55. package/src/git/diffSurface.tsx +588 -0
  56. package/src/git/diffWidget.tsx +105 -465
  57. package/src/git/imageDiff.tsx +469 -0
  58. package/src/git/index.ts +38 -68
  59. package/src/index.ts +14 -8
  60. package/src/launcher/agents.ts +23 -14
  61. package/src/launcher/commands.ts +98 -60
  62. package/src/launcher/dashboard.tsx +205 -3
  63. package/src/launcher/editorRegistry.ts +149 -0
  64. package/src/launcher/editors.ts +183 -0
  65. package/src/launcher/icons.ts +120 -20
  66. package/src/launcher/index.ts +62 -27
  67. package/src/launcher/registry.ts +33 -0
  68. package/src/launcher/tokens.ts +51 -0
  69. package/src/terminals/detection.ts +65 -0
  70. package/src/terminals/index.ts +222 -0
  71. package/src/terminals/model.ts +497 -0
  72. package/src/terminals/widget.tsx +240 -0
  73. package/src/topBar/icons.ts +30 -0
  74. package/src/topBar/index.ts +121 -0
  75. package/style/git.css +267 -266
  76. package/style/index.css +2 -1
  77. package/style/index.js +2 -1
  78. package/style/launcher.css +124 -2
  79. package/style/terminals.css +235 -0
  80. package/style/topBar.css +38 -0
  81. package/lib/git/icons.d.ts +0 -14
  82. package/lib/git/icons.js +0 -19
  83. package/lib/git/panel.d.ts +0 -29
  84. package/lib/git/panel.js +0 -42
  85. package/lib/git/panelComponent.d.ts +0 -28
  86. package/lib/git/panelComponent.js +0 -320
  87. package/lib/statusBar/index.d.ts +0 -14
  88. package/lib/statusBar/index.js +0 -89
  89. package/lib/statusBar/widget.d.ts +0 -121
  90. package/lib/statusBar/widget.js +0 -237
  91. package/src/git/icons.ts +0 -20
  92. package/src/git/panel.tsx +0 -56
  93. package/src/git/panelComponent.tsx +0 -575
  94. package/src/statusBar/index.ts +0 -115
  95. package/src/statusBar/widget.tsx +0 -318
  96. package/style/statusBar.css +0 -95
@@ -0,0 +1,86 @@
1
+ import { ILabShell } from '@jupyterlab/application';
2
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
3
+ import { CommandToolbarButton } from '@jupyterlab/ui-components';
4
+ import { leftSidebarIcon, rightSidebarIcon } from './icons';
5
+ const PLUGIN_ID = 'xtralab:top-bar';
6
+ /**
7
+ * Upstream commands from `@jupyterlab/application-extension` that collapse or
8
+ * expand the side areas. We bind the buttons to these rather than
9
+ * reimplementing the toggle, so behavior (and the `isToggled`/`isEnabled`
10
+ * state the buttons reflect) stays in lockstep with the View menu entries
11
+ * and their keyboard shortcuts.
12
+ */
13
+ const TOGGLE_LEFT_AREA = 'application:toggle-left-area';
14
+ const TOGGLE_RIGHT_AREA = 'application:toggle-right-area';
15
+ const BUTTONS = [
16
+ {
17
+ id: 'xtralab-toggle-left-sidebar',
18
+ command: TOGGLE_LEFT_AREA,
19
+ icon: leftSidebarIcon,
20
+ caption: trans => trans.__('Toggle left sidebar'),
21
+ rank: -1,
22
+ sideClass: 'xtralab-TopBarButton-left'
23
+ },
24
+ {
25
+ id: 'xtralab-toggle-right-sidebar',
26
+ command: TOGGLE_RIGHT_AREA,
27
+ icon: rightSidebarIcon,
28
+ caption: trans => trans.__('Toggle right sidebar'),
29
+ rank: 1000,
30
+ sideClass: 'xtralab-TopBarButton-right'
31
+ }
32
+ ];
33
+ /**
34
+ * Add two icon buttons to the top bar that toggle the left and right side
35
+ * areas, mirroring the macOS-style sidebar buttons: a left-sidebar button at
36
+ * the leading edge and a right-sidebar button at the far edge.
37
+ *
38
+ * Each is a `CommandToolbarButton` wrapping an existing
39
+ * `application:toggle-{left,right}-area` command, so clicking it runs the
40
+ * same toggle as the View menu and the button reflects the command's state —
41
+ * pressed while the matching sidebar is open, and disabled when the area is
42
+ * empty. Note xtralab ships nothing in the right area by default (the
43
+ * property inspector, table of contents and debugger are all disabled), so
44
+ * the right button stays disabled until a widget is moved or added there.
45
+ */
46
+ const plugin = {
47
+ id: PLUGIN_ID,
48
+ description: 'Add left and right sidebar toggle buttons to the top bar.',
49
+ autoStart: true,
50
+ requires: [ILabShell],
51
+ optional: [ITranslator],
52
+ activate: (app, labShell, translator) => {
53
+ const { commands } = app;
54
+ const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
55
+ // Defer to `app.restored` so the upstream toggle commands are already in
56
+ // the registry: `CommandToolbarButton` renders nothing until its command
57
+ // exists and only re-renders on `commandChanged` of type `changed` /
58
+ // `many-changed` (not `added`), so mounting earlier could leave a button
59
+ // blank until the next unrelated command change.
60
+ void app.restored.then(() => {
61
+ for (const spec of BUTTONS) {
62
+ const button = new CommandToolbarButton({
63
+ commands,
64
+ id: spec.command,
65
+ icon: spec.icon,
66
+ // Empty label keeps the button icon-only: the override is forwarded
67
+ // verbatim and the component skips the label span when it is falsy.
68
+ label: '',
69
+ caption: spec.caption(trans),
70
+ // Toggle on mousedown without focusing the button. Otherwise
71
+ // ToolbarButtonComponent calls `event.target.focus()` on click,
72
+ // leaving the stealth button showing a focus fill/outline (the
73
+ // stray "border") until you click elsewhere — unwanted chrome for a
74
+ // title-bar control. Keyboard Tab focus still works, so this stays
75
+ // accessible.
76
+ noFocusOnClick: true
77
+ });
78
+ button.id = spec.id;
79
+ button.addClass('xtralab-TopBarButton');
80
+ button.addClass(spec.sideClass);
81
+ labShell.add(button, 'top', { rank: spec.rank });
82
+ }
83
+ });
84
+ }
85
+ };
86
+ export default plugin;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "xtralab",
3
- "version": "0.4.0",
4
- "description": "An opinionated JupyterLab meta-package that bundles a curated set of extensions, ships a path-first file browser and a VS Code-style git changes panel, and applies a quieter default workspace configuration.",
3
+ "version": "0.6.0",
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",
7
7
  "jupyterlab",
@@ -30,27 +30,28 @@
30
30
  "type": "git",
31
31
  "url": "https://github.com/jtpio/xtralab.git"
32
32
  },
33
+ "packageManager": "pnpm@10.28.0",
33
34
  "scripts": {
34
- "build": "jlpm build:lib && jlpm build:labextension:dev",
35
- "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
35
+ "build": "pnpm build:lib && pnpm build:labextension:dev",
36
+ "build:prod": "pnpm clean && pnpm build:lib:prod && pnpm build:labextension",
36
37
  "build:labextension": "jupyter-builder build .",
37
38
  "build:labextension:dev": "jupyter-builder build --development True .",
38
39
  "build:lib": "tsc --sourceMap",
39
40
  "build:lib:prod": "tsc",
40
- "clean": "jlpm clean:lib",
41
+ "clean": "pnpm clean:lib",
41
42
  "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
42
43
  "clean:lintcache": "rimraf .eslintcache .stylelintcache",
43
44
  "clean:labextension": "rimraf xtralab/labextension xtralab/_version.py",
44
- "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
45
- "eslint": "jlpm eslint:check --fix",
45
+ "clean:all": "pnpm clean:lib && pnpm clean:labextension && pnpm clean:lintcache",
46
+ "eslint": "pnpm eslint:check --fix",
46
47
  "eslint:check": "eslint . --cache --ext .ts,.tsx",
47
- "install:extension": "jlpm build",
48
- "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
49
- "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
50
- "prettier": "jlpm prettier:base --write --list-different",
48
+ "install:extension": "pnpm build",
49
+ "lint": "pnpm stylelint && pnpm prettier && pnpm eslint",
50
+ "lint:check": "pnpm stylelint:check && pnpm prettier:check && pnpm eslint:check",
51
+ "prettier": "pnpm prettier:base --write --list-different",
51
52
  "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
52
- "prettier:check": "jlpm prettier:base --check",
53
- "stylelint": "jlpm stylelint:check --fix",
53
+ "prettier:check": "pnpm prettier:base --check",
54
+ "stylelint": "pnpm stylelint:check --fix",
54
55
  "stylelint:check": "stylelint --cache \"style/**/*.css\"",
55
56
  "watch": "run-p watch:src watch:labextension",
56
57
  "watch:src": "tsc -w --sourceMap",
@@ -60,24 +61,25 @@
60
61
  "@codemirror/language": "^6.12.1",
61
62
  "@codemirror/state": "^6.5.4",
62
63
  "@codemirror/view": "^6.39.14",
63
- "@jupyterlab/application": "^4.6.0-alpha.5",
64
- "@jupyterlab/apputils": "^4.7.0-alpha.5",
65
- "@jupyterlab/codemirror": "^4.6.0-alpha.5",
66
- "@jupyterlab/coreutils": "^6.6.0-alpha.5",
67
- "@jupyterlab/docmanager": "^4.6.0-alpha.5",
68
- "@jupyterlab/docregistry": "^4.6.0-alpha.5",
69
- "@jupyterlab/launcher": "^4.6.0-alpha.5",
70
- "@jupyterlab/mainmenu": "^4.6.0-alpha.5",
71
- "@jupyterlab/nbformat": "^4.6.0-alpha.5",
72
- "@jupyterlab/outputarea": "^4.6.0-alpha.5",
73
- "@jupyterlab/rendermime": "^4.6.0-alpha.5",
74
- "@jupyterlab/services": "^7.6.0-alpha.5",
75
- "@jupyterlab/settingregistry": "^4.6.0-alpha.5",
76
- "@jupyterlab/statusbar": "^4.6.0-alpha.5",
77
- "@jupyterlab/terminal": "^4.6.0-alpha.5",
78
- "@jupyterlab/translation": "^4.6.0-alpha.5",
79
- "@jupyterlab/ui-components": "^4.6.0-alpha.5",
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",
80
81
  "@lumino/algorithm": "^2.0.0",
82
+ "@lumino/commands": "^2.0.0",
81
83
  "@lumino/coreutils": "^2.0.0",
82
84
  "@lumino/disposable": "^2.0.0",
83
85
  "@lumino/dragdrop": "^2.0.0",
@@ -93,7 +95,7 @@
93
95
  "react-dom": "^18.2.0"
94
96
  },
95
97
  "devDependencies": {
96
- "@jupyter/builder": "^0.0.8",
98
+ "@jupyter/builder": "^1.0.0-beta.1",
97
99
  "@types/json-schema": "^7.0.11",
98
100
  "@types/react": "^18.0.26",
99
101
  "@types/react-addons-linked-state-mixin": "^0.14.22",
@@ -116,8 +118,10 @@
116
118
  "typescript": "~5.5.4",
117
119
  "yjs": "^13.5.0"
118
120
  },
119
- "resolutions": {
120
- "lib0": "0.2.111"
121
+ "pnpm": {
122
+ "ignoredBuiltDependencies": [
123
+ "@fortawesome/fontawesome-free"
124
+ ]
121
125
  },
122
126
  "sideEffects": [
123
127
  "style/*.css",
@@ -131,8 +135,16 @@
131
135
  "extension": true,
132
136
  "outputDir": "xtralab/labextension",
133
137
  "schemaDir": "schema",
138
+ "sharedPackages": {
139
+ "@jupyterlab/git": {
140
+ "bundled": false,
141
+ "singleton": true
142
+ }
143
+ },
134
144
  "disabledExtensions": [
135
- "@jupyterlab/git",
145
+ "@jupyterlab/git:notebook-diff",
146
+ "@jupyterlab/git:plain-text-diff",
147
+ "@jupyterlab/git:image-diff",
136
148
  "@jupyterlab/launcher-extension"
137
149
  ]
138
150
  },
@@ -140,7 +152,12 @@
140
152
  "node_modules",
141
153
  "dist",
142
154
  "coverage",
143
- "**/*.d.ts"
155
+ "**/*.d.ts",
156
+ "desktop/out",
157
+ "desktop/dist",
158
+ "desktop/python/wheels",
159
+ "desktop/python/runtime",
160
+ "desktop/python/runtime.staging"
144
161
  ],
145
162
  "eslintConfig": {
146
163
  "extends": [
@@ -7,7 +7,7 @@
7
7
  "properties": {
8
8
  "agents": {
9
9
  "title": "Agents",
10
- "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, gemini, 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.",
10
+ "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.",
11
11
  "type": "array",
12
12
  "default": [],
13
13
  "items": {
@@ -65,6 +65,61 @@
65
65
  }
66
66
  }
67
67
  }
68
+ },
69
+ "editors": {
70
+ "title": "Editors",
71
+ "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.",
72
+ "type": "array",
73
+ "default": [],
74
+ "items": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "required": ["id"],
78
+ "properties": {
79
+ "id": {
80
+ "title": "ID",
81
+ "description": "Stable identifier. Match a built-in (nvim, vim) to override, or pick a new id to define a new editor.",
82
+ "type": "string"
83
+ },
84
+ "label": {
85
+ "title": "Label",
86
+ "description": "Tile title shown on the launcher.",
87
+ "type": "string"
88
+ },
89
+ "caption": {
90
+ "title": "Caption",
91
+ "description": "Tooltip shown for the tile.",
92
+ "type": "string"
93
+ },
94
+ "command": {
95
+ "title": "Command",
96
+ "description": "Literal string typed into the new terminal (e.g. `nvim`). Aliases work because shells expand them on interactive lines.",
97
+ "type": "string"
98
+ },
99
+ "iconSvg": {
100
+ "title": "Icon SVG",
101
+ "description": "Inline SVG for the tile icon. Required only for new editors whose icon is not already shipped by xtralab.",
102
+ "type": "string"
103
+ },
104
+ "rank": {
105
+ "title": "Rank",
106
+ "description": "Preference order (lower numbers come first). The launcher shows the first available editor by rank.",
107
+ "type": "number"
108
+ },
109
+ "enabled": {
110
+ "title": "Enabled",
111
+ "description": "When false, hides the editor from the launcher (and the Terminals panel stops badging it).",
112
+ "type": "boolean",
113
+ "default": true
114
+ },
115
+ "requireAvailable": {
116
+ "title": "Require available",
117
+ "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
+ "type": "boolean",
119
+ "default": true
120
+ }
121
+ }
122
+ }
68
123
  }
69
124
  }
70
125
  }
@@ -0,0 +1,94 @@
1
+ import type { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ import { Token } from '@lumino/coreutils';
3
+ import { ISignal, Signal } from '@lumino/signaling';
4
+
5
+ /**
6
+ * A shared map from a terminal session name to the agent command it was
7
+ * *launched* with (e.g. `claude`).
8
+ *
9
+ * This is the optimistic half of the running-agent badge: when the launcher
10
+ * (or the terminals panel's `+` menu) starts an agent via a
11
+ * `xtralab:start-agent:<id>` command, it records the session here so the
12
+ * panel can show the agent's logo immediately, before the server-side
13
+ * process detection has had a chance to confirm it. Server detection is the
14
+ * authoritative source once it reports on a session; this tag only fills the
15
+ * gap right after launch (and serves as a fallback if detection is
16
+ * unavailable).
17
+ *
18
+ * It exists as its own token, provided by a tiny dependency-free plugin,
19
+ * specifically so the launcher (the writer) and the terminals panel (the
20
+ * reader) can share it without depending on each other — a direct
21
+ * launcher↔terminals token pair would form an activation cycle.
22
+ */
23
+ export interface IAgentSessions {
24
+ /**
25
+ * The agent command a session was launched with, or `null` if we never
26
+ * launched an agent into it (e.g. a plain terminal, or one started before
27
+ * this ran).
28
+ */
29
+ get(sessionName: string): string | null;
30
+
31
+ /** Record that a session was launched as the given agent command. */
32
+ set(sessionName: string, command: string): void;
33
+
34
+ /** Forget a session — called when it shuts down so the map stays bounded. */
35
+ delete(sessionName: string): void;
36
+
37
+ /**
38
+ * Emitted with the affected session name whenever a record is added or
39
+ * removed, so readers can re-render.
40
+ */
41
+ readonly changed: ISignal<IAgentSessions, string>;
42
+ }
43
+
44
+ /**
45
+ * DI token for {@link IAgentSessions}. Provided by `xtralab:agent-sessions`;
46
+ * consumed (optionally) by both `xtralab:launcher` and `xtralab:terminals`.
47
+ */
48
+ export const IAgentSessions = new Token<IAgentSessions>(
49
+ 'xtralab:IAgentSessions',
50
+ 'A shared map from terminal session name to the agent command it was launched with.'
51
+ );
52
+
53
+ class AgentSessions implements IAgentSessions {
54
+ get(sessionName: string): string | null {
55
+ return this._byName.get(sessionName) ?? null;
56
+ }
57
+
58
+ set(sessionName: string, command: string): void {
59
+ if (this._byName.get(sessionName) === command) {
60
+ return;
61
+ }
62
+ this._byName.set(sessionName, command);
63
+ this._changed.emit(sessionName);
64
+ }
65
+
66
+ delete(sessionName: string): void {
67
+ if (this._byName.delete(sessionName)) {
68
+ this._changed.emit(sessionName);
69
+ }
70
+ }
71
+
72
+ get changed(): ISignal<IAgentSessions, string> {
73
+ return this._changed;
74
+ }
75
+
76
+ private _byName = new Map<string, string>();
77
+ private _changed = new Signal<IAgentSessions, string>(this);
78
+ }
79
+
80
+ /**
81
+ * Provides {@link IAgentSessions}. Deliberately has no dependencies so it can
82
+ * sit underneath both the launcher and the terminals panel without creating
83
+ * a dependency cycle between them.
84
+ */
85
+ const plugin: JupyterFrontEndPlugin<IAgentSessions> = {
86
+ id: 'xtralab:agent-sessions',
87
+ description:
88
+ 'Shared map from terminal session to the agent command it was launched with, used to badge terminal rows with the running agent.',
89
+ autoStart: true,
90
+ provides: IAgentSessions,
91
+ activate: (): IAgentSessions => new AgentSessions()
92
+ };
93
+
94
+ export default plugin;
@@ -37,7 +37,11 @@ const plugin: JupyterFrontEndPlugin<void> = {
37
37
  void app.commands.execute('docmanager:open', { path: serverPath });
38
38
  }
39
39
  });
40
- app.shell.add(browser, 'left', { rank: 50 });
40
+ // Ranks just under the Terminals panel in the left sidebar. The shipped
41
+ // `layout` setting assigns the default ranks (rank only, no area pin),
42
+ // so this browser stays movable via the "Move Widget" context menu; the
43
+ // `rank` here is the in-code fallback for when that setting is absent.
44
+ app.shell.add(browser, 'left', { rank: 2 });
41
45
  if (restorer) {
42
46
  restorer.add(browser, browser.id);
43
47
  }
@@ -1,8 +1,7 @@
1
1
  import { JupyterFrontEnd } from '@jupyterlab/application';
2
- import { ICommandPalette, IThemeManager } from '@jupyterlab/apputils';
2
+ import { IThemeManager } from '@jupyterlab/apputils';
3
3
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
4
4
  import { Contents } from '@jupyterlab/services';
5
- import { refreshIcon } from '@jupyterlab/ui-components';
6
5
  import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
7
6
 
8
7
  import {
@@ -10,16 +9,15 @@ import {
10
9
  diffWidgetId,
11
10
  updateGitDiffWidget
12
11
  } from './diffWidget';
13
- import { GitPanel } from './panel';
14
12
  import { IFileChange } from './tokens';
15
13
 
16
14
  /**
17
- * Command IDs exposed by the git plugin. Both are registered in
18
- * {@link registerGitCommands} so other extensions, the command palette and
19
- * keybindings can drive the panel without going through the React internals.
15
+ * Command IDs exposed by the launcher's git diff path. The launcher
16
+ * dashboard's "Changes" section drives {@link CommandIDs.openDiff} to open
17
+ * its own diff tab, independent of `jupyterlab-git` (whose panel reaches
18
+ * the same rendering via the providers in `diffProvider.tsx`).
20
19
  */
21
20
  export namespace CommandIDs {
22
- export const refresh = 'xtralab:git:refresh';
23
21
  export const openDiff = 'xtralab:git:open-diff';
24
22
  }
25
23
 
@@ -39,9 +37,7 @@ export namespace CommandArguments {
39
37
 
40
38
  export interface IRegisterGitCommandsOptions {
41
39
  app: JupyterFrontEnd;
42
- panel: GitPanel;
43
40
  themeManager: IThemeManager | null;
44
- commandPalette: ICommandPalette | null;
45
41
  /**
46
42
  * Contents manager used by the diff widget to write hunk-discard
47
43
  * results back to the working tree.
@@ -56,7 +52,10 @@ export interface IRegisterGitCommandsOptions {
56
52
  rendermime: IRenderMimeRegistry | null;
57
53
  /**
58
54
  * Called whenever the diff widget mutates the working tree (e.g. after
59
- * a hunk discard) so the plugin can refresh the changes panel.
55
+ * a hunk discard). The launcher dashboard polls git status on its own
56
+ * cadence and `jupyterlab-git`'s panel auto-refreshes on the contents
57
+ * `fileChanged` signal the save emits, so this is a hook for any extra
58
+ * bookkeeping rather than a required refresh.
60
59
  */
61
60
  onChanged: () => void;
62
61
  /**
@@ -77,18 +76,15 @@ export interface IRegisterGitCommandsOptions {
77
76
  }
78
77
 
79
78
  /**
80
- * Register the git commands on the application command registry. Returns
81
- * a disposer that clears the command palette items the registration created
82
- * (the commands themselves stay registered for the lifetime of the plugin).
79
+ * Register the launcher's git diff command on the application command
80
+ * registry.
83
81
  */
84
82
  export function registerGitCommands(
85
83
  options: IRegisterGitCommandsOptions
86
- ): () => void {
84
+ ): void {
87
85
  const {
88
86
  app,
89
- panel,
90
87
  themeManager,
91
- commandPalette,
92
88
  contentsManager,
93
89
  rendermime,
94
90
  onChanged,
@@ -98,15 +94,6 @@ export function registerGitCommands(
98
94
  } = options;
99
95
  const { commands } = app;
100
96
 
101
- commands.addCommand(CommandIDs.refresh, {
102
- label: 'Refresh Git Changes',
103
- caption: 'Refresh the git changes panel',
104
- icon: refreshIcon,
105
- execute: () => {
106
- panel.refresh();
107
- }
108
- });
109
-
110
97
  commands.addCommand(CommandIDs.openDiff, {
111
98
  label: 'Open Git Diff',
112
99
  caption: 'Open a side-by-side diff for a changed file',
@@ -139,19 +126,6 @@ export function registerGitCommands(
139
126
  app.shell.activateById(widget.id);
140
127
  }
141
128
  });
142
-
143
- const paletteItems = commandPalette
144
- ? [
145
- commandPalette.addItem({
146
- command: CommandIDs.refresh,
147
- category: 'Git'
148
- })
149
- ]
150
- : [];
151
-
152
- return () => {
153
- paletteItems.forEach(item => item.dispose());
154
- };
155
129
  }
156
130
 
157
131
  export { diffWidgetId };