@workbench-kit/shell-react 0.0.2-prototype.0.2.33 → 0.0.2-prototype.0.2.34
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 +32 -0
- package/package.json +9 -9
- package/src/commands/use-command-descriptors.ts +7 -3
- package/src/commands/use-extension-registry-command-descriptors.ts +23 -6
- package/src/devtools/use-workbench-devtools-snapshot.ts +39 -14
- package/src/devtools/workbench-devtools-snapshot.ts +32 -13
- package/src/editor/area.tsx +3 -2
- package/src/editor/state-storage.ts +31 -0
- package/src/editor/tab-context-menu.ts +8 -8
- package/src/editor/use-editor.ts +6 -6
- package/src/explorer/context-menu.ts +8 -4
- package/src/explorer/view.tsx +4 -3
- package/src/extensions/canonical-extension-descriptions.ts +58 -0
- package/src/extensions/context-menu.ts +9 -6
- package/src/extensions/extension-enablement-context.ts +15 -0
- package/src/extensions/extension-enablement-controller.ts +453 -0
- package/src/extensions/management-model.ts +111 -47
- package/src/extensions/theme-selection-protection.ts +98 -0
- package/src/extensions/uninstall-eligibility.ts +103 -0
- package/src/extensions/use-extension-management.ts +151 -67
- package/src/extensions/view.tsx +4 -0
- package/src/field-remap/chrome-labels.ts +58 -2
- package/src/field-remap/convert-palette.tsx +151 -6
- package/src/field-remap/demo.tsx +31 -2
- package/src/field-remap/flow.tsx +57 -13
- package/src/field-remap/history.ts +99 -0
- package/src/field-remap/index.ts +9 -1
- package/src/field-remap/panel.tsx +371 -97
- package/src/field-remap/preview-controller.ts +122 -0
- package/src/field-remap/preview.tsx +171 -0
- package/src/field-remap/view.css +72 -0
- package/src/index.ts +16 -1
- package/src/management/keybinding-overrides-storage.ts +28 -0
- package/src/management/preference-settings-storage.ts +28 -0
- package/src/management/settings.tsx +2 -0
- package/src/management/use-command-management.ts +31 -26
- package/src/management/use-keybinding-management.ts +25 -12
- package/src/shell/focused-extension-services.ts +138 -0
- package/src/shell/host-shell.tsx +13 -10
- package/src/shell/persistence-diagnostic-context.ts +11 -0
- package/src/shell/provider.tsx +373 -69
- package/src/shell/settings.tsx +25 -16
- package/src/shell/shell.tsx +119 -78
- package/src/shell/view-host.tsx +23 -22
- package/src/storage/local-json-storage.ts +43 -29
- package/src/storage/persistence-diagnostics.ts +72 -0
- package/src/workbench/appearance-storage.ts +28 -0
- package/src/workbench/command-host.tsx +19 -22
- package/src/workbench/command-palette.ts +14 -13
- package/src/workbench/layout-storage.ts +52 -5
- package/src/workbench/use-persisted-appearance.ts +38 -12
package/src/field-remap/demo.tsx
CHANGED
|
@@ -4,7 +4,11 @@ import { Button } from '@workbench-kit/react/primitives';
|
|
|
4
4
|
import type { FieldRemapChromeLabels, FieldRemapTranslate } from './chrome-labels.js';
|
|
5
5
|
import type { FieldRemapFlowActions, FieldRemapFlowMapperProps } from './flow.js';
|
|
6
6
|
import type { FieldRemapIoChrome } from './io-class-browse.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
FieldRemapPanel,
|
|
9
|
+
type FieldRemapHistoryActions,
|
|
10
|
+
type FieldRemapHistoryAvailability,
|
|
11
|
+
} from './panel.js';
|
|
8
12
|
import {
|
|
9
13
|
getFieldRemapBrowseDemoShapes,
|
|
10
14
|
getFieldRemapSample,
|
|
@@ -21,7 +25,7 @@ export interface SampleFieldRemapDemoProps {
|
|
|
21
25
|
readonly showBindingsList?: FieldRemapFlowMapperProps['showBindingsList'];
|
|
22
26
|
readonly showConvertPalette?: FieldRemapFlowMapperProps['showConvertPalette'];
|
|
23
27
|
readonly emptyDetail?: FieldRemapFlowMapperProps['emptyDetail'];
|
|
24
|
-
/** When true, show host-chrome demo controls (
|
|
28
|
+
/** When true, show host-chrome demo controls (history + fit view). */
|
|
25
29
|
readonly showHostChromeDemo?: boolean | undefined;
|
|
26
30
|
/** Prefer `browse` for I/O class/field inspection demos. */
|
|
27
31
|
readonly ioChrome?: FieldRemapIoChrome | undefined;
|
|
@@ -51,7 +55,12 @@ export function SampleFieldRemapDemo({
|
|
|
51
55
|
const sample = getFieldRemapSample(sampleId);
|
|
52
56
|
const browseShapes = browseSeedShapes ? getFieldRemapBrowseDemoShapes() : null;
|
|
53
57
|
const flowActionsRef = useRef<FieldRemapFlowActions | null>(null);
|
|
58
|
+
const historyActionsRef = useRef<FieldRemapHistoryActions | null>(null);
|
|
54
59
|
const [showMinimap, setShowMinimap] = useState(showMinimapProp ?? true);
|
|
60
|
+
const [historyAvailability, setHistoryAvailability] = useState<FieldRemapHistoryAvailability>({
|
|
61
|
+
canUndo: false,
|
|
62
|
+
canRedo: false,
|
|
63
|
+
});
|
|
55
64
|
const [lastPaneMenu, setLastPaneMenu] = useState<string | null>(null);
|
|
56
65
|
|
|
57
66
|
useEffect(() => {
|
|
@@ -76,6 +85,24 @@ export function SampleFieldRemapDemo({
|
|
|
76
85
|
>
|
|
77
86
|
Fit view
|
|
78
87
|
</Button>
|
|
88
|
+
<Button
|
|
89
|
+
compact
|
|
90
|
+
type="button"
|
|
91
|
+
data-testid="field-remap-undo"
|
|
92
|
+
disabled={!historyAvailability.canUndo}
|
|
93
|
+
onClick={() => historyActionsRef.current?.undo()}
|
|
94
|
+
>
|
|
95
|
+
Undo
|
|
96
|
+
</Button>
|
|
97
|
+
<Button
|
|
98
|
+
compact
|
|
99
|
+
type="button"
|
|
100
|
+
data-testid="field-remap-redo"
|
|
101
|
+
disabled={!historyAvailability.canRedo}
|
|
102
|
+
onClick={() => historyActionsRef.current?.redo()}
|
|
103
|
+
>
|
|
104
|
+
Redo
|
|
105
|
+
</Button>
|
|
79
106
|
{lastPaneMenu ? (
|
|
80
107
|
<span data-testid="field-remap-pane-menu-log" role="status">
|
|
81
108
|
Pane menu: {lastPaneMenu}
|
|
@@ -100,6 +127,8 @@ export function SampleFieldRemapDemo({
|
|
|
100
127
|
labels={labels}
|
|
101
128
|
t={t}
|
|
102
129
|
flowActionsRef={showHostChromeDemo ? flowActionsRef : undefined}
|
|
130
|
+
historyActionsRef={showHostChromeDemo ? historyActionsRef : undefined}
|
|
131
|
+
onHistoryAvailabilityChange={showHostChromeDemo ? setHistoryAvailability : undefined}
|
|
103
132
|
onPaneContextMenu={
|
|
104
133
|
showHostChromeDemo
|
|
105
134
|
? (event) => {
|
package/src/field-remap/flow.tsx
CHANGED
|
@@ -84,6 +84,7 @@ import {
|
|
|
84
84
|
type FieldRemapDraftTransform,
|
|
85
85
|
type FieldRemapSelection,
|
|
86
86
|
} from './flow-ops.js';
|
|
87
|
+
import { FieldRemapPreviewRail, type FieldRemapPreviewState } from './preview.js';
|
|
87
88
|
import './view.css';
|
|
88
89
|
|
|
89
90
|
function TypeBadge({ dataType }: { readonly dataType?: string }): JSX.Element | null {
|
|
@@ -349,6 +350,10 @@ export interface FieldRemapFlowMapperProps {
|
|
|
349
350
|
readonly targetTitle?: string | undefined;
|
|
350
351
|
readonly selection?: FieldRemapSelection | undefined;
|
|
351
352
|
readonly onSelectionChange?: ((next: FieldRemapSelection) => void) | undefined;
|
|
353
|
+
/** Precomputed runtime-only preview. Flow never evaluates mappings itself. */
|
|
354
|
+
readonly preview?: FieldRemapPreviewState;
|
|
355
|
+
/** Explicitly hide an injected preview without reserving a splitter track. */
|
|
356
|
+
readonly showPreview?: boolean;
|
|
352
357
|
/**
|
|
353
358
|
* When false, MiniMap is not mounted (not CSS-hidden). Defaults to true for
|
|
354
359
|
* backward compatibility with existing samples.
|
|
@@ -421,36 +426,58 @@ function FieldRemapSplitWorkspace({
|
|
|
421
426
|
}: FieldRemapSplitWorkspaceProps): JSX.Element {
|
|
422
427
|
const [palette, canvas, detail] = Children.toArray(children);
|
|
423
428
|
const isNarrow = layout === 'narrow';
|
|
424
|
-
const
|
|
429
|
+
const [paletteSizeByLayout, setPaletteSizeByLayout] = useState({
|
|
430
|
+
wide: 240,
|
|
431
|
+
medium: 240,
|
|
432
|
+
narrow: 192,
|
|
433
|
+
});
|
|
434
|
+
const [detailSizeByLayout, setDetailSizeByLayout] = useState({
|
|
435
|
+
wide: 320,
|
|
436
|
+
medium: 320,
|
|
437
|
+
narrow: 220,
|
|
438
|
+
});
|
|
439
|
+
const paletteSizePx = paletteSizeByLayout[layout];
|
|
440
|
+
const detailSizePx = detailSizeByLayout[layout];
|
|
441
|
+
const canvasWithDetail = (
|
|
425
442
|
<SplitView
|
|
426
|
-
className=
|
|
427
|
-
|
|
443
|
+
className={
|
|
444
|
+
showDetail
|
|
445
|
+
? 'workbench-field-remap-flow__canvas-detail-split'
|
|
446
|
+
: 'workbench-field-remap-flow__canvas-detail-split ui-workbench-split-view--secondary-collapsed'
|
|
447
|
+
}
|
|
428
448
|
layoutMode="secondary-fixed"
|
|
429
449
|
maxSecondarySizePx={isNarrow ? 320 : 480}
|
|
430
450
|
minPrimarySizePx={isNarrow ? 200 : 280}
|
|
431
451
|
minSecondarySizePx={isNarrow ? 160 : 256}
|
|
452
|
+
onSecondarySizePxChange={(nextSize) => {
|
|
453
|
+
setDetailSizeByLayout((current) => ({ ...current, [layout]: nextSize }));
|
|
454
|
+
}}
|
|
432
455
|
orientation={isNarrow ? 'vertical' : 'horizontal'}
|
|
433
456
|
primary={canvas}
|
|
434
457
|
secondary={detail}
|
|
458
|
+
secondarySizePx={detailSizePx}
|
|
435
459
|
/>
|
|
436
|
-
) : (
|
|
437
|
-
canvas
|
|
438
460
|
);
|
|
439
461
|
|
|
440
|
-
const content =
|
|
462
|
+
const content = (
|
|
441
463
|
<SplitView
|
|
442
|
-
className=
|
|
443
|
-
|
|
464
|
+
className={
|
|
465
|
+
showConvertPalette
|
|
466
|
+
? 'workbench-field-remap-flow__palette-split'
|
|
467
|
+
: 'workbench-field-remap-flow__palette-split ui-workbench-split-view--primary-collapsed'
|
|
468
|
+
}
|
|
444
469
|
maxPrimarySizePx={isNarrow ? 288 : 320}
|
|
445
470
|
minPrimarySizePx={isNarrow ? 160 : 192}
|
|
446
471
|
minSecondarySizePx={isNarrow ? 200 : 280}
|
|
472
|
+
onPrimarySizePxChange={(nextSize) => {
|
|
473
|
+
setPaletteSizeByLayout((current) => ({ ...current, [layout]: nextSize }));
|
|
474
|
+
}}
|
|
447
475
|
orientation={layout === 'wide' ? 'horizontal' : 'vertical'}
|
|
448
476
|
primary={palette}
|
|
477
|
+
primarySizePx={paletteSizePx}
|
|
449
478
|
primarySizeUnit="pixels"
|
|
450
479
|
secondary={canvasWithDetail}
|
|
451
480
|
/>
|
|
452
|
-
) : (
|
|
453
|
-
canvasWithDetail
|
|
454
481
|
);
|
|
455
482
|
|
|
456
483
|
return (
|
|
@@ -486,6 +513,8 @@ function FieldRemapFlowCanvas({
|
|
|
486
513
|
targetTitle,
|
|
487
514
|
selection: selectionProp,
|
|
488
515
|
onSelectionChange: onSelectionChangeProp,
|
|
516
|
+
preview,
|
|
517
|
+
showPreview = true,
|
|
489
518
|
showMinimap = true,
|
|
490
519
|
onShowMinimapChange,
|
|
491
520
|
includeHidden = false,
|
|
@@ -509,6 +538,7 @@ function FieldRemapFlowCanvas({
|
|
|
509
538
|
const [internalSelection, setInternalSelection] = useState<FieldRemapSelection>(null);
|
|
510
539
|
const selection = selectionProp !== undefined ? selectionProp : internalSelection;
|
|
511
540
|
const setSelection = onSelectionChangeProp ?? setInternalSelection;
|
|
541
|
+
const previewVisible = showPreview && preview !== undefined && preview.status !== 'unavailable';
|
|
512
542
|
const [drafts, setDrafts] = useState<readonly FieldRemapDraftTransform[]>([]);
|
|
513
543
|
const [placeTransformId, setPlaceTransformId] = useState(() => {
|
|
514
544
|
const first = transforms.list().find((definition) => definition.id !== 'identity');
|
|
@@ -878,6 +908,7 @@ function FieldRemapFlowCanvas({
|
|
|
878
908
|
data-empty-detail={emptyDetail}
|
|
879
909
|
data-minimap={showMinimap ? 'on' : 'off'}
|
|
880
910
|
data-hidden-fields={includeHidden ? 'on' : 'off'}
|
|
911
|
+
data-preview={previewVisible ? 'on' : 'off'}
|
|
881
912
|
onKeyDown={onKeyDown}
|
|
882
913
|
>
|
|
883
914
|
{showFlowHint ? (
|
|
@@ -892,7 +923,7 @@ function FieldRemapFlowCanvas({
|
|
|
892
923
|
<FieldRemapSplitWorkspace
|
|
893
924
|
layout={workspaceLayout}
|
|
894
925
|
showConvertPalette={showConvertPalette}
|
|
895
|
-
showDetail={emptyDetail === 'hint' || selection !== null}
|
|
926
|
+
showDetail={emptyDetail === 'hint' || selection !== null || previewVisible}
|
|
896
927
|
surface={
|
|
897
928
|
selection?.kind === 'transformStep'
|
|
898
929
|
? 'convert-note'
|
|
@@ -1062,7 +1093,7 @@ function FieldRemapFlowCanvas({
|
|
|
1062
1093
|
</ReactFlow>
|
|
1063
1094
|
</div>
|
|
1064
1095
|
|
|
1065
|
-
|
|
1096
|
+
<div className="workbench-field-remap-flow__side-rail">
|
|
1066
1097
|
{emptyDetail === 'hint' || selection !== null ? (
|
|
1067
1098
|
<FieldRemapDetailPanel
|
|
1068
1099
|
selection={selection}
|
|
@@ -1082,7 +1113,20 @@ function FieldRemapFlowCanvas({
|
|
|
1082
1113
|
emptyDetailDescription={chromeLabels.emptyDetailDescription}
|
|
1083
1114
|
/>
|
|
1084
1115
|
) : null}
|
|
1085
|
-
|
|
1116
|
+
{previewVisible && preview ? (
|
|
1117
|
+
<FieldRemapPreviewRail
|
|
1118
|
+
preview={preview}
|
|
1119
|
+
selection={selection}
|
|
1120
|
+
edges={edges}
|
|
1121
|
+
operatorExists={
|
|
1122
|
+
selection?.kind === 'operator'
|
|
1123
|
+
? operators.some((operator) => operator.id === selection.operatorId)
|
|
1124
|
+
: false
|
|
1125
|
+
}
|
|
1126
|
+
labels={chromeLabels}
|
|
1127
|
+
/>
|
|
1128
|
+
) : null}
|
|
1129
|
+
</div>
|
|
1086
1130
|
</FieldRemapSplitWorkspace>
|
|
1087
1131
|
|
|
1088
1132
|
{showBindingsList ? (
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { MappingEdge, MappingOperator } from '@workbench-kit/field-remap';
|
|
2
|
+
|
|
3
|
+
export interface FieldRemapHistorySnapshot {
|
|
4
|
+
readonly edges: readonly MappingEdge[];
|
|
5
|
+
readonly operators: readonly MappingOperator[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface FieldRemapHistoryState {
|
|
9
|
+
readonly past: readonly FieldRemapHistorySnapshot[];
|
|
10
|
+
readonly future: readonly FieldRemapHistorySnapshot[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const FIELD_REMAP_HISTORY_LIMIT = 100;
|
|
14
|
+
|
|
15
|
+
export function createFieldRemapHistorySnapshot(
|
|
16
|
+
edges: readonly MappingEdge[],
|
|
17
|
+
operators: readonly MappingOperator[],
|
|
18
|
+
): FieldRemapHistorySnapshot {
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
edges: Object.freeze([...edges]),
|
|
21
|
+
operators: Object.freeze([...operators]),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createFieldRemapHistoryState(): FieldRemapHistoryState {
|
|
26
|
+
return { past: [], future: [] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function sameItems<T>(left: readonly T[], right: readonly T[]): boolean {
|
|
30
|
+
return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function areFieldRemapHistorySnapshotsEqual(
|
|
34
|
+
left: FieldRemapHistorySnapshot,
|
|
35
|
+
right: FieldRemapHistorySnapshot,
|
|
36
|
+
): boolean {
|
|
37
|
+
return sameItems(left.edges, right.edges) && sameItems(left.operators, right.operators);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function appendBounded(
|
|
41
|
+
snapshots: readonly FieldRemapHistorySnapshot[],
|
|
42
|
+
snapshot: FieldRemapHistorySnapshot,
|
|
43
|
+
): readonly FieldRemapHistorySnapshot[] {
|
|
44
|
+
return [...snapshots.slice(-(FIELD_REMAP_HISTORY_LIMIT - 1)), snapshot];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function recordFieldRemapHistory(
|
|
48
|
+
state: FieldRemapHistoryState,
|
|
49
|
+
current: FieldRemapHistorySnapshot,
|
|
50
|
+
next: FieldRemapHistorySnapshot,
|
|
51
|
+
): FieldRemapHistoryState {
|
|
52
|
+
if (areFieldRemapHistorySnapshotsEqual(current, next)) {
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
past: appendBounded(
|
|
57
|
+
state.past,
|
|
58
|
+
createFieldRemapHistorySnapshot(current.edges, current.operators),
|
|
59
|
+
),
|
|
60
|
+
future: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function undoFieldRemapHistory(
|
|
65
|
+
state: FieldRemapHistoryState,
|
|
66
|
+
current: FieldRemapHistorySnapshot,
|
|
67
|
+
): { readonly state: FieldRemapHistoryState; readonly snapshot: FieldRemapHistorySnapshot } | null {
|
|
68
|
+
const snapshot = state.past[state.past.length - 1];
|
|
69
|
+
if (!snapshot) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
state: {
|
|
74
|
+
past: state.past.slice(0, -1),
|
|
75
|
+
future: [createFieldRemapHistorySnapshot(current.edges, current.operators), ...state.future],
|
|
76
|
+
},
|
|
77
|
+
snapshot,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function redoFieldRemapHistory(
|
|
82
|
+
state: FieldRemapHistoryState,
|
|
83
|
+
current: FieldRemapHistorySnapshot,
|
|
84
|
+
): { readonly state: FieldRemapHistoryState; readonly snapshot: FieldRemapHistorySnapshot } | null {
|
|
85
|
+
const snapshot = state.future[0];
|
|
86
|
+
if (!snapshot) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
state: {
|
|
91
|
+
past: appendBounded(
|
|
92
|
+
state.past,
|
|
93
|
+
createFieldRemapHistorySnapshot(current.edges, current.operators),
|
|
94
|
+
),
|
|
95
|
+
future: state.future.slice(1),
|
|
96
|
+
},
|
|
97
|
+
snapshot,
|
|
98
|
+
};
|
|
99
|
+
}
|
package/src/field-remap/index.ts
CHANGED
|
@@ -6,12 +6,20 @@
|
|
|
6
6
|
* (Flow also side-imports the same stylesheet).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export {
|
|
9
|
+
export {
|
|
10
|
+
FieldRemapPanel,
|
|
11
|
+
type FieldRemapHistoryActions,
|
|
12
|
+
type FieldRemapHistoryAvailability,
|
|
13
|
+
type FieldRemapHistoryOwner,
|
|
14
|
+
type FieldRemapHistorySnapshot,
|
|
15
|
+
type FieldRemapPanelProps,
|
|
16
|
+
} from './panel.js';
|
|
10
17
|
export {
|
|
11
18
|
FieldRemapFlowMapper,
|
|
12
19
|
type FieldRemapFlowActions,
|
|
13
20
|
type FieldRemapFlowMapperProps,
|
|
14
21
|
} from './flow.js';
|
|
22
|
+
export type { FieldRemapPreviewState } from './preview.js';
|
|
15
23
|
export {
|
|
16
24
|
defaultFieldRemapChromeLabels,
|
|
17
25
|
fieldRemapChromeLabelKeys,
|