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.
- package/README.md +37 -229
- package/lib/agentSessions.d.ts +6 -2
- package/lib/askAgent/editorSelection.d.ts +53 -0
- package/lib/askAgent/editorSelection.js +95 -0
- package/lib/askAgent/icons.d.ts +7 -0
- package/lib/askAgent/icons.js +15 -0
- package/lib/askAgent/index.d.ts +32 -0
- package/lib/askAgent/index.js +741 -0
- package/lib/askAgent/popup.d.ts +83 -0
- package/lib/askAgent/popup.js +221 -0
- package/lib/askAgent/prompt.d.ts +24 -0
- package/lib/askAgent/prompt.js +116 -0
- package/lib/askAgent/queue.d.ts +93 -0
- package/lib/askAgent/queue.js +207 -0
- package/lib/askAgent/queuePanel.d.ts +76 -0
- package/lib/askAgent/queuePanel.js +188 -0
- package/lib/askAgent/targetPicker.d.ts +58 -0
- package/lib/askAgent/targetPicker.js +74 -0
- package/lib/askAgent/tokens.d.ts +124 -0
- package/lib/askAgent/tokens.js +13 -0
- package/lib/commandBar/index.d.ts +12 -8
- package/lib/commandBar/index.js +58 -41
- package/lib/editorBreadcrumbs/index.js +9 -4
- package/lib/editorBreadcrumbs/widget.d.ts +7 -7
- package/lib/editorBreadcrumbs/widget.js +6 -8
- package/lib/fileBrowser/commands.d.ts +2 -8
- package/lib/fileBrowser/commands.js +39 -30
- package/lib/fileBrowser/contents.d.ts +8 -3
- package/lib/fileBrowser/fileBrowser.d.ts +2 -1
- package/lib/fileBrowser/widget.d.ts +5 -9
- package/lib/fileBrowser/widget.js +1 -1
- package/lib/fileTypeIcons/index.js +5 -2
- package/lib/git/api.d.ts +0 -26
- package/lib/git/api.js +2 -44
- package/lib/git/askRequest.d.ts +23 -0
- package/lib/git/askRequest.js +90 -0
- package/lib/git/commands.d.ts +14 -2
- package/lib/git/commands.js +16 -13
- package/lib/git/diffModel.d.ts +1 -9
- package/lib/git/diffModel.js +1 -1
- package/lib/git/diffProvider.js +8 -3
- package/lib/git/diffSurface.d.ts +19 -2
- package/lib/git/diffSurface.js +64 -18
- package/lib/git/diffTheme.d.ts +0 -4
- package/lib/git/diffTheme.js +1 -1
- package/lib/git/diffWidget.d.ts +9 -0
- package/lib/git/diffWidget.js +38 -10
- package/lib/git/imageDiff.d.ts +17 -4
- package/lib/git/imageDiff.js +90 -30
- package/lib/git/index.js +7 -2
- package/lib/git/notebookDiff.d.ts +10 -67
- package/lib/git/notebookDiff.js +62 -69
- package/lib/git/tokens.d.ts +5 -2
- package/lib/highlight/index.js +12 -8
- package/lib/index.js +2 -0
- package/lib/launcher/agents.d.ts +11 -1
- package/lib/launcher/agents.js +26 -8
- package/lib/launcher/commands.d.ts +6 -2
- package/lib/launcher/dashboard.d.ts +8 -1
- package/lib/launcher/dashboard.js +39 -33
- package/lib/launcher/editorRegistry.js +5 -3
- package/lib/launcher/editors.d.ts +24 -7
- package/lib/launcher/editors.js +14 -5
- package/lib/launcher/icons.d.ts +0 -37
- package/lib/launcher/icons.js +11 -12
- package/lib/launcher/index.js +5 -2
- package/lib/launcher/invocation.d.ts +0 -15
- package/lib/launcher/invocation.js +1 -1
- package/lib/launcher/schemaDefaults.d.ts +3 -1
- package/lib/launcher/schemaDefaults.js +3 -1
- package/lib/mimeData.d.ts +7 -0
- package/lib/mimeData.js +16 -0
- package/lib/omnibox/index.d.ts +4 -0
- package/lib/omnibox/index.js +43 -2
- package/lib/omnibox/model.d.ts +43 -14
- package/lib/omnibox/model.js +117 -14
- package/lib/omnibox/recents.d.ts +65 -0
- package/lib/omnibox/recents.js +109 -0
- package/lib/omnibox/tokens.d.ts +6 -2
- package/lib/omnibox/widget.d.ts +23 -6
- package/lib/omnibox/widget.js +26 -7
- package/lib/showOutput/index.js +15 -18
- package/lib/terminals/agentTerminals.d.ts +63 -0
- package/lib/terminals/agentTerminals.js +157 -0
- package/lib/terminals/index.d.ts +7 -1
- package/lib/terminals/index.js +50 -30
- package/lib/terminals/model.d.ts +11 -2
- package/lib/terminals/model.js +27 -8
- package/lib/terminals/tokens.d.ts +66 -0
- package/lib/terminals/tokens.js +7 -0
- package/lib/terminals/widget.d.ts +3 -6
- package/lib/terminals/widget.js +1 -1
- package/lib/topBar/index.js +3 -3
- package/lib/walkthrough/index.js +14 -3
- package/lib/walkthrough/panel.d.ts +12 -4
- package/lib/walkthrough/panel.js +9 -3
- package/package.json +49 -51
- package/schema/command-bar.json +10 -0
- package/schema/launcher.json +4 -4
- package/schema/omnibox.json +15 -0
- package/src/agentSessions.ts +6 -2
- package/src/askAgent/editorSelection.ts +138 -0
- package/src/askAgent/icons.ts +16 -0
- package/src/askAgent/index.ts +915 -0
- package/src/askAgent/popup.tsx +407 -0
- package/src/askAgent/prompt.ts +144 -0
- package/src/askAgent/queue.ts +269 -0
- package/src/askAgent/queuePanel.tsx +418 -0
- package/src/askAgent/targetPicker.tsx +209 -0
- package/src/askAgent/tokens.ts +139 -0
- package/src/commandBar/index.ts +75 -49
- package/src/editorBreadcrumbs/index.ts +14 -4
- package/src/editorBreadcrumbs/widget.ts +11 -9
- package/src/fileBrowser/commands.ts +45 -31
- package/src/fileBrowser/contents.ts +7 -3
- package/src/fileBrowser/fileBrowser.tsx +1 -1
- package/src/fileBrowser/widget.tsx +5 -3
- package/src/fileTypeIcons/index.ts +6 -3
- package/src/git/api.ts +2 -61
- package/src/git/askRequest.ts +113 -0
- package/src/git/commands.ts +41 -14
- package/src/git/diffModel.ts +1 -1
- package/src/git/diffProvider.tsx +11 -3
- package/src/git/diffSurface.tsx +114 -22
- package/src/git/diffTheme.ts +1 -1
- package/src/git/diffWidget.tsx +54 -7
- package/src/git/imageDiff.tsx +169 -58
- package/src/git/index.ts +10 -2
- package/src/git/notebookDiff.tsx +91 -57
- package/src/git/tokens.ts +4 -2
- package/src/highlight/index.ts +12 -8
- package/src/index.ts +2 -0
- package/src/launcher/agents.ts +31 -7
- package/src/launcher/commands.ts +6 -2
- package/src/launcher/dashboard.tsx +86 -38
- package/src/launcher/editorRegistry.ts +10 -4
- package/src/launcher/editors.ts +34 -9
- package/src/launcher/icons.ts +11 -12
- package/src/launcher/index.ts +5 -2
- package/src/launcher/invocation.ts +1 -1
- package/src/launcher/schemaDefaults.ts +3 -1
- package/src/mimeData.ts +20 -0
- package/src/omnibox/index.ts +48 -1
- package/src/omnibox/model.ts +173 -33
- package/src/omnibox/recents.ts +155 -0
- package/src/omnibox/tokens.ts +6 -2
- package/src/omnibox/widget.tsx +54 -13
- package/src/showOutput/index.ts +20 -23
- package/src/terminals/agentTerminals.ts +238 -0
- package/src/terminals/index.ts +54 -30
- package/src/terminals/model.ts +28 -7
- package/src/terminals/tokens.ts +77 -0
- package/src/terminals/widget.tsx +4 -2
- package/src/topBar/index.ts +12 -6
- package/src/walkthrough/index.ts +19 -4
- package/src/walkthrough/panel.ts +15 -5
- package/style/askAgent.css +544 -0
- package/style/commandBar.css +12 -33
- package/style/git.css +72 -31
- package/style/index.css +1 -0
- package/style/index.js +2 -0
- package/style/topBar.css +4 -4
package/src/git/notebookDiff.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import type * as nbformat from '@jupyterlab/nbformat';
|
|
4
4
|
import { OutputAreaModel, SimplifiedOutputArea } from '@jupyterlab/outputarea';
|
|
5
5
|
import { IRenderMimeRegistry, MimeModel } from '@jupyterlab/rendermime';
|
|
6
|
+
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
6
7
|
import { MessageLoop } from '@lumino/messaging';
|
|
7
8
|
import { Widget } from '@lumino/widgets';
|
|
8
9
|
import { FileDiff } from '@pierre/diffs/react';
|
|
@@ -20,14 +21,14 @@ import { resolveDiffTheme } from './diffTheme';
|
|
|
20
21
|
* style cell sections, headers, and the per-cell diff blocks hang off this
|
|
21
22
|
* class.
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
const NOTEBOOK_DIFF_CSS_CLASS = 'jp-xtralab-NotebookDiff';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Minimal slice of nbformat 4.x that the diff inspects. Fields we don't
|
|
27
28
|
* read (attachments, e.g.) are intentionally not narrowed — we still pass
|
|
28
29
|
* them around as part of the cell, but we never project them into the diff.
|
|
29
30
|
*/
|
|
30
|
-
|
|
31
|
+
interface INotebookCell {
|
|
31
32
|
cell_type: string;
|
|
32
33
|
id?: string;
|
|
33
34
|
source: string | string[];
|
|
@@ -37,7 +38,7 @@ export interface INotebookCell {
|
|
|
37
38
|
attachments?: Record<string, unknown>;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
interface INotebookOutput {
|
|
41
42
|
output_type: string;
|
|
42
43
|
data?: Record<string, unknown>;
|
|
43
44
|
text?: string | string[];
|
|
@@ -49,7 +50,7 @@ export interface INotebookOutput {
|
|
|
49
50
|
metadata?: Record<string, unknown>;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
interface INotebook {
|
|
53
54
|
cells: INotebookCell[];
|
|
54
55
|
metadata: Record<string, unknown>;
|
|
55
56
|
nbformat: number;
|
|
@@ -63,7 +64,7 @@ export interface INotebook {
|
|
|
63
64
|
* carry the same payload but live on separate union members so `Extract`
|
|
64
65
|
* narrowing works on `kind` literals downstream.
|
|
65
66
|
*/
|
|
66
|
-
|
|
67
|
+
type NotebookCellDiff =
|
|
67
68
|
| {
|
|
68
69
|
kind: 'modified';
|
|
69
70
|
oldCell: INotebookCell;
|
|
@@ -116,7 +117,7 @@ export interface INotebookDiffResult {
|
|
|
116
117
|
* look like a notebook (missing `cells` array). The caller falls back to a
|
|
117
118
|
* raw text diff so a malformed file is still inspectable.
|
|
118
119
|
*/
|
|
119
|
-
|
|
120
|
+
function parseNotebook(text: string): INotebook | null {
|
|
120
121
|
if (text.length === 0) {
|
|
121
122
|
return emptyNotebook();
|
|
122
123
|
}
|
|
@@ -155,14 +156,14 @@ function emptyNotebook(): INotebook {
|
|
|
155
156
|
* strings. Normalize to one string so equality checks and diffs see the
|
|
156
157
|
* same shape regardless of how the writer chose to serialize it.
|
|
157
158
|
*/
|
|
158
|
-
|
|
159
|
+
function joinMultiline(value: string | string[] | undefined): string {
|
|
159
160
|
if (value === undefined) {
|
|
160
161
|
return '';
|
|
161
162
|
}
|
|
162
163
|
return Array.isArray(value) ? value.join('') : value;
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
|
|
166
|
+
function cellSource(cell: INotebookCell): string {
|
|
166
167
|
return joinMultiline(cell.source);
|
|
167
168
|
}
|
|
168
169
|
|
|
@@ -173,7 +174,7 @@ export function cellSource(cell: INotebookCell): string {
|
|
|
173
174
|
* to a `<mime-type>` placeholder so diffing image bytes doesn't drown the
|
|
174
175
|
* panel in base64 noise.
|
|
175
176
|
*/
|
|
176
|
-
|
|
177
|
+
function canonicalOutputs(cell: INotebookCell): string {
|
|
177
178
|
const outputs = cell.outputs ?? [];
|
|
178
179
|
if (outputs.length === 0) {
|
|
179
180
|
return '';
|
|
@@ -241,7 +242,7 @@ function stripAnsi(value: string): string {
|
|
|
241
242
|
* An empty metadata object returns the empty string so freshly-added cells
|
|
242
243
|
* that carry the default `{}` don't produce a `+{}` placeholder section.
|
|
243
244
|
*/
|
|
244
|
-
|
|
245
|
+
function canonicalMetadata(cell: INotebookCell): string {
|
|
245
246
|
const md = cell.metadata ?? {};
|
|
246
247
|
if (Object.keys(md).length === 0) {
|
|
247
248
|
return '';
|
|
@@ -254,7 +255,7 @@ export function canonicalMetadata(cell: INotebookCell): string {
|
|
|
254
255
|
* info, nbformat). We diff the bundle as a single block so the user sees
|
|
255
256
|
* all environment-level changes in one place.
|
|
256
257
|
*/
|
|
257
|
-
|
|
258
|
+
function canonicalNotebookMetadata(notebook: INotebook): string {
|
|
258
259
|
return stableStringify({
|
|
259
260
|
metadata: notebook.metadata,
|
|
260
261
|
nbformat: notebook.nbformat,
|
|
@@ -266,11 +267,11 @@ function stableStringify(value: unknown): string {
|
|
|
266
267
|
return JSON.stringify(value, sortKeysReplacer, 2);
|
|
267
268
|
}
|
|
268
269
|
|
|
269
|
-
function sortKeysReplacer(
|
|
270
|
+
function sortKeysReplacer(key: string, value: unknown): unknown {
|
|
270
271
|
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
271
272
|
const sorted: Record<string, unknown> = {};
|
|
272
|
-
for (const
|
|
273
|
-
sorted[
|
|
273
|
+
for (const name of Object.keys(value).sort()) {
|
|
274
|
+
sorted[name] = (value as Record<string, unknown>)[name];
|
|
274
275
|
}
|
|
275
276
|
return sorted;
|
|
276
277
|
}
|
|
@@ -284,7 +285,7 @@ function sortKeysReplacer(_key: string, value: unknown): unknown {
|
|
|
284
285
|
* itself sees, so an "equal" verdict here implies all per-cell diff blocks
|
|
285
286
|
* would be empty.
|
|
286
287
|
*/
|
|
287
|
-
|
|
288
|
+
function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean {
|
|
288
289
|
return (
|
|
289
290
|
a.cell_type === b.cell_type &&
|
|
290
291
|
cellSource(a) === cellSource(b) &&
|
|
@@ -305,7 +306,7 @@ export function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean {
|
|
|
305
306
|
* edits this is more readable than guessing alignment between unrelated
|
|
306
307
|
* insertions and deletions.
|
|
307
308
|
*/
|
|
308
|
-
|
|
309
|
+
function alignNotebookCells(
|
|
309
310
|
oldCells: INotebookCell[],
|
|
310
311
|
newCells: INotebookCell[]
|
|
311
312
|
): NotebookCellDiff[] {
|
|
@@ -373,7 +374,7 @@ export function alignNotebookCells(
|
|
|
373
374
|
return entries;
|
|
374
375
|
}
|
|
375
376
|
|
|
376
|
-
|
|
377
|
+
interface IBuildNotebookDiffOptions {
|
|
377
378
|
oldText: string;
|
|
378
379
|
newText: string;
|
|
379
380
|
}
|
|
@@ -754,8 +755,9 @@ function OutputsSection(props: {
|
|
|
754
755
|
entry: NotebookCellDiff;
|
|
755
756
|
rendermime: IRenderMimeRegistry | null;
|
|
756
757
|
placement: CellPlacement;
|
|
758
|
+
trans: TranslationBundle;
|
|
757
759
|
}): React.ReactElement | null {
|
|
758
|
-
const { entry, rendermime, placement } = props;
|
|
760
|
+
const { entry, rendermime, placement, trans } = props;
|
|
759
761
|
const oldCell = 'oldCell' in entry ? entry.oldCell : null;
|
|
760
762
|
const newCell = 'newCell' in entry ? entry.newCell : null;
|
|
761
763
|
const oldOutputs = oldCell?.outputs ?? [];
|
|
@@ -775,7 +777,7 @@ function OutputsSection(props: {
|
|
|
775
777
|
if (rendermime === null) {
|
|
776
778
|
return (
|
|
777
779
|
<SideBySidePanes
|
|
778
|
-
label=
|
|
780
|
+
label={trans.__('Outputs')}
|
|
779
781
|
hasOld={showOld}
|
|
780
782
|
hasNew={showNew}
|
|
781
783
|
oldNode={
|
|
@@ -793,7 +795,7 @@ function OutputsSection(props: {
|
|
|
793
795
|
}
|
|
794
796
|
return (
|
|
795
797
|
<SideBySidePanes
|
|
796
|
-
label=
|
|
798
|
+
label={trans.__('Outputs')}
|
|
797
799
|
hasOld={showOld}
|
|
798
800
|
hasNew={showNew}
|
|
799
801
|
oldNode={<OutputsPreview outputs={oldOutputs} rendermime={rendermime} />}
|
|
@@ -811,8 +813,9 @@ function MarkdownPreviewSection(props: {
|
|
|
811
813
|
entry: NotebookCellDiff;
|
|
812
814
|
rendermime: IRenderMimeRegistry | null;
|
|
813
815
|
placement: CellPlacement;
|
|
816
|
+
trans: TranslationBundle;
|
|
814
817
|
}): React.ReactElement | null {
|
|
815
|
-
const { entry, rendermime, placement } = props;
|
|
818
|
+
const { entry, rendermime, placement, trans } = props;
|
|
816
819
|
const isMarkdown = referenceCellType(entry) === 'markdown';
|
|
817
820
|
if (!isMarkdown || rendermime === null) {
|
|
818
821
|
return null;
|
|
@@ -826,7 +829,7 @@ function MarkdownPreviewSection(props: {
|
|
|
826
829
|
}
|
|
827
830
|
return (
|
|
828
831
|
<SideBySidePanes
|
|
829
|
-
label=
|
|
832
|
+
label={trans.__('Rendered')}
|
|
830
833
|
hasOld={showOld}
|
|
831
834
|
hasNew={showNew}
|
|
832
835
|
oldNode={<MarkdownPreview source={oldSource} rendermime={rendermime} />}
|
|
@@ -844,6 +847,10 @@ interface INotebookDiffViewProps {
|
|
|
844
847
|
*/
|
|
845
848
|
pierreTheme: boolean;
|
|
846
849
|
rendermime: IRenderMimeRegistry | null;
|
|
850
|
+
/**
|
|
851
|
+
* Translation bundle for user-facing strings.
|
|
852
|
+
*/
|
|
853
|
+
trans: TranslationBundle;
|
|
847
854
|
}
|
|
848
855
|
|
|
849
856
|
/**
|
|
@@ -859,12 +866,12 @@ interface INotebookDiffViewProps {
|
|
|
859
866
|
export function NotebookDiffView(
|
|
860
867
|
props: INotebookDiffViewProps
|
|
861
868
|
): React.ReactElement {
|
|
862
|
-
const { diff, dark, pierreTheme, rendermime } = props;
|
|
869
|
+
const { diff, dark, pierreTheme, rendermime, trans } = props;
|
|
863
870
|
const theme: DiffsThemeNames = resolveDiffTheme(dark, pierreTheme);
|
|
864
871
|
const summary = React.useMemo(() => summarize(diff.cells), [diff.cells]);
|
|
865
872
|
return (
|
|
866
873
|
<div className={NOTEBOOK_DIFF_CSS_CLASS}>
|
|
867
|
-
<NotebookDiffHeader summary={summary} />
|
|
874
|
+
<NotebookDiffHeader summary={summary} trans={trans} />
|
|
868
875
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-grid`}>
|
|
869
876
|
{diff.cells.map(entry => (
|
|
870
877
|
<CellEntryRow
|
|
@@ -874,6 +881,7 @@ export function NotebookDiffView(
|
|
|
874
881
|
theme={theme}
|
|
875
882
|
dark={dark}
|
|
876
883
|
rendermime={rendermime}
|
|
884
|
+
trans={trans}
|
|
877
885
|
/>
|
|
878
886
|
))}
|
|
879
887
|
</div>
|
|
@@ -882,6 +890,7 @@ export function NotebookDiffView(
|
|
|
882
890
|
metadata={diff.notebookMetadataDiff}
|
|
883
891
|
theme={theme}
|
|
884
892
|
dark={dark}
|
|
893
|
+
trans={trans}
|
|
885
894
|
/>
|
|
886
895
|
) : null}
|
|
887
896
|
</div>
|
|
@@ -897,11 +906,11 @@ export function NotebookDiffView(
|
|
|
897
906
|
* cell was inserted or removed relative to the other side.
|
|
898
907
|
*/
|
|
899
908
|
function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
|
|
900
|
-
const { entry } = props;
|
|
909
|
+
const { entry, trans } = props;
|
|
901
910
|
if (entry.kind === 'added') {
|
|
902
911
|
return (
|
|
903
912
|
<>
|
|
904
|
-
<EmptyCellPlaceholder side="old" reason="added" />
|
|
913
|
+
<EmptyCellPlaceholder side="old" reason="added" trans={trans} />
|
|
905
914
|
<CellDiffBlock {...props} placement="right" />
|
|
906
915
|
</>
|
|
907
916
|
);
|
|
@@ -910,7 +919,7 @@ function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
|
|
|
910
919
|
return (
|
|
911
920
|
<>
|
|
912
921
|
<CellDiffBlock {...props} placement="left" />
|
|
913
|
-
<EmptyCellPlaceholder side="new" reason="removed" />
|
|
922
|
+
<EmptyCellPlaceholder side="new" reason="removed" trans={trans} />
|
|
914
923
|
</>
|
|
915
924
|
);
|
|
916
925
|
}
|
|
@@ -920,9 +929,13 @@ function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
|
|
|
920
929
|
function EmptyCellPlaceholder(props: {
|
|
921
930
|
side: 'old' | 'new';
|
|
922
931
|
reason: 'added' | 'removed';
|
|
932
|
+
trans: TranslationBundle;
|
|
923
933
|
}): React.ReactElement {
|
|
924
|
-
const { side, reason } = props;
|
|
925
|
-
const label =
|
|
934
|
+
const { side, reason, trans } = props;
|
|
935
|
+
const label =
|
|
936
|
+
reason === 'added'
|
|
937
|
+
? trans.__('cell added on the right')
|
|
938
|
+
: trans.__('cell removed');
|
|
926
939
|
return (
|
|
927
940
|
<div
|
|
928
941
|
className={`${NOTEBOOK_DIFF_CSS_CLASS}-empty`}
|
|
@@ -930,7 +943,9 @@ function EmptyCellPlaceholder(props: {
|
|
|
930
943
|
aria-label={label}
|
|
931
944
|
>
|
|
932
945
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-emptyLabel`}>
|
|
933
|
-
{reason === 'added'
|
|
946
|
+
{reason === 'added'
|
|
947
|
+
? trans.__('— no cell —')
|
|
948
|
+
: trans.__('— cell removed —')}
|
|
934
949
|
</span>
|
|
935
950
|
</div>
|
|
936
951
|
);
|
|
@@ -958,31 +973,32 @@ function summarize(cells: NotebookCellDiff[]): INotebookDiffSummary {
|
|
|
958
973
|
|
|
959
974
|
function NotebookDiffHeader(props: {
|
|
960
975
|
summary: INotebookDiffSummary;
|
|
976
|
+
trans: TranslationBundle;
|
|
961
977
|
}): React.ReactElement {
|
|
962
|
-
const { summary } = props;
|
|
978
|
+
const { summary, trans } = props;
|
|
963
979
|
const total =
|
|
964
980
|
summary.added + summary.removed + summary.modified + summary.unchanged;
|
|
965
981
|
return (
|
|
966
982
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-header`}>
|
|
967
983
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-headerCount`}>
|
|
968
|
-
{
|
|
984
|
+
{trans._n('%1 cell', '%1 cells', total)}
|
|
969
985
|
</span>
|
|
970
986
|
{summary.modified > 0 ? (
|
|
971
987
|
<span
|
|
972
988
|
className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`}
|
|
973
989
|
data-kind="modified"
|
|
974
990
|
>
|
|
975
|
-
{summary.modified}
|
|
991
|
+
{trans.__('%1 modified', summary.modified)}
|
|
976
992
|
</span>
|
|
977
993
|
) : null}
|
|
978
994
|
{summary.added > 0 ? (
|
|
979
995
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`} data-kind="added">
|
|
980
|
-
{summary.added}
|
|
996
|
+
{trans.__('%1 added', summary.added)}
|
|
981
997
|
</span>
|
|
982
998
|
) : null}
|
|
983
999
|
{summary.removed > 0 ? (
|
|
984
1000
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`} data-kind="removed">
|
|
985
|
-
{summary.removed}
|
|
1001
|
+
{trans.__('%1 removed', summary.removed)}
|
|
986
1002
|
</span>
|
|
987
1003
|
) : null}
|
|
988
1004
|
{summary.unchanged > 0 ? (
|
|
@@ -990,7 +1006,7 @@ function NotebookDiffHeader(props: {
|
|
|
990
1006
|
className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`}
|
|
991
1007
|
data-kind="unchanged"
|
|
992
1008
|
>
|
|
993
|
-
{summary.unchanged}
|
|
1009
|
+
{trans.__('%1 unchanged', summary.unchanged)}
|
|
994
1010
|
</span>
|
|
995
1011
|
) : null}
|
|
996
1012
|
</div>
|
|
@@ -1013,6 +1029,7 @@ interface ICellDiffBlockProps {
|
|
|
1013
1029
|
theme: DiffsThemeNames;
|
|
1014
1030
|
dark: boolean;
|
|
1015
1031
|
rendermime: IRenderMimeRegistry | null;
|
|
1032
|
+
trans: TranslationBundle;
|
|
1016
1033
|
}
|
|
1017
1034
|
|
|
1018
1035
|
/**
|
|
@@ -1034,7 +1051,7 @@ interface IPlacedCellDiffBlockProps extends ICellDiffBlockProps {
|
|
|
1034
1051
|
}
|
|
1035
1052
|
|
|
1036
1053
|
function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
1037
|
-
const { entry, language, theme, dark, rendermime, placement } = props;
|
|
1054
|
+
const { entry, language, theme, dark, rendermime, placement, trans } = props;
|
|
1038
1055
|
// Unchanged cells start collapsed — the user opted out of seeing those
|
|
1039
1056
|
// sections by virtue of them being unchanged. A toggle lets them peek if
|
|
1040
1057
|
// they want.
|
|
@@ -1081,21 +1098,25 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
|
1081
1098
|
className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`}
|
|
1082
1099
|
data-kind={entry.kind}
|
|
1083
1100
|
>
|
|
1084
|
-
{kindBadge(entry.kind)}
|
|
1101
|
+
{kindBadge(entry.kind, trans)}
|
|
1085
1102
|
</span>
|
|
1086
1103
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellLabel`}>
|
|
1087
1104
|
{indexLabel} · {cellType}
|
|
1088
1105
|
</span>
|
|
1089
1106
|
{entry.kind === 'unchanged' ? (
|
|
1090
1107
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellHint`}>
|
|
1091
|
-
unchanged
|
|
1108
|
+
{trans.__('unchanged')}
|
|
1092
1109
|
</span>
|
|
1093
1110
|
) : null}
|
|
1094
1111
|
</header>
|
|
1095
1112
|
{!collapsed ? (
|
|
1096
1113
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBody`}>
|
|
1097
1114
|
{entry.kind === 'unchanged' ? (
|
|
1098
|
-
<UnchangedCellBody
|
|
1115
|
+
<UnchangedCellBody
|
|
1116
|
+
entry={entry}
|
|
1117
|
+
rendermime={rendermime}
|
|
1118
|
+
trans={trans}
|
|
1119
|
+
/>
|
|
1099
1120
|
) : (
|
|
1100
1121
|
<>
|
|
1101
1122
|
{subdiffs.map(sub => (
|
|
@@ -1106,18 +1127,21 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
|
1106
1127
|
theme={theme}
|
|
1107
1128
|
dark={dark}
|
|
1108
1129
|
placement={placement}
|
|
1130
|
+
trans={trans}
|
|
1109
1131
|
/>
|
|
1110
1132
|
))}
|
|
1111
1133
|
<MarkdownPreviewSection
|
|
1112
1134
|
entry={entry}
|
|
1113
1135
|
rendermime={rendermime}
|
|
1114
1136
|
placement={placement}
|
|
1137
|
+
trans={trans}
|
|
1115
1138
|
/>
|
|
1116
1139
|
{showOutputs ? (
|
|
1117
1140
|
<OutputsSection
|
|
1118
1141
|
entry={entry}
|
|
1119
1142
|
rendermime={rendermime}
|
|
1120
1143
|
placement={placement}
|
|
1144
|
+
trans={trans}
|
|
1121
1145
|
/>
|
|
1122
1146
|
) : null}
|
|
1123
1147
|
{subdiffs.length === 0 &&
|
|
@@ -1127,7 +1151,7 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
|
1127
1151
|
('newCell' in entry && entry.newCell.outputs?.length))
|
|
1128
1152
|
) ? (
|
|
1129
1153
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellEmpty`}>
|
|
1130
|
-
No content differences detected.
|
|
1154
|
+
{trans.__('No content differences detected.')}
|
|
1131
1155
|
</div>
|
|
1132
1156
|
) : null}
|
|
1133
1157
|
</>
|
|
@@ -1144,8 +1168,9 @@ function CellSubDiff(props: {
|
|
|
1144
1168
|
theme: DiffsThemeNames;
|
|
1145
1169
|
dark: boolean;
|
|
1146
1170
|
placement: CellPlacement;
|
|
1171
|
+
trans: TranslationBundle;
|
|
1147
1172
|
}): React.ReactElement {
|
|
1148
|
-
const { kind, metadata, theme, dark, placement } = props;
|
|
1173
|
+
const { kind, metadata, theme, dark, placement, trans } = props;
|
|
1149
1174
|
// For modified / unchanged cells the diff spans both outer columns, and
|
|
1150
1175
|
// split mode lets it visually align with the outer old | new lanes. For
|
|
1151
1176
|
// added / removed cells the diff sits inside a single outer column, so
|
|
@@ -1156,7 +1181,7 @@ function CellSubDiff(props: {
|
|
|
1156
1181
|
return (
|
|
1157
1182
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-subdiff`} data-section={kind}>
|
|
1158
1183
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel`}>
|
|
1159
|
-
{subdiffLabel(kind)}
|
|
1184
|
+
{subdiffLabel(kind, trans)}
|
|
1160
1185
|
</div>
|
|
1161
1186
|
<FileDiff
|
|
1162
1187
|
fileDiff={metadata}
|
|
@@ -1175,8 +1200,9 @@ function NotebookMetadataBlock(props: {
|
|
|
1175
1200
|
metadata: FileDiffMetadata;
|
|
1176
1201
|
theme: DiffsThemeNames;
|
|
1177
1202
|
dark: boolean;
|
|
1203
|
+
trans: TranslationBundle;
|
|
1178
1204
|
}): React.ReactElement {
|
|
1179
|
-
const { metadata, theme, dark } = props;
|
|
1205
|
+
const { metadata, theme, dark, trans } = props;
|
|
1180
1206
|
return (
|
|
1181
1207
|
<section
|
|
1182
1208
|
className={`${NOTEBOOK_DIFF_CSS_CLASS}-cell`}
|
|
@@ -1185,17 +1211,17 @@ function NotebookMetadataBlock(props: {
|
|
|
1185
1211
|
>
|
|
1186
1212
|
<header
|
|
1187
1213
|
className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellHeader`}
|
|
1188
|
-
aria-label=
|
|
1214
|
+
aria-label={trans.__('Notebook metadata')}
|
|
1189
1215
|
>
|
|
1190
1216
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellChevron`}>▾</span>
|
|
1191
1217
|
<span
|
|
1192
1218
|
className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`}
|
|
1193
1219
|
data-kind="modified"
|
|
1194
1220
|
>
|
|
1195
|
-
M
|
|
1221
|
+
{trans.__('M')}
|
|
1196
1222
|
</span>
|
|
1197
1223
|
<span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellLabel`}>
|
|
1198
|
-
Notebook metadata
|
|
1224
|
+
{trans.__('Notebook metadata')}
|
|
1199
1225
|
</span>
|
|
1200
1226
|
</header>
|
|
1201
1227
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBody`}>
|
|
@@ -1205,6 +1231,7 @@ function NotebookMetadataBlock(props: {
|
|
|
1205
1231
|
theme={theme}
|
|
1206
1232
|
dark={dark}
|
|
1207
1233
|
placement="full"
|
|
1234
|
+
trans={trans}
|
|
1208
1235
|
/>
|
|
1209
1236
|
</div>
|
|
1210
1237
|
</section>
|
|
@@ -1222,8 +1249,9 @@ function NotebookMetadataBlock(props: {
|
|
|
1222
1249
|
function UnchangedCellBody(props: {
|
|
1223
1250
|
entry: Extract<NotebookCellDiff, { kind: 'unchanged' }>;
|
|
1224
1251
|
rendermime: IRenderMimeRegistry | null;
|
|
1252
|
+
trans: TranslationBundle;
|
|
1225
1253
|
}): React.ReactElement {
|
|
1226
|
-
const { entry, rendermime } = props;
|
|
1254
|
+
const { entry, rendermime, trans } = props;
|
|
1227
1255
|
const cell = entry.newCell;
|
|
1228
1256
|
const source = cellSource(cell);
|
|
1229
1257
|
const isMarkdown = cell.cell_type === 'markdown';
|
|
@@ -1237,13 +1265,13 @@ function UnchangedCellBody(props: {
|
|
|
1237
1265
|
</div>
|
|
1238
1266
|
) : (
|
|
1239
1267
|
<pre className={`${NOTEBOOK_DIFF_CSS_CLASS}-unchangedSource`}>
|
|
1240
|
-
{source.length > 0 ? source : '(empty cell)'}
|
|
1268
|
+
{source.length > 0 ? source : trans.__('(empty cell)')}
|
|
1241
1269
|
</pre>
|
|
1242
1270
|
)}
|
|
1243
1271
|
{hasOutputs && rendermime !== null ? (
|
|
1244
1272
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-unchangedOutputs`}>
|
|
1245
1273
|
<div className={`${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel`}>
|
|
1246
|
-
Outputs
|
|
1274
|
+
{trans.__('Outputs')}
|
|
1247
1275
|
</div>
|
|
1248
1276
|
<OutputsPreview outputs={outputs} rendermime={rendermime} />
|
|
1249
1277
|
</div>
|
|
@@ -1275,24 +1303,30 @@ function cellIndexLabel(entry: NotebookCellDiff): string {
|
|
|
1275
1303
|
return `Cell ${entry.oldIndex + 1} → ${entry.newIndex + 1}`;
|
|
1276
1304
|
}
|
|
1277
1305
|
|
|
1278
|
-
function kindBadge(
|
|
1306
|
+
function kindBadge(
|
|
1307
|
+
kind: NotebookCellDiff['kind'],
|
|
1308
|
+
trans: TranslationBundle
|
|
1309
|
+
): string {
|
|
1279
1310
|
switch (kind) {
|
|
1280
1311
|
case 'added':
|
|
1281
|
-
return 'A';
|
|
1312
|
+
return trans.__('A');
|
|
1282
1313
|
case 'removed':
|
|
1283
|
-
return 'D';
|
|
1314
|
+
return trans.__('D');
|
|
1284
1315
|
case 'modified':
|
|
1285
|
-
return 'M';
|
|
1316
|
+
return trans.__('M');
|
|
1286
1317
|
case 'unchanged':
|
|
1287
1318
|
return '·';
|
|
1288
1319
|
}
|
|
1289
1320
|
}
|
|
1290
1321
|
|
|
1291
|
-
function subdiffLabel(
|
|
1322
|
+
function subdiffLabel(
|
|
1323
|
+
kind: 'source' | 'metadata',
|
|
1324
|
+
trans: TranslationBundle
|
|
1325
|
+
): string {
|
|
1292
1326
|
switch (kind) {
|
|
1293
1327
|
case 'source':
|
|
1294
|
-
return 'Source';
|
|
1328
|
+
return trans.__('Source');
|
|
1295
1329
|
case 'metadata':
|
|
1296
|
-
return 'Metadata';
|
|
1330
|
+
return trans.__('Metadata');
|
|
1297
1331
|
}
|
|
1298
1332
|
}
|
package/src/git/tokens.ts
CHANGED
|
@@ -19,7 +19,9 @@ export interface IGitStatusFile {
|
|
|
19
19
|
is_binary: boolean | null;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Response shape of `POST /git/<path>/status`.
|
|
24
|
+
*/
|
|
23
25
|
export interface IGitStatusResult {
|
|
24
26
|
code: number;
|
|
25
27
|
branch: string | null;
|
|
@@ -61,7 +63,7 @@ export interface IGitContentResult {
|
|
|
61
63
|
* changes on top of an already-staged version. The panel models that as two
|
|
62
64
|
* separate entries, one per group.
|
|
63
65
|
*/
|
|
64
|
-
|
|
66
|
+
type FileChangeGroup = 'staged' | 'unstaged';
|
|
65
67
|
|
|
66
68
|
/**
|
|
67
69
|
* The user-facing status of a file change. Drives the single-letter badge
|
package/src/highlight/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
import { ICommandPalette } from '@jupyterlab/apputils';
|
|
6
6
|
import { CodeMirrorEditor } from '@jupyterlab/codemirror';
|
|
7
7
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
8
|
+
import { FileEditor } from '@jupyterlab/fileeditor';
|
|
8
9
|
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
9
10
|
import { StateEffect, StateField } from '@codemirror/state';
|
|
10
11
|
import { Decoration, DecorationSet, EditorView } from '@codemirror/view';
|
|
@@ -20,7 +21,9 @@ const PLUGIN_ID = 'xtralab:highlight';
|
|
|
20
21
|
const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
|
|
21
22
|
const CLEAR_HIGHLIGHTS_COMMAND = 'xtralab:clear-highlights';
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* CodeMirror line-decoration class; styled in style/highlight.css.
|
|
26
|
+
*/
|
|
24
27
|
const HIGHLIGHT_LINE_CLASS = 'jp-xtralab-highlightLine';
|
|
25
28
|
|
|
26
29
|
/**
|
|
@@ -66,15 +69,16 @@ const highlightField = StateField.define<DecorationSet>({
|
|
|
66
69
|
});
|
|
67
70
|
|
|
68
71
|
/**
|
|
69
|
-
* Return the CodeMirror view backing a widget's editor, or `null` when
|
|
70
|
-
* widget is not a CodeMirror-based text editor (a notebook, a terminal,
|
|
71
|
-
* settings editor, …).
|
|
72
|
-
* depend on `@jupyterlab/fileeditor`.
|
|
72
|
+
* Return the CodeMirror view backing a widget's file editor, or `null` when
|
|
73
|
+
* the widget is not a CodeMirror-based text editor (a notebook, a terminal,
|
|
74
|
+
* the settings editor, …).
|
|
73
75
|
*/
|
|
74
76
|
function viewForWidget(widget: Widget | null): EditorView | null {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
const content = (widget as { content?: unknown } | null)?.content;
|
|
78
|
+
return content instanceof FileEditor &&
|
|
79
|
+
content.editor instanceof CodeMirrorEditor
|
|
80
|
+
? content.editor.editor
|
|
81
|
+
: null;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
/**
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
2
|
|
|
3
3
|
import agentSessionsPlugin from './agentSessions';
|
|
4
|
+
import askAgentPlugin from './askAgent';
|
|
4
5
|
import commandBarPlugin from './commandBar';
|
|
5
6
|
import fileBrowserPlugin from './fileBrowser';
|
|
6
7
|
import fileTypeIconsPlugin from './fileTypeIcons';
|
|
@@ -30,6 +31,7 @@ import walkthroughPlugin from './walkthrough';
|
|
|
30
31
|
*/
|
|
31
32
|
const plugins: JupyterFrontEndPlugin<unknown>[] = [
|
|
32
33
|
agentSessionsPlugin,
|
|
34
|
+
askAgentPlugin,
|
|
33
35
|
commandBarPlugin,
|
|
34
36
|
editorBreadcrumbsPlugin,
|
|
35
37
|
editorIndentPlugin,
|
package/src/launcher/agents.ts
CHANGED
|
@@ -63,7 +63,12 @@ export interface IAgentSettings {
|
|
|
63
63
|
* palette. Defaults to true.
|
|
64
64
|
*/
|
|
65
65
|
enabled?: boolean;
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* See `IAgent.requireAvailable`. Defaults to true for a built-in agent that
|
|
68
|
+
* still uses its shipped command, and to false once you override the
|
|
69
|
+
* `command` (a user-chosen command — often a shell alias — is trusted and
|
|
70
|
+
* always shown). Set it explicitly to force the check on or off.
|
|
71
|
+
*/
|
|
67
72
|
requireAvailable?: boolean;
|
|
68
73
|
/**
|
|
69
74
|
* See `IAgent.promptArgs`. Pass an empty array to mark the agent as
|
|
@@ -80,10 +85,9 @@ export interface IAgentSettings {
|
|
|
80
85
|
* in `jupyter-ai-contrib/jupyter-ai-acp-client` (which is also where their
|
|
81
86
|
* icons come from), but use the bare CLI names a user would type in a
|
|
82
87
|
* terminal rather than the ACP wrapper binaries that project spawns.
|
|
83
|
-
* Antigravity (Google's agent-first IDE/CLI, `agy`)
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* out at activation time, so the wider list is harmless.
|
|
88
|
+
* Antigravity (Google's agent-first IDE/CLI, `agy`) is the Google agent.
|
|
89
|
+
* Anything not on the user's `$PATH` is filtered out at activation time, so
|
|
90
|
+
* the wider list is harmless.
|
|
87
91
|
*/
|
|
88
92
|
const DEFAULTS: IAgent[] = [
|
|
89
93
|
{
|
|
@@ -215,6 +219,11 @@ function resolveIcon(id: string, iconSvg: string | undefined): LabIcon {
|
|
|
215
219
|
* their built-in fields unless explicitly overridden; user-only entries are
|
|
216
220
|
* appended. `enabled: false` filters an entry out of the result entirely
|
|
217
221
|
* (so callers don't need to check the flag again).
|
|
222
|
+
*
|
|
223
|
+
* Overriding a built-in agent's `command` also turns its `requireAvailable`
|
|
224
|
+
* off, so the card survives the launcher's `which`-based availability filter
|
|
225
|
+
* even when the new command is a shell alias the server can't resolve. See the
|
|
226
|
+
* built-in branch below for the rationale.
|
|
218
227
|
*/
|
|
219
228
|
export function mergeAgents(overrides: IAgentSettings[]): IAgent[] {
|
|
220
229
|
const overrideById = new Map(overrides.map(entry => [entry.id, entry]));
|
|
@@ -230,16 +239,31 @@ export function mergeAgents(overrides: IAgentSettings[]): IAgent[] {
|
|
|
230
239
|
if (override.enabled === false) {
|
|
231
240
|
continue;
|
|
232
241
|
}
|
|
242
|
+
const command = override.command ?? base.command;
|
|
233
243
|
merged.push({
|
|
234
244
|
id: base.id,
|
|
235
245
|
label: override.label ?? base.label,
|
|
236
246
|
caption: override.caption ?? base.caption,
|
|
237
|
-
command
|
|
247
|
+
command,
|
|
238
248
|
icon: override.iconSvg
|
|
239
249
|
? resolveIcon(base.id, override.iconSvg)
|
|
240
250
|
: base.icon,
|
|
241
251
|
rank: override.rank ?? base.rank,
|
|
242
|
-
|
|
252
|
+
// The availability filter exists to prune xtralab's *built-in* command
|
|
253
|
+
// from the launcher when it isn't installed. Once the user points the
|
|
254
|
+
// agent at their own command — e.g. the `ccm` alias wrapping `claude
|
|
255
|
+
// --effort=max …` — the server's `shutil.which` probe can't see it
|
|
256
|
+
// (aliases and shell functions only exist inside an interactive shell),
|
|
257
|
+
// so keeping the check on would wrongly hide the card. We therefore only
|
|
258
|
+
// require availability while the command is still xtralab's default; a
|
|
259
|
+
// user-chosen command is trusted and always shown. An explicit
|
|
260
|
+
// `requireAvailable` still applies to the unchanged default command, so a
|
|
261
|
+
// user can alias `claude` itself and set `requireAvailable: false` (or
|
|
262
|
+
// force the check back on).
|
|
263
|
+
requireAvailable:
|
|
264
|
+
command === base.command
|
|
265
|
+
? (override.requireAvailable ?? base.requireAvailable)
|
|
266
|
+
: false,
|
|
243
267
|
// `null` is the explicit way to opt out of an agent's default prompt
|
|
244
268
|
// support; an absent key keeps the default. `undefined` from `??` is
|
|
245
269
|
// pruned below.
|