@workbench-kit/shell-react 0.0.2-prototype.0.2.13 → 0.0.2-prototype.0.2.15
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 +15 -0
- package/package.json +11 -8
- package/src/chat/command-error-message.ts +7 -0
- package/src/chat/command-surface.tsx +1 -8
- package/src/chat/use-chat-command-proposals.ts +1 -8
- package/src/editor/state-storage.ts +10 -21
- package/src/field-remap/convert-note-editor.tsx +11 -16
- package/src/field-remap/convert-palette.tsx +4 -5
- package/src/field-remap/flow-sync-key.ts +149 -0
- package/src/field-remap/flow.tsx +35 -19
- package/src/field-remap/samples.ts +1 -14
- package/src/field-remap/view.css +12 -1
- package/src/index.ts +8 -0
- package/src/management/keybinding-overrides-storage.ts +9 -23
- package/src/management/preference-settings-storage.ts +9 -23
- package/src/shell/chrome-labels.ts +98 -0
- package/src/shell/model.tsx +63 -1
- package/src/shell/secondary-actions.tsx +10 -3
- package/src/shell/shell.tsx +106 -12
- package/src/shell/view-host.tsx +73 -0
- package/src/storage/local-json-storage.ts +66 -0
- package/src/workbench/appearance-storage.ts +14 -27
- package/src/workbench/command-host.tsx +145 -8
- package/src/workbench/layout-storage.ts +22 -24
package/README.md
CHANGED
|
@@ -92,6 +92,21 @@ const flowActionsRef = useRef<FieldRemapFlowActions | null>(null);
|
|
|
92
92
|
/>;
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
## Shell chrome labels / `t()`
|
|
96
|
+
|
|
97
|
+
`WorkbenchShell` accepts optional `labels` and `t(key, fallback)` for high-visibility chrome
|
|
98
|
+
(ActivityBar / StatusBar aria names, Profile/Settings secondary items, command palette).
|
|
99
|
+
English defaults apply when neither is set. See
|
|
100
|
+
`resolveWorkbenchShellChromeLabels` / `workbenchShellChromeLabelKeys` and
|
|
101
|
+
[Consumer Capabilities — Shell chrome label injection](../../docs/workbench/consumer-capabilities.md#shell-chrome-label--t-injection-126).
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
<WorkbenchShell
|
|
105
|
+
t={(key, fallback) => hostTranslate(key, fallback)}
|
|
106
|
+
labels={{ settingsLabel: 'Settings' }}
|
|
107
|
+
/>
|
|
108
|
+
```
|
|
109
|
+
|
|
95
110
|
## Related docs
|
|
96
111
|
|
|
97
112
|
- [Component Map](../../docs/guides/component-map.md)
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workbench-kit/shell-react",
|
|
3
|
-
"version": "0.0.2-prototype.0.2.
|
|
3
|
+
"version": "0.0.2-prototype.0.2.15",
|
|
4
4
|
"private": false,
|
|
5
|
+
"sideEffects": [
|
|
6
|
+
"**/*.css"
|
|
7
|
+
],
|
|
5
8
|
"type": "module",
|
|
6
9
|
"exports": {
|
|
7
10
|
".": "./src/index.ts",
|
|
@@ -21,13 +24,13 @@
|
|
|
21
24
|
"@xyflow/react": "^12.11.2",
|
|
22
25
|
"jsonata": "^2.2.0",
|
|
23
26
|
"react-table-mapping": "^1.0.3",
|
|
24
|
-
"@workbench-kit/
|
|
25
|
-
"@workbench-kit/
|
|
26
|
-
"@workbench-kit/
|
|
27
|
-
"@workbench-kit/
|
|
28
|
-
"@workbench-kit/workbench-
|
|
29
|
-
"@workbench-kit/workbench-
|
|
30
|
-
"@workbench-kit/workspace": "0.0.2-prototype.0.2.
|
|
27
|
+
"@workbench-kit/platform": "0.0.2-prototype.0.2.15",
|
|
28
|
+
"@workbench-kit/field-remap": "0.0.2-prototype.0.2.15",
|
|
29
|
+
"@workbench-kit/tokens": "0.0.2-prototype.0.2.15",
|
|
30
|
+
"@workbench-kit/react": "0.0.2-prototype.0.2.15",
|
|
31
|
+
"@workbench-kit/workbench-config": "0.0.2-prototype.0.2.15",
|
|
32
|
+
"@workbench-kit/workbench-core": "0.0.2-prototype.0.2.15",
|
|
33
|
+
"@workbench-kit/workspace": "0.0.2-prototype.0.2.15"
|
|
31
34
|
},
|
|
32
35
|
"peerDependencies": {
|
|
33
36
|
"react": "^19.0.0",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type WorkbenchCommandDescriptor,
|
|
7
7
|
} from '@workbench-kit/react/workbench';
|
|
8
8
|
|
|
9
|
+
import { getCommandErrorMessage } from './command-error-message.js';
|
|
9
10
|
import { parseWorkbenchChatCommandInput } from './command-input.js';
|
|
10
11
|
import { useWorkbench } from '../shell/provider.js';
|
|
11
12
|
import { useWorkbenchCommandDescriptors } from '../commands/use-command-descriptors.js';
|
|
@@ -145,11 +146,3 @@ export function useWorkbenchChatCommandSurface({
|
|
|
145
146
|
runInputAsCommand,
|
|
146
147
|
};
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
-
function getCommandErrorMessage(error: unknown) {
|
|
150
|
-
if (error instanceof Error) {
|
|
151
|
-
return error.message;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return String(error);
|
|
155
|
-
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@workbench-kit/react/workbench';
|
|
8
8
|
import type { ChatCommandProposal, ChatMessage } from '@workbench-kit/react/workbench/chat';
|
|
9
9
|
|
|
10
|
+
import { getCommandErrorMessage } from './command-error-message.js';
|
|
10
11
|
import { type WorkbenchChatCommandRunResult } from './command-surface.js';
|
|
11
12
|
import { useWorkbench } from '../shell/provider.js';
|
|
12
13
|
|
|
@@ -24,14 +25,6 @@ export interface UseWorkbenchChatCommandProposalsOptions {
|
|
|
24
25
|
policyInput?: ResolveWorkbenchCommandExecutionPolicyInput | undefined;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
function getCommandErrorMessage(error: unknown) {
|
|
28
|
-
if (error instanceof Error) {
|
|
29
|
-
return error.message;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return String(error);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
28
|
function createProposalId(commandId: string) {
|
|
36
29
|
return `proposal-${commandId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
37
30
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createBrowserWorkbenchStorage,
|
|
3
2
|
type EditorGroupState,
|
|
4
3
|
type EditorLayoutDirection,
|
|
5
4
|
type EditorLayoutNode,
|
|
@@ -10,11 +9,16 @@ import {
|
|
|
10
9
|
} from '@workbench-kit/workbench-core';
|
|
11
10
|
|
|
12
11
|
import { isRecord } from '../is-record.js';
|
|
12
|
+
import {
|
|
13
|
+
readLocalJsonStorage,
|
|
14
|
+
resolveLocalWorkbenchStorage,
|
|
15
|
+
writeLocalJsonStorage,
|
|
16
|
+
} from '../storage/local-json-storage.js';
|
|
13
17
|
|
|
14
18
|
export const DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY = 'workbench-kit/.workbench/editors';
|
|
15
19
|
|
|
16
20
|
export function isWorkbenchEditorStatePersistenceAvailable(): boolean {
|
|
17
|
-
return
|
|
21
|
+
return resolveLocalWorkbenchStorage() !== undefined;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export function editorStateToStorageValue(state: EditorState): EditorState {
|
|
@@ -42,17 +46,7 @@ export function readPersistedEditorState(
|
|
|
42
46
|
storageKey = DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY,
|
|
43
47
|
storage?: WorkbenchStorageReader,
|
|
44
48
|
): EditorState | undefined {
|
|
45
|
-
|
|
46
|
-
if (!resolvedStorage) return undefined;
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const raw = resolvedStorage.getItem(storageKey);
|
|
50
|
-
if (!raw) return undefined;
|
|
51
|
-
|
|
52
|
-
return parseEditorStateStorageValue(JSON.parse(raw) as unknown);
|
|
53
|
-
} catch {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
49
|
+
return readLocalJsonStorage(storageKey, parseEditorStateStorageValue, () => undefined, storage);
|
|
56
50
|
}
|
|
57
51
|
|
|
58
52
|
export function writePersistedEditorState(
|
|
@@ -60,14 +54,9 @@ export function writePersistedEditorState(
|
|
|
60
54
|
storageKey = DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY,
|
|
61
55
|
storage?: WorkbenchStorageWriter,
|
|
62
56
|
): void {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
resolvedStorage.setItem(storageKey, JSON.stringify(editorStateToStorageValue(state), null, 2));
|
|
68
|
-
} catch {
|
|
69
|
-
// Ignore quota and security errors so the shell keeps working offline.
|
|
70
|
-
}
|
|
57
|
+
writeLocalJsonStorage(storageKey, state, storage, {
|
|
58
|
+
toStorageValue: editorStateToStorageValue,
|
|
59
|
+
});
|
|
71
60
|
}
|
|
72
61
|
|
|
73
62
|
function parseEditorStateStorageValue(value: unknown): EditorState | undefined {
|
|
@@ -49,10 +49,9 @@ export function ConvertNoteEditor({
|
|
|
49
49
|
|
|
50
50
|
const portTypes = useMemo(() => edgePortTypes(edge, sources, targets), [edge, sources, targets]);
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
52
|
+
// ValueTransformRegistry is intentionally mutable (`register`). Resolve its
|
|
53
|
+
// display contract on render so a host rerender sees replacement definitions.
|
|
54
|
+
const optionFields = optionFieldsForStep(transforms, transformId);
|
|
56
55
|
|
|
57
56
|
const optionValue = useMemo(() => {
|
|
58
57
|
if (!transformId) {
|
|
@@ -63,18 +62,14 @@ export function ConvertNoteEditor({
|
|
|
63
62
|
);
|
|
64
63
|
}, [chain, edge.transformOptionSteps, edge.transformOptions, stepIndex, transformId]);
|
|
65
64
|
|
|
66
|
-
const replaceCatalog =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
mode: 'replace',
|
|
75
|
-
}),
|
|
76
|
-
[edge, portTypes.sourceType, portTypes.targetType, stepIndex, transforms],
|
|
77
|
-
);
|
|
65
|
+
const replaceCatalog = listCompatibleTransforms({
|
|
66
|
+
registry: transforms,
|
|
67
|
+
edge,
|
|
68
|
+
stepIndex,
|
|
69
|
+
sourceType: portTypes.sourceType,
|
|
70
|
+
targetType: portTypes.targetType,
|
|
71
|
+
mode: 'replace',
|
|
72
|
+
});
|
|
78
73
|
|
|
79
74
|
if (!transformId) {
|
|
80
75
|
return null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
2
|
import { Button } from '@workbench-kit/react/primitives';
|
|
3
3
|
import type { ValueTransformRegistry } from '@workbench-kit/field-remap';
|
|
4
4
|
|
|
@@ -28,10 +28,9 @@ export function FieldRemapConvertPalette({
|
|
|
28
28
|
onAddSplit,
|
|
29
29
|
chromeLabels = defaultFieldRemapChromeLabels,
|
|
30
30
|
}: FieldRemapConvertPaletteProps): JSX.Element {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
31
|
+
// The registry supports replacing definitions through `register`; resolve
|
|
32
|
+
// display metadata on each host render even when the registry object is stable.
|
|
33
|
+
const catalog = transforms.list().filter((definition) => definition.id !== 'identity');
|
|
35
34
|
|
|
36
35
|
return (
|
|
37
36
|
<aside
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { Edge, Node } from '@xyflow/react';
|
|
2
|
+
import type { ValueTransformRegistry } from '@workbench-kit/field-remap';
|
|
3
|
+
|
|
4
|
+
import type { FieldRemapFlowEdgeData, FieldRemapFlowNodeData } from './flow-adapter.js';
|
|
5
|
+
import type { FieldRemapSelection } from './flow-ops.js';
|
|
6
|
+
|
|
7
|
+
type GraphSyncToken = string | number | boolean | null | undefined;
|
|
8
|
+
|
|
9
|
+
function appendToken(parts: string[], value: GraphSyncToken): void {
|
|
10
|
+
const encoded =
|
|
11
|
+
value === undefined
|
|
12
|
+
? 'undefined'
|
|
13
|
+
: value === null
|
|
14
|
+
? 'null'
|
|
15
|
+
: `${typeof value}:${String(value)}`;
|
|
16
|
+
parts.push(`${encoded.length}:${encoded}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function appendTransformRegistrySignature(
|
|
20
|
+
parts: string[],
|
|
21
|
+
transforms: ValueTransformRegistry,
|
|
22
|
+
): void {
|
|
23
|
+
const definitions = transforms
|
|
24
|
+
.list()
|
|
25
|
+
.slice()
|
|
26
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
27
|
+
appendToken(parts, definitions.length);
|
|
28
|
+
for (const definition of definitions) {
|
|
29
|
+
appendToken(parts, definition.id);
|
|
30
|
+
appendToken(parts, definition.label);
|
|
31
|
+
appendToken(parts, definition.description);
|
|
32
|
+
appendToken(parts, definition.category);
|
|
33
|
+
appendToken(parts, definition.outputType);
|
|
34
|
+
appendToken(parts, definition.inputTypes?.length ?? 0);
|
|
35
|
+
for (const inputType of definition.inputTypes ?? []) {
|
|
36
|
+
appendToken(parts, inputType);
|
|
37
|
+
}
|
|
38
|
+
appendToken(parts, definition.optionFields?.length ?? 0);
|
|
39
|
+
for (const option of definition.optionFields ?? []) {
|
|
40
|
+
appendToken(parts, option.key);
|
|
41
|
+
appendToken(parts, option.label);
|
|
42
|
+
appendToken(parts, option.kind);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createTransformRegistrySignature(transforms: ValueTransformRegistry): string {
|
|
48
|
+
const parts: string[] = [];
|
|
49
|
+
appendTransformRegistrySignature(parts, transforms);
|
|
50
|
+
return parts.join('');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function appendSelection(parts: string[], selection: FieldRemapSelection): void {
|
|
54
|
+
appendToken(parts, selection?.kind);
|
|
55
|
+
if (!selection) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (selection.kind === 'edge' || selection.kind === 'transformStep') {
|
|
59
|
+
appendToken(parts, selection.edgeId);
|
|
60
|
+
appendToken(parts, selection.kind === 'transformStep' ? selection.stepIndex : undefined);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
appendToken(parts, selection.kind === 'draft' ? selection.localId : selection.operatorId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Stable signature for the controlled values copied into XYFlow's internal node store.
|
|
68
|
+
* This intentionally lists display fields instead of serializing arbitrary props.
|
|
69
|
+
*/
|
|
70
|
+
export function createFieldRemapGraphSyncKey(input: {
|
|
71
|
+
readonly nodes: readonly Node<FieldRemapFlowNodeData>[];
|
|
72
|
+
readonly edges: readonly Edge<FieldRemapFlowEdgeData>[];
|
|
73
|
+
readonly selection: FieldRemapSelection;
|
|
74
|
+
readonly transformRegistrySignature: string;
|
|
75
|
+
}): string {
|
|
76
|
+
const parts: string[] = [];
|
|
77
|
+
appendToken(parts, input.nodes.length);
|
|
78
|
+
for (const node of input.nodes) {
|
|
79
|
+
appendToken(parts, node.id);
|
|
80
|
+
appendToken(parts, node.type);
|
|
81
|
+
appendToken(parts, node.position.x);
|
|
82
|
+
appendToken(parts, node.position.y);
|
|
83
|
+
appendToken(parts, node.selected);
|
|
84
|
+
|
|
85
|
+
const data = node.data;
|
|
86
|
+
appendToken(parts, data.kind);
|
|
87
|
+
if (data.kind === 'source-object' || data.kind === 'target-object') {
|
|
88
|
+
appendToken(parts, data.title);
|
|
89
|
+
appendToken(parts, data.schemaRole);
|
|
90
|
+
appendToken(parts, data.ports.length);
|
|
91
|
+
for (const port of data.ports) {
|
|
92
|
+
appendToken(parts, port.fieldId);
|
|
93
|
+
appendToken(parts, port.label);
|
|
94
|
+
appendToken(parts, port.dataType);
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (data.kind === 'transform') {
|
|
99
|
+
appendToken(parts, data.mappingEdgeId);
|
|
100
|
+
appendToken(parts, data.stepIndex);
|
|
101
|
+
appendToken(parts, data.transformId);
|
|
102
|
+
appendToken(parts, data.label);
|
|
103
|
+
appendToken(parts, data.selected);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (data.kind === 'draft-transform') {
|
|
107
|
+
appendToken(parts, data.localId);
|
|
108
|
+
appendToken(parts, data.transformId);
|
|
109
|
+
appendToken(parts, data.label);
|
|
110
|
+
appendToken(parts, data.sourceFieldId);
|
|
111
|
+
appendToken(parts, data.targetSlotId);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
appendToken(parts, data.operatorId);
|
|
115
|
+
appendToken(parts, data.label);
|
|
116
|
+
if (data.kind === 'combine-operator') {
|
|
117
|
+
appendToken(parts, data.inputFieldIds.length);
|
|
118
|
+
for (const fieldId of data.inputFieldIds) {
|
|
119
|
+
appendToken(parts, fieldId);
|
|
120
|
+
}
|
|
121
|
+
appendToken(parts, data.outputSlotId);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
appendToken(parts, data.inputFieldId);
|
|
125
|
+
appendToken(parts, data.outputSlotIds.length);
|
|
126
|
+
for (const slotId of data.outputSlotIds) {
|
|
127
|
+
appendToken(parts, slotId);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
appendToken(parts, input.edges.length);
|
|
132
|
+
for (const edge of input.edges) {
|
|
133
|
+
appendToken(parts, edge.id);
|
|
134
|
+
appendToken(parts, edge.type);
|
|
135
|
+
appendToken(parts, edge.source);
|
|
136
|
+
appendToken(parts, edge.sourceHandle);
|
|
137
|
+
appendToken(parts, edge.target);
|
|
138
|
+
appendToken(parts, edge.targetHandle);
|
|
139
|
+
appendToken(parts, edge.selected);
|
|
140
|
+
appendToken(parts, edge.data?.mappingEdgeId);
|
|
141
|
+
appendToken(parts, edge.data?.draftLocalId);
|
|
142
|
+
appendToken(parts, edge.data?.operatorId);
|
|
143
|
+
appendToken(parts, edge.data?.segment);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
appendSelection(parts, input.selection);
|
|
147
|
+
appendToken(parts, input.transformRegistrySignature);
|
|
148
|
+
return parts.join('');
|
|
149
|
+
}
|
package/src/field-remap/flow.tsx
CHANGED
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
type FieldRemapTargetObjectNodeData,
|
|
61
61
|
type FieldRemapTransformNodeData,
|
|
62
62
|
} from './flow-adapter.js';
|
|
63
|
+
import { createFieldRemapGraphSyncKey, createTransformRegistrySignature } from './flow-sync-key.js';
|
|
63
64
|
import {
|
|
64
65
|
addTransformStepToEdge,
|
|
65
66
|
bindDraftSource,
|
|
@@ -414,6 +415,7 @@ function FieldRemapFlowCanvas({
|
|
|
414
415
|
const first = transforms.list().find((definition) => definition.id !== 'identity');
|
|
415
416
|
return first?.id ?? '';
|
|
416
417
|
});
|
|
418
|
+
const transformRegistrySignature = createTransformRegistrySignature(transforms);
|
|
417
419
|
|
|
418
420
|
const graph = useMemo(
|
|
419
421
|
() =>
|
|
@@ -427,7 +429,17 @@ function FieldRemapFlowCanvas({
|
|
|
427
429
|
targetTitle,
|
|
428
430
|
drafts,
|
|
429
431
|
}),
|
|
430
|
-
[
|
|
432
|
+
[
|
|
433
|
+
sources,
|
|
434
|
+
targets,
|
|
435
|
+
edges,
|
|
436
|
+
transforms,
|
|
437
|
+
operators,
|
|
438
|
+
sourceTitle,
|
|
439
|
+
targetTitle,
|
|
440
|
+
drafts,
|
|
441
|
+
transformRegistrySignature,
|
|
442
|
+
],
|
|
431
443
|
);
|
|
432
444
|
|
|
433
445
|
const nodesWithSelection = useMemo(
|
|
@@ -461,22 +473,15 @@ function FieldRemapFlowCanvas({
|
|
|
461
473
|
const [nodes, setNodes, onNodesChange] = useNodesState(nodesWithSelection);
|
|
462
474
|
const [flowEdges, setFlowEdges, onFlowEdgesChange] = useEdgesState(graph.edges);
|
|
463
475
|
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
const graphSyncKey =
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
`${edge.id}:${edge.source}:${edge.target}:${edge.sourceHandle ?? ''}:${edge.targetHandle ?? ''}`,
|
|
474
|
-
),
|
|
475
|
-
selection ? JSON.stringify(selection) : '',
|
|
476
|
-
...drafts.map((draft) => draft.localId),
|
|
477
|
-
].join('|'),
|
|
478
|
-
[drafts, graph.edges, graph.nodes, selection],
|
|
479
|
-
);
|
|
476
|
+
// Depending directly on `nodesWithSelection` (a new array after each graph
|
|
477
|
+
// calculation) re-enters XYFlow's StoreUpdater. The explicit signature keeps
|
|
478
|
+
// that loop guard while still tracking every value copied into rendered nodes.
|
|
479
|
+
const graphSyncKey = createFieldRemapGraphSyncKey({
|
|
480
|
+
nodes: nodesWithSelection,
|
|
481
|
+
edges: graph.edges,
|
|
482
|
+
selection,
|
|
483
|
+
transformRegistrySignature,
|
|
484
|
+
});
|
|
480
485
|
const nodesWithSelectionRef = useRef(nodesWithSelection);
|
|
481
486
|
const graphEdgesRef = useRef(graph.edges);
|
|
482
487
|
nodesWithSelectionRef.current = nodesWithSelection;
|
|
@@ -611,7 +616,18 @@ function FieldRemapFlowCanvas({
|
|
|
611
616
|
onEdgesChange([...withoutTarget, result.edge]);
|
|
612
617
|
setSelection({ kind: 'edge', edgeId: result.edge.id });
|
|
613
618
|
},
|
|
614
|
-
[
|
|
619
|
+
[
|
|
620
|
+
connectionContext,
|
|
621
|
+
drafts,
|
|
622
|
+
edges,
|
|
623
|
+
onEdgesChange,
|
|
624
|
+
onOperatorsChange,
|
|
625
|
+
operators,
|
|
626
|
+
setSelection,
|
|
627
|
+
sources,
|
|
628
|
+
targets,
|
|
629
|
+
transforms,
|
|
630
|
+
],
|
|
615
631
|
);
|
|
616
632
|
|
|
617
633
|
const onEdgesDelete = useCallback(
|
|
@@ -694,7 +710,7 @@ function FieldRemapFlowCanvas({
|
|
|
694
710
|
stepIndex: data.stepIndex,
|
|
695
711
|
});
|
|
696
712
|
},
|
|
697
|
-
[edges, onEdgesChange, setSelection],
|
|
713
|
+
[edges, onEdgesChange, onOperatorsChange, operators, selection, setSelection],
|
|
698
714
|
);
|
|
699
715
|
|
|
700
716
|
const onKeyDown = useCallback(
|
|
@@ -31,12 +31,6 @@ export interface FieldRemapSampleDefinition {
|
|
|
31
31
|
readonly operators?: readonly MappingOperator[];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/** Alias kept so older URIs still open a known sample. */
|
|
35
|
-
export const FIELD_REMAP_LEGACY_SAMPLE_ALIASES: Readonly<Record<string, FieldRemapSampleId>> = {
|
|
36
|
-
'interactive-bindings': 'nested-ab',
|
|
37
|
-
't-order-invoice': 't-event-time',
|
|
38
|
-
};
|
|
39
|
-
|
|
40
34
|
export const FIELD_REMAP_SAMPLES: readonly FieldRemapSampleDefinition[] = [
|
|
41
35
|
{
|
|
42
36
|
id: 'nested-ab',
|
|
@@ -386,14 +380,7 @@ export function isFieldRemapSampleId(value: string): value is FieldRemapSampleId
|
|
|
386
380
|
}
|
|
387
381
|
|
|
388
382
|
export function resolveFieldRemapSampleId(value: string | undefined): FieldRemapSampleId {
|
|
389
|
-
if (
|
|
390
|
-
return 'nested-ab';
|
|
391
|
-
}
|
|
392
|
-
const aliased = FIELD_REMAP_LEGACY_SAMPLE_ALIASES[value];
|
|
393
|
-
if (aliased) {
|
|
394
|
-
return aliased;
|
|
395
|
-
}
|
|
396
|
-
if (isFieldRemapSampleId(value)) {
|
|
383
|
+
if (value && isFieldRemapSampleId(value)) {
|
|
397
384
|
return value;
|
|
398
385
|
}
|
|
399
386
|
return 'nested-ab';
|
package/src/field-remap/view.css
CHANGED
|
@@ -331,7 +331,11 @@
|
|
|
331
331
|
display: flex;
|
|
332
332
|
flex-direction: column;
|
|
333
333
|
gap: 0.65rem;
|
|
334
|
+
box-sizing: border-box;
|
|
334
335
|
min-width: 0;
|
|
336
|
+
min-height: 0;
|
|
337
|
+
height: 100%;
|
|
338
|
+
overflow: hidden;
|
|
335
339
|
padding: 0.75rem;
|
|
336
340
|
border: 1px solid var(--vscode-panel-border, var(--color-border));
|
|
337
341
|
border-radius: 0.375rem;
|
|
@@ -354,14 +358,21 @@
|
|
|
354
358
|
color: var(--vscode-descriptionForeground, var(--color-text-muted));
|
|
355
359
|
}
|
|
356
360
|
|
|
361
|
+
.workbench-field-remap-convert-palette__header,
|
|
362
|
+
.workbench-field-remap-convert-palette__place,
|
|
363
|
+
.workbench-field-remap-convert-palette__operators {
|
|
364
|
+
flex: 0 0 auto;
|
|
365
|
+
}
|
|
366
|
+
|
|
357
367
|
.workbench-field-remap-convert-palette__list {
|
|
358
368
|
list-style: none;
|
|
359
369
|
margin: 0;
|
|
360
370
|
padding: 0;
|
|
361
371
|
display: flex;
|
|
372
|
+
flex: 1 1 auto;
|
|
362
373
|
flex-direction: column;
|
|
363
374
|
gap: 0.35rem;
|
|
364
|
-
|
|
375
|
+
min-height: 0;
|
|
365
376
|
overflow: auto;
|
|
366
377
|
}
|
|
367
378
|
|
package/src/index.ts
CHANGED
|
@@ -286,6 +286,14 @@ export {
|
|
|
286
286
|
type WorkbenchSecondaryActivityItemsInput,
|
|
287
287
|
type WorkbenchSecondaryActivityRoute,
|
|
288
288
|
} from './shell/secondary-actions.js';
|
|
289
|
+
export {
|
|
290
|
+
defaultWorkbenchShellChromeLabels,
|
|
291
|
+
resolveWorkbenchShellChromeLabels,
|
|
292
|
+
workbenchShellChromeLabelKeys,
|
|
293
|
+
type WorkbenchI18n,
|
|
294
|
+
type WorkbenchShellChromeLabels,
|
|
295
|
+
type WorkbenchTranslate,
|
|
296
|
+
} from './shell/chrome-labels.js';
|
|
289
297
|
export {
|
|
290
298
|
WorkbenchProfileModal,
|
|
291
299
|
type WorkbenchProfileDetail,
|
|
@@ -3,36 +3,27 @@ import {
|
|
|
3
3
|
type WorkbenchKeybindingDefinition,
|
|
4
4
|
} from '@workbench-kit/workbench-config';
|
|
5
5
|
import {
|
|
6
|
-
createBrowserWorkbenchStorage,
|
|
7
6
|
type WorkbenchStorageReader,
|
|
8
7
|
type WorkbenchStorageWriter,
|
|
9
8
|
} from '@workbench-kit/workbench-core';
|
|
10
9
|
|
|
10
|
+
import {
|
|
11
|
+
readLocalJsonStorage,
|
|
12
|
+
resolveLocalWorkbenchStorage,
|
|
13
|
+
writeLocalJsonStorage,
|
|
14
|
+
} from '../storage/local-json-storage.js';
|
|
15
|
+
|
|
11
16
|
export const DEFAULT_WORKBENCH_KEYBINDING_STORAGE_KEY = 'workbench-kit/.workbench/keybindings';
|
|
12
17
|
|
|
13
18
|
export function isWorkbenchKeybindingPersistenceAvailable(): boolean {
|
|
14
|
-
return
|
|
19
|
+
return resolveLocalWorkbenchStorage() !== undefined;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
export function readPersistedKeybindingOverrides(
|
|
18
23
|
storageKey = DEFAULT_WORKBENCH_KEYBINDING_STORAGE_KEY,
|
|
19
24
|
storage?: WorkbenchStorageReader,
|
|
20
25
|
): readonly WorkbenchKeybindingDefinition[] {
|
|
21
|
-
|
|
22
|
-
if (!resolvedStorage) {
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const raw = resolvedStorage.getItem(storageKey);
|
|
28
|
-
if (!raw) {
|
|
29
|
-
return [];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return parseWorkbenchKeybindingsConfig(JSON.parse(raw) as unknown);
|
|
33
|
-
} catch {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
26
|
+
return readLocalJsonStorage(storageKey, parseWorkbenchKeybindingsConfig, () => [], storage);
|
|
36
27
|
}
|
|
37
28
|
|
|
38
29
|
export function writePersistedKeybindingOverrides(
|
|
@@ -40,10 +31,5 @@ export function writePersistedKeybindingOverrides(
|
|
|
40
31
|
storageKey = DEFAULT_WORKBENCH_KEYBINDING_STORAGE_KEY,
|
|
41
32
|
storage?: WorkbenchStorageWriter,
|
|
42
33
|
): void {
|
|
43
|
-
|
|
44
|
-
if (!resolvedStorage) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
resolvedStorage.setItem(storageKey, JSON.stringify(overrides, null, 2));
|
|
34
|
+
writeLocalJsonStorage(storageKey, overrides, storage, { errorMode: 'throw' });
|
|
49
35
|
}
|
|
@@ -3,37 +3,28 @@ import {
|
|
|
3
3
|
type WorkbenchSettingsConfig,
|
|
4
4
|
} from '@workbench-kit/workbench-config';
|
|
5
5
|
import {
|
|
6
|
-
createBrowserWorkbenchStorage,
|
|
7
6
|
type WorkbenchStorageReader,
|
|
8
7
|
type WorkbenchStorageWriter,
|
|
9
8
|
} from '@workbench-kit/workbench-core';
|
|
10
9
|
|
|
10
|
+
import {
|
|
11
|
+
readLocalJsonStorage,
|
|
12
|
+
resolveLocalWorkbenchStorage,
|
|
13
|
+
writeLocalJsonStorage,
|
|
14
|
+
} from '../storage/local-json-storage.js';
|
|
15
|
+
|
|
11
16
|
export const DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY =
|
|
12
17
|
'workbench-kit/.workbench/settings.local';
|
|
13
18
|
|
|
14
19
|
export function isWorkbenchLocalPreferencePersistenceAvailable(): boolean {
|
|
15
|
-
return
|
|
20
|
+
return resolveLocalWorkbenchStorage() !== undefined;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
export function readPersistedLocalPreferences(
|
|
19
24
|
storageKey = DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY,
|
|
20
25
|
storage?: WorkbenchStorageReader,
|
|
21
26
|
): WorkbenchSettingsConfig {
|
|
22
|
-
|
|
23
|
-
if (!resolvedStorage) {
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const raw = resolvedStorage.getItem(storageKey);
|
|
29
|
-
if (!raw) {
|
|
30
|
-
return {};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return parseWorkbenchSettingsConfig(JSON.parse(raw) as unknown);
|
|
34
|
-
} catch {
|
|
35
|
-
return {};
|
|
36
|
-
}
|
|
27
|
+
return readLocalJsonStorage(storageKey, parseWorkbenchSettingsConfig, () => ({}), storage);
|
|
37
28
|
}
|
|
38
29
|
|
|
39
30
|
export function writePersistedLocalPreferences(
|
|
@@ -41,10 +32,5 @@ export function writePersistedLocalPreferences(
|
|
|
41
32
|
storageKey = DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY,
|
|
42
33
|
storage?: WorkbenchStorageWriter,
|
|
43
34
|
): void {
|
|
44
|
-
|
|
45
|
-
if (!resolvedStorage) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
resolvedStorage.setItem(storageKey, JSON.stringify(values, null, 2));
|
|
35
|
+
writeLocalJsonStorage(storageKey, values, storage, { errorMode: 'throw' });
|
|
50
36
|
}
|