@workbench-kit/shell-react 0.0.2-prototype.0.2.10
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 +98 -0
- package/package.json +51 -0
- package/src/chat/ai-mock-proposals.ts +39 -0
- package/src/chat/command-input.ts +52 -0
- package/src/chat/command-policy.ts +54 -0
- package/src/chat/command-surface.tsx +155 -0
- package/src/chat/use-chat-command-proposals.ts +194 -0
- package/src/chat/view-data.ts +20 -0
- package/src/chat/view.tsx +220 -0
- package/src/commands/inspector-surface.tsx +46 -0
- package/src/commands/use-command-descriptors.ts +41 -0
- package/src/commands/use-context-key-revision.ts +18 -0
- package/src/commands/view-data.ts +17 -0
- package/src/commands/view.tsx +43 -0
- package/src/devtools/WorkbenchDevtoolsPanel.tsx +124 -0
- package/src/devtools/WorkbenchDevtoolsShell.tsx +17 -0
- package/src/devtools/index.ts +11 -0
- package/src/devtools/use-workbench-devtools-snapshot.ts +84 -0
- package/src/devtools/workbench-devtools-snapshot.ts +140 -0
- package/src/devtools/workbench-devtools.css +82 -0
- package/src/editor/area-dnd.ts +69 -0
- package/src/editor/area-model.ts +71 -0
- package/src/editor/area.css +454 -0
- package/src/editor/area.tsx +676 -0
- package/src/editor/host-surface.tsx +691 -0
- package/src/editor/markdown-document-view-data.ts +16 -0
- package/src/editor/pane-visibility.ts +130 -0
- package/src/editor/resource.ts +36 -0
- package/src/editor/state-storage.ts +182 -0
- package/src/editor/tab-context-menu.ts +118 -0
- package/src/editor/use-editor.ts +138 -0
- package/src/editor/view-providers.tsx +504 -0
- package/src/editor/workspace-file-availability.ts +15 -0
- package/src/editor/workspace-reconcile.tsx +31 -0
- package/src/explorer/context-menu.ts +114 -0
- package/src/explorer/create-command-workspace-explorer-port.ts +86 -0
- package/src/explorer/reveal.ts +112 -0
- package/src/explorer/search-view-data.ts +15 -0
- package/src/explorer/search-view.tsx +65 -0
- package/src/explorer/use-active-workspace-path.ts +15 -0
- package/src/explorer/view-data.ts +58 -0
- package/src/explorer/view.tsx +227 -0
- package/src/extensions/context-menu.ts +52 -0
- package/src/extensions/management-model.ts +313 -0
- package/src/extensions/use-extension-management.ts +241 -0
- package/src/extensions/view-data.ts +19 -0
- package/src/extensions/view.tsx +63 -0
- package/src/field-remap/chrome-labels.ts +74 -0
- package/src/field-remap/convert-note-editor.tsx +206 -0
- package/src/field-remap/convert-palette.tsx +126 -0
- package/src/field-remap/demo.tsx +105 -0
- package/src/field-remap/detail-panel.tsx +659 -0
- package/src/field-remap/editor-surface.tsx +36 -0
- package/src/field-remap/flow-adapter.ts +838 -0
- package/src/field-remap/flow-ops.ts +442 -0
- package/src/field-remap/flow.tsx +948 -0
- package/src/field-remap/graph.tsx +559 -0
- package/src/field-remap/index.ts +88 -0
- package/src/field-remap/io-class-browse.tsx +145 -0
- package/src/field-remap/jsonata-transform.ts +166 -0
- package/src/field-remap/panel.tsx +466 -0
- package/src/field-remap/samples.ts +507 -0
- package/src/field-remap/shape-io-editor.tsx +170 -0
- package/src/field-remap/table-mapping-adapter.ts +59 -0
- package/src/field-remap/transform-options-editor.tsx +312 -0
- package/src/field-remap/tree.tsx +404 -0
- package/src/field-remap/view-data.ts +16 -0
- package/src/field-remap/view.css +1367 -0
- package/src/field-remap/view.tsx +86 -0
- package/src/index.ts +294 -0
- package/src/jdw/document-view-data.ts +31 -0
- package/src/jdw/json-form-source-patch.ts +323 -0
- package/src/jdw/lab-view-data.ts +25 -0
- package/src/jdw/lab-view.css +21 -0
- package/src/jdw/lab-view.tsx +79 -0
- package/src/jdw/widget-form-view.tsx +68 -0
- package/src/jdw/widget-preview-view.tsx +54 -0
- package/src/management/keybinding-overrides-storage.ts +49 -0
- package/src/management/keybinding-settings.tsx +16 -0
- package/src/management/palette-commands.ts +38 -0
- package/src/management/preference-settings-storage.ts +50 -0
- package/src/management/settings-ids.ts +12 -0
- package/src/management/settings.tsx +96 -0
- package/src/management/use-command-management.ts +191 -0
- package/src/management/use-keybinding-management.ts +130 -0
- package/src/shell/model.tsx +149 -0
- package/src/shell/provider.tsx +606 -0
- package/src/shell/secondary-actions.tsx +59 -0
- package/src/shell/settings-constants.ts +9 -0
- package/src/shell/settings.tsx +599 -0
- package/src/shell/shell.tsx +680 -0
- package/src/shell/titlebar-layout-controls.tsx +4 -0
- package/src/shell/view-host.tsx +212 -0
- package/src/workbench/appearance-storage.ts +107 -0
- package/src/workbench/command-host.tsx +243 -0
- package/src/workbench/command-palette.ts +208 -0
- package/src/workbench/keybinding-bridge.ts +54 -0
- package/src/workbench/layout-storage.ts +134 -0
- package/src/workbench/profile-modal.tsx +118 -0
- package/src/workbench/shell-command-registration.ts +58 -0
- package/src/workbench/startup-gate.tsx +136 -0
- package/src/workbench/status-sections.ts +43 -0
- package/src/workbench/use-persisted-appearance.ts +33 -0
- package/src/workbench/user-commands.ts +46 -0
- package/src/workbench/workspace-view-state.ts +35 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-overridable chrome strings for Field Remap Flow / palette surfaces.
|
|
3
|
+
* Domain vocabulary (`MappingEdge`, binding runtime) stays unchanged.
|
|
4
|
+
*
|
|
5
|
+
* Resolution order per string: `labels[key]` → `t(capabilityId, default)` → English default.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type FieldRemapTranslate = (
|
|
9
|
+
key: string,
|
|
10
|
+
fallback: string,
|
|
11
|
+
params?: Readonly<Record<string, string | number>>,
|
|
12
|
+
) => string;
|
|
13
|
+
|
|
14
|
+
export interface FieldRemapChromeLabels {
|
|
15
|
+
/** Edge-list heading under the Flow canvas (product noun; default “Bindings”). */
|
|
16
|
+
readonly bindingsTitle: string;
|
|
17
|
+
readonly convertPaletteTitle: string;
|
|
18
|
+
readonly convertPaletteDescription: string;
|
|
19
|
+
readonly convertPaletteAriaLabel: string;
|
|
20
|
+
readonly convertsListAriaLabel: string;
|
|
21
|
+
readonly placeConvert: string;
|
|
22
|
+
readonly operatorsTitle: string;
|
|
23
|
+
readonly operatorsDescription: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const defaultFieldRemapChromeLabels: FieldRemapChromeLabels = {
|
|
27
|
+
bindingsTitle: 'Bindings',
|
|
28
|
+
convertPaletteTitle: 'Convert palette',
|
|
29
|
+
convertPaletteDescription:
|
|
30
|
+
'Place a convert first, then wire source → draft → target. Drafts stay off the document until both ports bind.',
|
|
31
|
+
convertPaletteAriaLabel: 'Convert palette',
|
|
32
|
+
convertsListAriaLabel: 'Converts',
|
|
33
|
+
placeConvert: 'Place convert',
|
|
34
|
+
operatorsTitle: 'n→m operators',
|
|
35
|
+
operatorsDescription:
|
|
36
|
+
'Create combine (n→1) or split (1→n), then wire ports or edit in the side rail.',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Stable capability ids for optional `t()` injection (not free prose). */
|
|
40
|
+
export const fieldRemapChromeLabelKeys = {
|
|
41
|
+
bindingsTitle: 'fieldRemap.bindingsTitle',
|
|
42
|
+
convertPaletteTitle: 'fieldRemap.convertPaletteTitle',
|
|
43
|
+
convertPaletteDescription: 'fieldRemap.convertPaletteDescription',
|
|
44
|
+
convertPaletteAriaLabel: 'fieldRemap.convertPaletteAriaLabel',
|
|
45
|
+
convertsListAriaLabel: 'fieldRemap.convertsListAriaLabel',
|
|
46
|
+
placeConvert: 'fieldRemap.placeConvert',
|
|
47
|
+
operatorsTitle: 'fieldRemap.operatorsTitle',
|
|
48
|
+
operatorsDescription: 'fieldRemap.operatorsDescription',
|
|
49
|
+
} as const satisfies Record<keyof FieldRemapChromeLabels, string>;
|
|
50
|
+
|
|
51
|
+
export function resolveFieldRemapChromeLabels(
|
|
52
|
+
labels?: Partial<FieldRemapChromeLabels> | undefined,
|
|
53
|
+
t?: FieldRemapTranslate | undefined,
|
|
54
|
+
): FieldRemapChromeLabels {
|
|
55
|
+
const resolve = <K extends keyof FieldRemapChromeLabels>(key: K): string => {
|
|
56
|
+
const override = labels?.[key];
|
|
57
|
+
if (override !== undefined) {
|
|
58
|
+
return override;
|
|
59
|
+
}
|
|
60
|
+
const fallback = defaultFieldRemapChromeLabels[key];
|
|
61
|
+
return t?.(fieldRemapChromeLabelKeys[key], fallback) ?? fallback;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
bindingsTitle: resolve('bindingsTitle'),
|
|
66
|
+
convertPaletteTitle: resolve('convertPaletteTitle'),
|
|
67
|
+
convertPaletteDescription: resolve('convertPaletteDescription'),
|
|
68
|
+
convertPaletteAriaLabel: resolve('convertPaletteAriaLabel'),
|
|
69
|
+
convertsListAriaLabel: resolve('convertsListAriaLabel'),
|
|
70
|
+
placeConvert: resolve('placeConvert'),
|
|
71
|
+
operatorsTitle: resolve('operatorsTitle'),
|
|
72
|
+
operatorsDescription: resolve('operatorsDescription'),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { useMemo, type JSX } from 'react';
|
|
2
|
+
import { Button } from '@workbench-kit/react/primitives';
|
|
3
|
+
import {
|
|
4
|
+
optionFieldsForStep,
|
|
5
|
+
resolveOptionSteps,
|
|
6
|
+
type MappingEdge,
|
|
7
|
+
type SourceField,
|
|
8
|
+
type TargetSlot,
|
|
9
|
+
type ValueTransformRegistry,
|
|
10
|
+
} from '@workbench-kit/field-remap';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
edgePortTypes,
|
|
14
|
+
listCompatibleTransforms,
|
|
15
|
+
removeTransformStepFromEdge,
|
|
16
|
+
replaceTransformStepOptionsOnEdge,
|
|
17
|
+
setTransformStepIdOnEdge,
|
|
18
|
+
type FieldRemapSelection,
|
|
19
|
+
} from './flow-ops.js';
|
|
20
|
+
import { TransformOptionsEditor } from './transform-options-editor.js';
|
|
21
|
+
|
|
22
|
+
export interface ConvertNoteEditorProps {
|
|
23
|
+
readonly edge: MappingEdge;
|
|
24
|
+
readonly stepIndex: number;
|
|
25
|
+
readonly sources: readonly SourceField[];
|
|
26
|
+
readonly targets: readonly TargetSlot[];
|
|
27
|
+
readonly transforms: ValueTransformRegistry;
|
|
28
|
+
readonly onEdgesChange: (edges: readonly MappingEdge[]) => void;
|
|
29
|
+
readonly edges: readonly MappingEdge[];
|
|
30
|
+
readonly onSelectionChange: (next: FieldRemapSelection) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Dedicated convert-note authoring surface for a selected `xf:*` step.
|
|
35
|
+
* Kept separate from binding / list-context chrome on the Flow detail rail.
|
|
36
|
+
*/
|
|
37
|
+
export function ConvertNoteEditor({
|
|
38
|
+
edge,
|
|
39
|
+
stepIndex,
|
|
40
|
+
sources,
|
|
41
|
+
targets,
|
|
42
|
+
transforms,
|
|
43
|
+
edges,
|
|
44
|
+
onEdgesChange,
|
|
45
|
+
onSelectionChange,
|
|
46
|
+
}: ConvertNoteEditorProps): JSX.Element | null {
|
|
47
|
+
const chain = edge.transformIds ?? [];
|
|
48
|
+
const transformId = chain[stepIndex];
|
|
49
|
+
|
|
50
|
+
const portTypes = useMemo(() => edgePortTypes(edge, sources, targets), [edge, sources, targets]);
|
|
51
|
+
|
|
52
|
+
const optionFields = useMemo(
|
|
53
|
+
() => optionFieldsForStep(transforms, transformId),
|
|
54
|
+
[transforms, transformId],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const optionValue = useMemo(() => {
|
|
58
|
+
if (!transformId) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
return (
|
|
62
|
+
resolveOptionSteps(chain, edge.transformOptionSteps, edge.transformOptions)[stepIndex] ?? {}
|
|
63
|
+
);
|
|
64
|
+
}, [chain, edge.transformOptionSteps, edge.transformOptions, stepIndex, transformId]);
|
|
65
|
+
|
|
66
|
+
const replaceCatalog = useMemo(
|
|
67
|
+
() =>
|
|
68
|
+
listCompatibleTransforms({
|
|
69
|
+
registry: transforms,
|
|
70
|
+
edge,
|
|
71
|
+
stepIndex,
|
|
72
|
+
sourceType: portTypes.sourceType,
|
|
73
|
+
targetType: portTypes.targetType,
|
|
74
|
+
mode: 'replace',
|
|
75
|
+
}),
|
|
76
|
+
[edge, portTypes.sourceType, portTypes.targetType, stepIndex, transforms],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (!transformId) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const definition = transforms.get(transformId);
|
|
84
|
+
const stepLabel = definition?.label ?? transformId;
|
|
85
|
+
|
|
86
|
+
const applyEdge = (next: MappingEdge | null) => {
|
|
87
|
+
if (!next) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
onEdgesChange(edges.map((item) => (item.id === edge.id ? next : item)));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<aside
|
|
95
|
+
className="workbench-field-remap-convert-note"
|
|
96
|
+
data-testid="field-remap-convert-note"
|
|
97
|
+
aria-label="Convert note editor"
|
|
98
|
+
>
|
|
99
|
+
<div className="workbench-field-remap-convert-note__header">
|
|
100
|
+
<div>
|
|
101
|
+
<p className="workbench-field-remap-convert-note__eyebrow">Convert</p>
|
|
102
|
+
<h4 data-testid="field-remap-convert-note-title">{stepLabel}</h4>
|
|
103
|
+
<code
|
|
104
|
+
className="workbench-field-remap-convert-note__binding"
|
|
105
|
+
data-testid="field-remap-convert-note-binding"
|
|
106
|
+
>
|
|
107
|
+
{edge.sourceFieldId} → {edge.targetSlotId}
|
|
108
|
+
</code>
|
|
109
|
+
<p className="workbench-field-remap-convert-note__muted">
|
|
110
|
+
Step {stepIndex + 1} of {chain.length} on this binding
|
|
111
|
+
</p>
|
|
112
|
+
</div>
|
|
113
|
+
<div className="workbench-field-remap-convert-note__actions">
|
|
114
|
+
<Button
|
|
115
|
+
compact
|
|
116
|
+
type="button"
|
|
117
|
+
data-testid="field-remap-convert-note-back"
|
|
118
|
+
onClick={() => onSelectionChange({ kind: 'edge', edgeId: edge.id })}
|
|
119
|
+
>
|
|
120
|
+
Binding
|
|
121
|
+
</Button>
|
|
122
|
+
<Button
|
|
123
|
+
compact
|
|
124
|
+
type="button"
|
|
125
|
+
data-testid="field-remap-convert-note-clear"
|
|
126
|
+
onClick={() => onSelectionChange(null)}
|
|
127
|
+
>
|
|
128
|
+
Close
|
|
129
|
+
</Button>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<section
|
|
134
|
+
className="workbench-field-remap-convert-note__section"
|
|
135
|
+
aria-labelledby="field-remap-convert-note-registry"
|
|
136
|
+
data-testid="field-remap-step-settings"
|
|
137
|
+
>
|
|
138
|
+
<h5 id="field-remap-convert-note-registry">Transform</h5>
|
|
139
|
+
<label className="workbench-field-remap-convert-note__field">
|
|
140
|
+
<span>Registry id</span>
|
|
141
|
+
<select
|
|
142
|
+
aria-label="Convert transform id"
|
|
143
|
+
data-testid="field-remap-step-id"
|
|
144
|
+
value={transformId}
|
|
145
|
+
onChange={(event) => {
|
|
146
|
+
applyEdge(
|
|
147
|
+
setTransformStepIdOnEdge(edge, stepIndex, event.target.value, {
|
|
148
|
+
registry: transforms,
|
|
149
|
+
sourceType: portTypes.sourceType,
|
|
150
|
+
targetType: portTypes.targetType,
|
|
151
|
+
}),
|
|
152
|
+
);
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
{!replaceCatalog.some((item) => item.id === transformId) ? (
|
|
156
|
+
<option value={transformId}>{transformId}</option>
|
|
157
|
+
) : null}
|
|
158
|
+
{replaceCatalog.map((item) => (
|
|
159
|
+
<option key={item.id} value={item.id}>
|
|
160
|
+
{item.label}
|
|
161
|
+
</option>
|
|
162
|
+
))}
|
|
163
|
+
</select>
|
|
164
|
+
</label>
|
|
165
|
+
</section>
|
|
166
|
+
|
|
167
|
+
<section
|
|
168
|
+
className="workbench-field-remap-convert-note__section"
|
|
169
|
+
aria-labelledby="field-remap-convert-note-options"
|
|
170
|
+
>
|
|
171
|
+
<h5 id="field-remap-convert-note-options">Options</h5>
|
|
172
|
+
<TransformOptionsEditor
|
|
173
|
+
fields={optionFields}
|
|
174
|
+
value={optionValue}
|
|
175
|
+
onChange={(nextOptions) => {
|
|
176
|
+
applyEdge(replaceTransformStepOptionsOnEdge(edge, stepIndex, nextOptions));
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
</section>
|
|
180
|
+
|
|
181
|
+
<div className="workbench-field-remap-convert-note__footer">
|
|
182
|
+
<Button
|
|
183
|
+
compact
|
|
184
|
+
type="button"
|
|
185
|
+
data-testid="field-remap-convert-note-remove"
|
|
186
|
+
onClick={() => {
|
|
187
|
+
const next = removeTransformStepFromEdge(edge, stepIndex);
|
|
188
|
+
applyEdge(next);
|
|
189
|
+
const nextLen = next.transformIds?.length ?? 0;
|
|
190
|
+
if (nextLen === 0) {
|
|
191
|
+
onSelectionChange({ kind: 'edge', edgeId: edge.id });
|
|
192
|
+
} else {
|
|
193
|
+
onSelectionChange({
|
|
194
|
+
kind: 'transformStep',
|
|
195
|
+
edgeId: edge.id,
|
|
196
|
+
stepIndex: Math.min(stepIndex, nextLen - 1),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
Remove convert
|
|
202
|
+
</Button>
|
|
203
|
+
</div>
|
|
204
|
+
</aside>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { useMemo, type JSX } from 'react';
|
|
2
|
+
import { Button } from '@workbench-kit/react/primitives';
|
|
3
|
+
import type { ValueTransformRegistry } from '@workbench-kit/field-remap';
|
|
4
|
+
|
|
5
|
+
import { defaultFieldRemapChromeLabels, type FieldRemapChromeLabels } from './chrome-labels.js';
|
|
6
|
+
|
|
7
|
+
export interface FieldRemapConvertPaletteProps {
|
|
8
|
+
readonly transforms: ValueTransformRegistry;
|
|
9
|
+
readonly selectedTransformId: string;
|
|
10
|
+
readonly onSelectedTransformIdChange: (transformId: string) => void;
|
|
11
|
+
readonly onPlaceDraft: (transformId: string) => void;
|
|
12
|
+
readonly onAddCombine?: (() => void) | undefined;
|
|
13
|
+
readonly onAddSplit?: (() => void) | undefined;
|
|
14
|
+
/** Resolved chrome labels (defaults when omitted). */
|
|
15
|
+
readonly chromeLabels?: FieldRemapChromeLabels | undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Primary convert chrome for Field Remap Flow: place-then-wire drafts first,
|
|
20
|
+
* with optional combine/split authoring entry points.
|
|
21
|
+
*/
|
|
22
|
+
export function FieldRemapConvertPalette({
|
|
23
|
+
transforms,
|
|
24
|
+
selectedTransformId,
|
|
25
|
+
onSelectedTransformIdChange,
|
|
26
|
+
onPlaceDraft,
|
|
27
|
+
onAddCombine,
|
|
28
|
+
onAddSplit,
|
|
29
|
+
chromeLabels = defaultFieldRemapChromeLabels,
|
|
30
|
+
}: FieldRemapConvertPaletteProps): JSX.Element {
|
|
31
|
+
const catalog = useMemo(
|
|
32
|
+
() => transforms.list().filter((definition) => definition.id !== 'identity'),
|
|
33
|
+
[transforms],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<aside
|
|
38
|
+
className="workbench-field-remap-convert-palette"
|
|
39
|
+
data-testid="field-remap-convert-palette"
|
|
40
|
+
aria-label={chromeLabels.convertPaletteAriaLabel}
|
|
41
|
+
>
|
|
42
|
+
<header className="workbench-field-remap-convert-palette__header">
|
|
43
|
+
<h3>{chromeLabels.convertPaletteTitle}</h3>
|
|
44
|
+
<p>{chromeLabels.convertPaletteDescription}</p>
|
|
45
|
+
</header>
|
|
46
|
+
|
|
47
|
+
<div className="workbench-field-remap-convert-palette__place">
|
|
48
|
+
<Button
|
|
49
|
+
type="button"
|
|
50
|
+
data-testid="field-remap-place-draft"
|
|
51
|
+
disabled={!selectedTransformId}
|
|
52
|
+
onClick={() => {
|
|
53
|
+
if (!selectedTransformId) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
onPlaceDraft(selectedTransformId);
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{chromeLabels.placeConvert}
|
|
60
|
+
</Button>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<ul
|
|
64
|
+
className="workbench-field-remap-convert-palette__list"
|
|
65
|
+
role="listbox"
|
|
66
|
+
aria-label={chromeLabels.convertsListAriaLabel}
|
|
67
|
+
>
|
|
68
|
+
{catalog.map((definition) => {
|
|
69
|
+
const selected = definition.id === selectedTransformId;
|
|
70
|
+
return (
|
|
71
|
+
<li key={definition.id}>
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
role="option"
|
|
75
|
+
aria-selected={selected}
|
|
76
|
+
className={
|
|
77
|
+
selected
|
|
78
|
+
? 'workbench-field-remap-convert-palette__item is-selected'
|
|
79
|
+
: 'workbench-field-remap-convert-palette__item'
|
|
80
|
+
}
|
|
81
|
+
data-testid={`field-remap-palette-item-${definition.id}`}
|
|
82
|
+
onClick={() => onSelectedTransformIdChange(definition.id)}
|
|
83
|
+
onDoubleClick={() => onPlaceDraft(definition.id)}
|
|
84
|
+
>
|
|
85
|
+
<strong>{definition.label}</strong>
|
|
86
|
+
<code>{definition.id}</code>
|
|
87
|
+
</button>
|
|
88
|
+
</li>
|
|
89
|
+
);
|
|
90
|
+
})}
|
|
91
|
+
</ul>
|
|
92
|
+
|
|
93
|
+
{onAddCombine || onAddSplit ? (
|
|
94
|
+
<div
|
|
95
|
+
className="workbench-field-remap-convert-palette__operators"
|
|
96
|
+
data-testid="field-remap-operator-palette"
|
|
97
|
+
>
|
|
98
|
+
<h4>{chromeLabels.operatorsTitle}</h4>
|
|
99
|
+
<p>{chromeLabels.operatorsDescription}</p>
|
|
100
|
+
<div className="workbench-field-remap-convert-palette__operator-actions">
|
|
101
|
+
{onAddCombine ? (
|
|
102
|
+
<Button
|
|
103
|
+
compact
|
|
104
|
+
type="button"
|
|
105
|
+
data-testid="field-remap-add-combine"
|
|
106
|
+
onClick={onAddCombine}
|
|
107
|
+
>
|
|
108
|
+
Add combine
|
|
109
|
+
</Button>
|
|
110
|
+
) : null}
|
|
111
|
+
{onAddSplit ? (
|
|
112
|
+
<Button
|
|
113
|
+
compact
|
|
114
|
+
type="button"
|
|
115
|
+
data-testid="field-remap-add-split"
|
|
116
|
+
onClick={onAddSplit}
|
|
117
|
+
>
|
|
118
|
+
Add split
|
|
119
|
+
</Button>
|
|
120
|
+
) : null}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
) : null}
|
|
124
|
+
</aside>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, type JSX } from 'react';
|
|
2
|
+
import { Button } from '@workbench-kit/react/primitives';
|
|
3
|
+
|
|
4
|
+
import type { FieldRemapChromeLabels, FieldRemapTranslate } from './chrome-labels.js';
|
|
5
|
+
import type { FieldRemapFlowActions } from './flow.js';
|
|
6
|
+
import type { FieldRemapIoChrome } from './io-class-browse.js';
|
|
7
|
+
import { FieldRemapPanel } from './panel.js';
|
|
8
|
+
import {
|
|
9
|
+
getFieldRemapBrowseDemoShapes,
|
|
10
|
+
getFieldRemapSample,
|
|
11
|
+
type FieldRemapSampleId,
|
|
12
|
+
} from './samples.js';
|
|
13
|
+
|
|
14
|
+
export interface SampleFieldRemapDemoProps {
|
|
15
|
+
readonly sampleId?: FieldRemapSampleId | string | undefined;
|
|
16
|
+
/** Forwarded to Flow mapper (default true). */
|
|
17
|
+
readonly showMinimap?: boolean | undefined;
|
|
18
|
+
/** When true, show host-chrome demo controls (minimap toggle + fit view). */
|
|
19
|
+
readonly showHostChromeDemo?: boolean | undefined;
|
|
20
|
+
/** Prefer `browse` for I/O class/field inspection demos. */
|
|
21
|
+
readonly ioChrome?: FieldRemapIoChrome | undefined;
|
|
22
|
+
/** When true with `ioChrome: 'browse'`, seed classRef + hidden leaf shapes. */
|
|
23
|
+
readonly browseSeedShapes?: boolean | undefined;
|
|
24
|
+
readonly labels?: Partial<FieldRemapChromeLabels> | undefined;
|
|
25
|
+
readonly t?: FieldRemapTranslate | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sample host wrapper around {@link FieldRemapPanel}.
|
|
30
|
+
*/
|
|
31
|
+
export function SampleFieldRemapDemo({
|
|
32
|
+
sampleId = 'nested-ab',
|
|
33
|
+
showMinimap: showMinimapProp,
|
|
34
|
+
showHostChromeDemo = false,
|
|
35
|
+
ioChrome,
|
|
36
|
+
browseSeedShapes = false,
|
|
37
|
+
labels,
|
|
38
|
+
t,
|
|
39
|
+
}: SampleFieldRemapDemoProps = {}): JSX.Element {
|
|
40
|
+
const sample = getFieldRemapSample(sampleId);
|
|
41
|
+
const browseShapes = browseSeedShapes ? getFieldRemapBrowseDemoShapes() : null;
|
|
42
|
+
const flowActionsRef = useRef<FieldRemapFlowActions | null>(null);
|
|
43
|
+
const [showMinimap, setShowMinimap] = useState(showMinimapProp ?? true);
|
|
44
|
+
const [lastPaneMenu, setLastPaneMenu] = useState<string | null>(null);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (showMinimapProp !== undefined) {
|
|
48
|
+
setShowMinimap(showMinimapProp);
|
|
49
|
+
}
|
|
50
|
+
}, [showMinimapProp]);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div data-testid="field-remap-demo-host">
|
|
54
|
+
{showHostChromeDemo ? (
|
|
55
|
+
<div
|
|
56
|
+
className="workbench-field-remap-demo__host-chrome"
|
|
57
|
+
data-testid="field-remap-host-chrome"
|
|
58
|
+
style={{ display: 'flex', gap: '0.5rem', marginBottom: '0.75rem', flexWrap: 'wrap' }}
|
|
59
|
+
>
|
|
60
|
+
<Button
|
|
61
|
+
compact
|
|
62
|
+
type="button"
|
|
63
|
+
data-testid="field-remap-toggle-minimap"
|
|
64
|
+
onClick={() => setShowMinimap((current) => !current)}
|
|
65
|
+
>
|
|
66
|
+
{showMinimap ? 'Hide MiniMap' : 'Show MiniMap'}
|
|
67
|
+
</Button>
|
|
68
|
+
<Button
|
|
69
|
+
compact
|
|
70
|
+
type="button"
|
|
71
|
+
data-testid="field-remap-fit-view"
|
|
72
|
+
onClick={() => flowActionsRef.current?.fitView()}
|
|
73
|
+
>
|
|
74
|
+
Fit view
|
|
75
|
+
</Button>
|
|
76
|
+
{lastPaneMenu ? (
|
|
77
|
+
<span data-testid="field-remap-pane-menu-log" role="status">
|
|
78
|
+
Pane menu: {lastPaneMenu}
|
|
79
|
+
</span>
|
|
80
|
+
) : null}
|
|
81
|
+
</div>
|
|
82
|
+
) : null}
|
|
83
|
+
<FieldRemapPanel
|
|
84
|
+
key={`${sample.id}:${ioChrome ?? 'default'}:${browseSeedShapes ? 'seed' : 'plain'}`}
|
|
85
|
+
sample={sample}
|
|
86
|
+
showMinimap={showMinimap}
|
|
87
|
+
ioChrome={ioChrome}
|
|
88
|
+
editableShapes={ioChrome === 'browse' ? false : undefined}
|
|
89
|
+
sources={browseShapes?.sources}
|
|
90
|
+
targets={browseShapes?.targets}
|
|
91
|
+
labels={labels}
|
|
92
|
+
t={t}
|
|
93
|
+
flowActionsRef={showHostChromeDemo ? flowActionsRef : undefined}
|
|
94
|
+
onPaneContextMenu={
|
|
95
|
+
showHostChromeDemo
|
|
96
|
+
? (event) => {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
setLastPaneMenu(new Date().toISOString());
|
|
99
|
+
}
|
|
100
|
+
: undefined
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|