@workbench-kit/shell-react 0.0.2-prototype.0.2.41 → 0.0.2-prototype.0.2.44
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 +81 -2
- package/package.json +14 -10
- package/src/editor/workspace-reconcile.tsx +12 -7
- package/src/extensions/extension-enablement-controller.ts +40 -9
- package/src/extensions/theme-selection-protection.ts +80 -55
- package/src/field-remap/chrome-labels.ts +117 -0
- package/src/field-remap/convert-note-editor.tsx +119 -104
- package/src/field-remap/convert-palette.tsx +6 -0
- package/src/field-remap/demo.tsx +8 -2
- package/src/field-remap/detail-panel.tsx +535 -434
- package/src/field-remap/document-io.tsx +299 -0
- package/src/field-remap/drag-payload.ts +48 -0
- package/src/field-remap/flow-adapter.ts +216 -88
- package/src/field-remap/flow-ops.ts +150 -0
- package/src/field-remap/flow.tsx +1227 -272
- package/src/field-remap/index.ts +2 -0
- package/src/field-remap/io-class-browse.tsx +34 -22
- package/src/field-remap/keyboard.ts +16 -0
- package/src/field-remap/modal-detail.tsx +34 -0
- package/src/field-remap/panel.tsx +110 -14
- package/src/field-remap/transform-options-editor.tsx +68 -48
- package/src/field-remap/view.css +107 -189
- package/src/index.ts +7 -0
- package/src/keybinding-management-settings.ts +4 -0
- package/src/management/keybinding-overrides-storage.ts +48 -23
- package/src/management/keybinding-settings-view.tsx +31 -0
- package/src/management/keybinding-settings.tsx +4 -12
- package/src/management/use-keybinding-management.ts +72 -22
- package/src/shell/appearance-catalog.ts +453 -0
- package/src/shell/appearance-controller.ts +331 -0
- package/src/shell/appearance-presentation.ts +269 -0
- package/src/shell/provider.tsx +157 -19
- package/src/shell/settings.tsx +282 -153
- package/src/shell/shell.tsx +88 -18
- package/src/workbench/appearance-storage.ts +2 -2
- package/src/workbench/command-host-controller.tsx +223 -0
- package/src/workbench/command-host.tsx +100 -150
- package/src/workbench/keybinding-bridge.ts +84 -38
- package/src/workbench/shell-command-registration.ts +27 -2
package/README.md
CHANGED
|
@@ -51,6 +51,28 @@ registry can use `registry-command-descriptors` without importing Provider
|
|
|
51
51
|
context into that leaf bundle. The root barrel remains the discovery surface,
|
|
52
52
|
not the default runtime import graph.
|
|
53
53
|
|
|
54
|
+
### Provider-free command host
|
|
55
|
+
|
|
56
|
+
Hosts that already own command descriptors and execution can compose the canonical
|
|
57
|
+
Command Palette and Quick Open without `WorkbenchProvider`:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { WorkbenchCommandHostController } from '@workbench-kit/shell-react/command-host-controller';
|
|
61
|
+
|
|
62
|
+
<WorkbenchCommandHostController
|
|
63
|
+
commands={commands}
|
|
64
|
+
executeCommand={executeCommand}
|
|
65
|
+
quickOpenProviders={quickOpenProviders}
|
|
66
|
+
/>;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`commands` is the complete palette descriptor set. The controller owns only overlay
|
|
70
|
+
state, hard Palette/Quick Open shortcuts, selection routing, and completion-driven
|
|
71
|
+
closing. Hosts continue to own command registration, descriptor projection, Quick Open
|
|
72
|
+
provider construction, persistence, and error reporting. Pass `shortcutBridge` only
|
|
73
|
+
when the host also wants the generic keybinding bridge; omit it or pass `false` to keep
|
|
74
|
+
that routing host-owned.
|
|
75
|
+
|
|
54
76
|
### Focused extension context migration
|
|
55
77
|
|
|
56
78
|
`WorkbenchContextValue` no longer exposes the aggregate `ExtensionRegistry`.
|
|
@@ -98,6 +120,7 @@ import {
|
|
|
98
120
|
FieldRemapFlowMapper,
|
|
99
121
|
createJsonataValueTransform,
|
|
100
122
|
} from '@workbench-kit/shell-react/field-remap';
|
|
123
|
+
import type { FieldRemapDocument } from '@workbench-kit/field-remap';
|
|
101
124
|
import '@workbench-kit/shell-react/field-remap/view.css';
|
|
102
125
|
|
|
103
126
|
// Uncontrolled demo:
|
|
@@ -111,14 +134,63 @@ import '@workbench-kit/shell-react/field-remap/view.css';
|
|
|
111
134
|
for Flow-only embeds and custom bundler setups. The full barrel
|
|
112
135
|
`import { FieldRemapPanel } from '@workbench-kit/shell-react'` stays supported.
|
|
113
136
|
|
|
137
|
+
### n→m operators in direct Flow embeds
|
|
138
|
+
|
|
139
|
+
`FieldRemapFlowMapper` keeps durable edges and document-v2 combine/split operators
|
|
140
|
+
controlled. A host that enables operator authoring owns both arrays and commits each
|
|
141
|
+
complete next operator array through the same persistence/history boundary:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const [document, setDocument] = useState(initialDocument);
|
|
145
|
+
const commitDocumentChange = (next: FieldRemapDocument) => {
|
|
146
|
+
setDocument(next);
|
|
147
|
+
hostHistory.record(next);
|
|
148
|
+
persist(next);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
<FieldRemapFlowMapper
|
|
152
|
+
sources={sources}
|
|
153
|
+
targets={targets}
|
|
154
|
+
transforms={registry}
|
|
155
|
+
edges={document.edges}
|
|
156
|
+
onEdgesChange={(next) => commitDocumentChange({ ...document, edges: next })}
|
|
157
|
+
operators={document.operators ?? []}
|
|
158
|
+
onOperatorsChange={(next) => commitDocumentChange({ ...document, operators: next })}
|
|
159
|
+
/>;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The presence of `onOperatorsChange` is the operator-authoring capability signal: it
|
|
163
|
+
enables the existing Add combine / Add split actions and routes operator wiring, detail,
|
|
164
|
+
and deletion mutations through that callback. Omitting the callback is intentional
|
|
165
|
+
inspect-only projection; supplied operators can still render and be selected, but operator
|
|
166
|
+
mutation chrome is absent. Do not infer writability from operator count, sample, chrome, or
|
|
167
|
+
labels. `readOnly` suppresses Flow authoring even when mutation callbacks are present.
|
|
168
|
+
|
|
169
|
+
`FieldRemapPanel` already supplies this wiring for its fully uncontrolled composite
|
|
170
|
+
`{ edges, operators }` state. If either durable channel is controlled, its existing
|
|
171
|
+
composite `historyOwner` contract applies as described below; consumers do not add a second
|
|
172
|
+
operator state layer merely to enable Panel authoring. Direct operator inventory drag/drop
|
|
173
|
+
and double-click placement are deferred to [#219](https://github.com/NewChoBo/workbench-kit/issues/219).
|
|
174
|
+
|
|
114
175
|
### Semantic history ownership
|
|
115
176
|
|
|
116
177
|
`FieldRemapPanel` keeps a private composite `{ edges, operators }` undo/redo stack only
|
|
117
178
|
when both durable channels are uncontrolled. If either channel is controlled, pass one
|
|
118
179
|
`historyOwner` for the complete composite state; the Panel never creates a partial stack.
|
|
119
180
|
`historyActionsRef` exposes host-chrome actions and
|
|
120
|
-
`onHistoryAvailabilityChange` reports whether those actions are available.
|
|
121
|
-
|
|
181
|
+
`onHistoryAvailabilityChange` reports whether those actions are available. The Panel routes
|
|
182
|
+
available undo/redo chords through that same owner; direct `FieldRemapFlowMapper` consumers
|
|
183
|
+
remain responsible for host-owned history routing.
|
|
184
|
+
|
|
185
|
+
| Surface | Shortcut | Behavior |
|
|
186
|
+
| ------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
187
|
+
| Flow | `Escape` | Clears the active selection and unfinished drafts. If collapsed detail chrome owned focus, focus moves to the programmatic-only mapper root. |
|
|
188
|
+
| Flow | `Delete` / `Backspace` | Removes the eligible selected edge, convert step, operator, or draft through the existing mutation path. |
|
|
189
|
+
| Panel | `Ctrl/Cmd+Z` | Invokes the existing composite history owner's available undo action. |
|
|
190
|
+
| Panel | `Ctrl/Cmd+Shift+Z`, `Ctrl+Y` | Invokes the existing composite history owner's available redo action. |
|
|
191
|
+
|
|
192
|
+
Editable inputs, textareas, selects, contenteditable elements, and transform option editors
|
|
193
|
+
retain their native key behavior. Unavailable or read-only actions leave the event unconsumed.
|
|
122
194
|
|
|
123
195
|
Only semantic edits coming from the Flow mapper create entries. Hidden mappings are
|
|
124
196
|
reconstructed before an entry is recorded, so undo does not discard filtered state.
|
|
@@ -140,6 +212,13 @@ hooks so hosts avoid CSS/DOM workarounds:
|
|
|
140
212
|
| `flowActionsRef` | `{ fitView(options?) }` using the same defaults as Controls fit-view. |
|
|
141
213
|
| `labels` / `t` | Override edge-list / Convert palette chrome (e.g. “Field maps”). |
|
|
142
214
|
| `ioChrome` (Panel) | `'browse' \| 'edit' \| 'none'` — prefer browse for inspect-only I/O. |
|
|
215
|
+
| `rewirePolicy` (Flow) | `'replace'` by default; `'reject'` preserves prior edges and reports impacted edge IDs. |
|
|
216
|
+
| `onConnectionFeedback` (Flow) | Receives one structured result at connection-attempt completion; hover validation stays silent. |
|
|
217
|
+
| `parentChildConflicts` (Flow) | Optional authoritative conflict projection; `undefined` derives from the supplied Flow inputs. |
|
|
218
|
+
|
|
219
|
+
Rejected attempts render one compact `role="status"` message. Standalone Flow embeds derive
|
|
220
|
+
parent/child conflicts with the domain detector; Panel computes the same conflicts from full shapes
|
|
221
|
+
before hidden-field projection and supplies that authoritative result, so it renders only once.
|
|
143
222
|
|
|
144
223
|
```tsx
|
|
145
224
|
const flowActionsRef = useRef<FieldRemapFlowActions | null>(null);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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.44",
|
|
4
4
|
"private": false,
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
"./command-descriptors": "./src/commands/use-command-descriptors.ts",
|
|
12
12
|
"./registry-command-descriptors": "./src/commands/use-extension-registry-command-descriptors.ts",
|
|
13
13
|
"./command-host": "./src/workbench/command-host.tsx",
|
|
14
|
+
"./command-host-controller": "./src/workbench/command-host-controller.tsx",
|
|
14
15
|
"./command-palette": "./src/workbench/command-palette.ts",
|
|
15
16
|
"./field-remap": "./src/field-remap/index.ts",
|
|
16
17
|
"./field-remap/view.css": "./src/field-remap/view.css",
|
|
17
18
|
"./host-shell": "./src/shell/host-shell.tsx",
|
|
19
|
+
"./keybinding-management-settings": "./src/keybinding-management-settings.ts",
|
|
18
20
|
"./layout-storage": "./src/workbench/layout-storage.ts",
|
|
19
21
|
"./provider": "./src/shell/provider.tsx",
|
|
20
22
|
"./shell": "./src/shell/shell.tsx"
|
|
@@ -31,14 +33,14 @@
|
|
|
31
33
|
"@radix-ui/react-slot": "^1.3.0",
|
|
32
34
|
"@xyflow/react": "^12.11.2",
|
|
33
35
|
"jsonata": "^2.2.0",
|
|
34
|
-
"@workbench-kit/platform": "0.0.2-prototype.0.2.
|
|
35
|
-
"@workbench-kit/field-remap": "0.0.2-prototype.0.2.
|
|
36
|
-
"@workbench-kit/
|
|
37
|
-
"@workbench-kit/
|
|
38
|
-
"@workbench-kit/
|
|
39
|
-
"@workbench-kit/workbench-core": "0.0.2-prototype.0.2.
|
|
40
|
-
"@workbench-kit/workspace": "0.0.2-prototype.0.2.
|
|
41
|
-
"@workbench-kit/
|
|
36
|
+
"@workbench-kit/platform": "0.0.2-prototype.0.2.44",
|
|
37
|
+
"@workbench-kit/field-remap": "0.0.2-prototype.0.2.44",
|
|
38
|
+
"@workbench-kit/tokens": "0.0.2-prototype.0.2.44",
|
|
39
|
+
"@workbench-kit/react": "0.0.2-prototype.0.2.44",
|
|
40
|
+
"@workbench-kit/workbench-config": "0.0.2-prototype.0.2.44",
|
|
41
|
+
"@workbench-kit/workbench-core": "0.0.2-prototype.0.2.44",
|
|
42
|
+
"@workbench-kit/workspace": "0.0.2-prototype.0.2.44",
|
|
43
|
+
"@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.44"
|
|
42
44
|
},
|
|
43
45
|
"peerDependencies": {
|
|
44
46
|
"react": "^19.0.0",
|
|
@@ -57,6 +59,8 @@
|
|
|
57
59
|
},
|
|
58
60
|
"scripts": {
|
|
59
61
|
"test": "vitest run --config vitest.config.ts src",
|
|
60
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
62
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
63
|
+
"typecheck:exact-optional": "pnpm --filter @workbench-kit/react exec tsc -p typecheck-exact-optional/tsconfig.emit.json && pnpm typecheck:exact-optional:consumer",
|
|
64
|
+
"typecheck:exact-optional:consumer": "tsc -p typecheck-exact-optional/tsconfig.emit.json && tsc -p typecheck-exact-optional/tsconfig.json --noEmit"
|
|
61
65
|
}
|
|
62
66
|
}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
+
import type { EditorService } from '@workbench-kit/workbench-core';
|
|
2
3
|
|
|
3
4
|
import { createWorkspaceFileAvailabilityChecker } from './workspace-file-availability.js';
|
|
4
|
-
import { useEditorService } from './use-editor.js';
|
|
5
|
-
import { useWorkbench } from '../shell/provider.js';
|
|
6
5
|
import {
|
|
7
6
|
isWorkspaceResourceService,
|
|
8
7
|
useWorkspaceResourceState,
|
|
9
8
|
} from '../workbench/workspace-view-state.js';
|
|
10
9
|
|
|
10
|
+
interface EditorWorkspaceReconcilerProps {
|
|
11
|
+
readonly editorService: EditorService;
|
|
12
|
+
readonly workspaceHostService?: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
/** Component-only module so Vite Fast Refresh can accept this boundary. */
|
|
12
|
-
export function EditorWorkspaceReconciler(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
export function EditorWorkspaceReconciler({
|
|
17
|
+
editorService,
|
|
18
|
+
workspaceHostService,
|
|
19
|
+
}: EditorWorkspaceReconcilerProps): null {
|
|
20
|
+
const workspaceService = isWorkspaceResourceService(workspaceHostService)
|
|
21
|
+
? workspaceHostService
|
|
17
22
|
: undefined;
|
|
18
23
|
const workspaceState = useWorkspaceResourceState(workspaceService);
|
|
19
24
|
|
|
@@ -76,7 +76,11 @@ export class ExtensionEnablementController implements DisposableLike {
|
|
|
76
76
|
private readonly storage: WorkbenchStorageAdapter | undefined;
|
|
77
77
|
private readonly storageKey: string;
|
|
78
78
|
private installedRecords: readonly InstalledExtensionRecord[];
|
|
79
|
-
private
|
|
79
|
+
private readonly themeSelectionProtectionOwners = new Map<
|
|
80
|
+
number,
|
|
81
|
+
ThemeSelectionProtectionSnapshot
|
|
82
|
+
>();
|
|
83
|
+
private themeSelectionProtectionGeneration = 0;
|
|
80
84
|
private disposed = false;
|
|
81
85
|
|
|
82
86
|
constructor({
|
|
@@ -123,11 +127,27 @@ export class ExtensionEnablementController implements DisposableLike {
|
|
|
123
127
|
};
|
|
124
128
|
};
|
|
125
129
|
|
|
126
|
-
setThemeSelectionProtection(snapshot: ThemeSelectionProtectionSnapshot | undefined): void {
|
|
127
|
-
this.
|
|
128
|
-
|
|
130
|
+
setThemeSelectionProtection(snapshot: ThemeSelectionProtectionSnapshot | undefined): () => void {
|
|
131
|
+
const generation = ++this.themeSelectionProtectionGeneration;
|
|
132
|
+
if (snapshot === undefined) {
|
|
133
|
+
this.themeSelectionProtectionOwners.clear();
|
|
134
|
+
return () => undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.themeSelectionProtectionOwners.set(
|
|
138
|
+
generation,
|
|
139
|
+
snapshot.kind === 'known'
|
|
129
140
|
? { ...snapshot, protectedThemeIds: [...snapshot.protectedThemeIds] }
|
|
130
|
-
: snapshot
|
|
141
|
+
: snapshot,
|
|
142
|
+
);
|
|
143
|
+
let disposed = false;
|
|
144
|
+
return () => {
|
|
145
|
+
if (disposed) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
disposed = true;
|
|
149
|
+
this.themeSelectionProtectionOwners.delete(generation);
|
|
150
|
+
};
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
commitInstalledRecords(
|
|
@@ -228,6 +248,7 @@ export class ExtensionEnablementController implements DisposableLike {
|
|
|
228
248
|
this.disposed = true;
|
|
229
249
|
this.listeners.clear();
|
|
230
250
|
this.registrationHandles.clear();
|
|
251
|
+
this.themeSelectionProtectionOwners.clear();
|
|
231
252
|
this.registrationLifetime.dispose();
|
|
232
253
|
}
|
|
233
254
|
|
|
@@ -345,15 +366,25 @@ export class ExtensionEnablementController implements DisposableLike {
|
|
|
345
366
|
return { commitRequestedState: true, eligible: false, kind: 'reloadRequired' };
|
|
346
367
|
}
|
|
347
368
|
|
|
348
|
-
const
|
|
369
|
+
const selectionProtections = [...this.themeSelectionProtectionOwners.values()];
|
|
370
|
+
const themeRegistryRevision = this.registry.themes.getRevision();
|
|
349
371
|
if (
|
|
350
|
-
|
|
351
|
-
|
|
372
|
+
selectionProtections.length === 0 ||
|
|
373
|
+
selectionProtections.some(
|
|
374
|
+
(selectionProtection) =>
|
|
375
|
+
selectionProtection.kind !== 'known' ||
|
|
376
|
+
selectionProtection.themeRegistryRevision !== themeRegistryRevision ||
|
|
377
|
+
!selectionProtection.isCurrent(),
|
|
378
|
+
)
|
|
352
379
|
) {
|
|
353
380
|
return { commitRequestedState: false, eligible: false, kind: 'reloadRequired' };
|
|
354
381
|
}
|
|
355
382
|
|
|
356
|
-
const protectedThemeIds = new Set(
|
|
383
|
+
const protectedThemeIds = new Set(
|
|
384
|
+
selectionProtections.flatMap((selectionProtection) =>
|
|
385
|
+
selectionProtection.kind === 'known' ? selectionProtection.protectedThemeIds : [],
|
|
386
|
+
),
|
|
387
|
+
);
|
|
357
388
|
|
|
358
389
|
const themes = description.manifest.contributes?.themes ?? [];
|
|
359
390
|
if (themes.some((theme) => protectedThemeIds.has(theme.id))) {
|
|
@@ -1,98 +1,123 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DARK_THEME_PRESET_OPTIONS,
|
|
3
|
-
LIGHT_THEME_PRESET_OPTIONS,
|
|
4
|
-
WORKBENCH_COLOR_SCHEME_OPTIONS,
|
|
5
|
-
} from '@workbench-kit/react/workbench';
|
|
6
1
|
import type { ThemeRegistry } from '@workbench-kit/workbench-core';
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import {
|
|
4
|
+
createWorkbenchAppearanceCatalogSnapshot,
|
|
5
|
+
resolveWorkbenchAppearanceSelection,
|
|
6
|
+
type WorkbenchAppearanceCatalogSnapshot,
|
|
7
|
+
type WorkbenchAppearanceHostOptionInput,
|
|
8
|
+
} from '../shell/appearance-catalog.js';
|
|
9
|
+
import { classifyWorkbenchAppearanceThemeSelection } from '../shell/appearance-presentation.js';
|
|
10
|
+
|
|
11
|
+
interface ThemeSelectionProtectionBase {
|
|
12
|
+
readonly isCurrent: () => boolean;
|
|
13
|
+
readonly sourceFingerprint: string;
|
|
14
|
+
readonly themeRegistryRevision: number;
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
export type ThemeSelectionProtectionSnapshot =
|
|
13
|
-
| {
|
|
18
|
+
| (ThemeSelectionProtectionBase & {
|
|
14
19
|
readonly kind: 'known';
|
|
15
20
|
readonly protectedThemeIds: readonly string[];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
| {
|
|
21
|
+
})
|
|
22
|
+
| (ThemeSelectionProtectionBase & {
|
|
19
23
|
readonly kind: 'unknown';
|
|
20
|
-
|
|
21
|
-
};
|
|
24
|
+
});
|
|
22
25
|
|
|
23
26
|
export interface ThemeSelectionProtectionInput {
|
|
27
|
+
readonly catalog?: WorkbenchAppearanceCatalogSnapshot | undefined;
|
|
24
28
|
readonly darkPreset: string | undefined;
|
|
25
29
|
readonly lightPreset: string | undefined;
|
|
26
30
|
readonly theme: string | undefined;
|
|
27
|
-
readonly themeOptions: readonly
|
|
31
|
+
readonly themeOptions: readonly WorkbenchAppearanceHostOptionInput[] | undefined;
|
|
28
32
|
readonly themes: ThemeRegistry;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
/**
|
|
32
|
-
* Captures only selections that resolve to exactly one
|
|
33
|
-
*
|
|
34
|
-
*
|
|
36
|
+
* Captures only selections that resolve to exactly one eligible catalog row. The action guard
|
|
37
|
+
* reconstructs current own data and compares both revision and the canonical source fingerprint,
|
|
38
|
+
* so writable public contributions cannot reuse stale lifecycle protection.
|
|
35
39
|
*/
|
|
36
40
|
export function createThemeSelectionProtectionSnapshot({
|
|
41
|
+
catalog: suppliedCatalog,
|
|
37
42
|
darkPreset,
|
|
38
43
|
lightPreset,
|
|
39
44
|
theme,
|
|
40
45
|
themeOptions,
|
|
41
46
|
themes,
|
|
42
47
|
}: ThemeSelectionProtectionInput): ThemeSelectionProtectionSnapshot {
|
|
43
|
-
const
|
|
44
|
-
|
|
48
|
+
const catalog =
|
|
49
|
+
suppliedCatalog ??
|
|
50
|
+
createWorkbenchAppearanceCatalogSnapshot({ hostOptions: themeOptions, themes });
|
|
51
|
+
const base: ThemeSelectionProtectionBase = {
|
|
52
|
+
isCurrent: () => isCatalogCurrent(catalog, themes, themeOptions),
|
|
53
|
+
sourceFingerprint: catalog.sourceFingerprint,
|
|
54
|
+
themeRegistryRevision: catalog.themeRegistryRevision,
|
|
55
|
+
};
|
|
45
56
|
const hasLightPreset = lightPreset !== undefined;
|
|
46
57
|
const hasDarkPreset = darkPreset !== undefined;
|
|
47
|
-
|
|
48
|
-
if (hasLightPreset !== hasDarkPreset) {
|
|
49
|
-
return { kind: 'unknown', themeRegistryRevision };
|
|
50
|
-
}
|
|
58
|
+
const themeSelection = classifyWorkbenchAppearanceThemeSelection(theme);
|
|
51
59
|
|
|
52
60
|
if (hasLightPreset && hasDarkPreset) {
|
|
53
|
-
|
|
54
|
-
...
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
if (themeSelection.kind !== 'base-preference') {
|
|
62
|
+
return Object.freeze({ ...base, kind: 'unknown' });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const lightResolution = resolveWorkbenchAppearanceSelection(
|
|
66
|
+
catalog,
|
|
67
|
+
'light-preset',
|
|
68
|
+
lightPreset,
|
|
69
|
+
);
|
|
70
|
+
const darkResolution = resolveWorkbenchAppearanceSelection(catalog, 'dark-preset', darkPreset);
|
|
61
71
|
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
!hasExactlyOneOption(lightPreset, lightOptions) ||
|
|
65
|
-
!hasExactlyOneOption(darkPreset, darkOptions)
|
|
66
|
-
) {
|
|
67
|
-
return { kind: 'unknown', themeRegistryRevision };
|
|
72
|
+
if (lightResolution.status !== 'resolved' || darkResolution.status !== 'resolved') {
|
|
73
|
+
return Object.freeze({ ...base, kind: 'unknown' });
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
return {
|
|
76
|
+
return Object.freeze({
|
|
77
|
+
...base,
|
|
71
78
|
kind: 'known',
|
|
72
|
-
protectedThemeIds: [
|
|
73
|
-
|
|
74
|
-
};
|
|
79
|
+
protectedThemeIds: Object.freeze([lightPreset, darkPreset]),
|
|
80
|
+
});
|
|
75
81
|
}
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
if (themeSelection.kind === 'base-preference') {
|
|
84
|
+
return Object.freeze({
|
|
85
|
+
...base,
|
|
86
|
+
kind: 'known',
|
|
87
|
+
protectedThemeIds: Object.freeze([]),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const themeResolution = resolveWorkbenchAppearanceSelection(
|
|
91
|
+
catalog,
|
|
92
|
+
'flat-theme',
|
|
93
|
+
themeSelection.rawTheme,
|
|
94
|
+
);
|
|
95
|
+
if (themeResolution.status !== 'resolved') {
|
|
96
|
+
return Object.freeze({ ...base, kind: 'unknown' });
|
|
80
97
|
}
|
|
81
98
|
|
|
82
|
-
return {
|
|
99
|
+
return Object.freeze({
|
|
100
|
+
...base,
|
|
83
101
|
kind: 'known',
|
|
84
|
-
protectedThemeIds: [
|
|
85
|
-
|
|
86
|
-
};
|
|
102
|
+
protectedThemeIds: Object.freeze([themeSelection.rawTheme]),
|
|
103
|
+
});
|
|
87
104
|
}
|
|
88
105
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
106
|
+
function isCatalogCurrent(
|
|
107
|
+
captured: WorkbenchAppearanceCatalogSnapshot,
|
|
108
|
+
themes: ThemeRegistry,
|
|
109
|
+
themeOptions: readonly WorkbenchAppearanceHostOptionInput[] | undefined,
|
|
110
|
+
): boolean {
|
|
111
|
+
try {
|
|
112
|
+
const current = createWorkbenchAppearanceCatalogSnapshot({
|
|
113
|
+
hostOptions: themeOptions,
|
|
114
|
+
themes,
|
|
115
|
+
});
|
|
116
|
+
return (
|
|
117
|
+
current.themeRegistryRevision === captured.themeRegistryRevision &&
|
|
118
|
+
current.sourceFingerprint === captured.sourceFingerprint
|
|
119
|
+
);
|
|
120
|
+
} catch {
|
|
94
121
|
return false;
|
|
95
122
|
}
|
|
96
|
-
|
|
97
|
-
return options.filter((option) => option.id === selection).length === 1;
|
|
98
123
|
}
|
|
@@ -37,6 +37,37 @@ export interface FieldRemapChromeLabels {
|
|
|
37
37
|
readonly hideHiddenFields: string;
|
|
38
38
|
readonly emptyDetailTitle: string;
|
|
39
39
|
readonly emptyDetailDescription: string;
|
|
40
|
+
/** Additive inspection-only empty-detail copy. Omitted hosts use the English defaults. */
|
|
41
|
+
readonly readOnlyEmptyDetailTitle?: string;
|
|
42
|
+
readonly readOnlyEmptyDetailDescription?: string;
|
|
43
|
+
/** Additive selection-detail Modal copy. Omitted legacy label objects use English defaults. */
|
|
44
|
+
readonly detailModalTitle?: string;
|
|
45
|
+
readonly closeDetailModal?: string;
|
|
46
|
+
/** Additive current-v2 document import/export chrome copy. */
|
|
47
|
+
readonly exportDocumentJson?: string;
|
|
48
|
+
readonly copyDocumentJson?: string;
|
|
49
|
+
readonly exportDocumentTitle?: string;
|
|
50
|
+
readonly closeDocumentExport?: string;
|
|
51
|
+
readonly exportDocumentDescription?: string;
|
|
52
|
+
readonly exportDocumentLabel?: string;
|
|
53
|
+
readonly importDocumentJson?: string;
|
|
54
|
+
readonly importDocumentTitle?: string;
|
|
55
|
+
readonly closeDocumentImport?: string;
|
|
56
|
+
readonly importDocumentDescription?: string;
|
|
57
|
+
readonly importDocumentLabel?: string;
|
|
58
|
+
readonly importDocumentPlaceholder?: string;
|
|
59
|
+
readonly applyDocumentImport?: string;
|
|
60
|
+
readonly cancelDocumentImport?: string;
|
|
61
|
+
readonly documentCopied?: string;
|
|
62
|
+
readonly documentCopyFailed?: string;
|
|
63
|
+
readonly documentImportUnavailable?: string;
|
|
64
|
+
readonly documentImportInvalidJson?: string;
|
|
65
|
+
readonly documentImportUnsupportedVersion?: string;
|
|
66
|
+
readonly documentImportInvalidDocument?: string;
|
|
67
|
+
readonly documentImportDuplicateId?: string;
|
|
68
|
+
readonly documentImportIncompatibleSource?: string;
|
|
69
|
+
readonly documentImportIncompatibleTarget?: string;
|
|
70
|
+
readonly documentImportUnavailableTransform?: string;
|
|
40
71
|
/** Additive Flow preview copy. Omitted legacy label objects use English defaults. */
|
|
41
72
|
readonly previewTitle?: string;
|
|
42
73
|
readonly previewLoading?: string;
|
|
@@ -76,6 +107,36 @@ export const defaultFieldRemapChromeLabels = {
|
|
|
76
107
|
emptyDetailTitle: 'Start with a convert',
|
|
77
108
|
emptyDetailDescription:
|
|
78
109
|
'Use the Convert palette to place a convert, then wire source → draft → target. Or select an existing binding / convert note on the canvas.',
|
|
110
|
+
readOnlyEmptyDetailTitle: 'Inspect mappings',
|
|
111
|
+
readOnlyEmptyDetailDescription: 'Select a mapping to inspect its details.',
|
|
112
|
+
detailModalTitle: 'Mapping details',
|
|
113
|
+
closeDetailModal: 'Close details',
|
|
114
|
+
exportDocumentJson: 'Export JSON',
|
|
115
|
+
copyDocumentJson: 'Copy JSON',
|
|
116
|
+
exportDocumentTitle: 'Export mapping document',
|
|
117
|
+
closeDocumentExport: 'Close export',
|
|
118
|
+
exportDocumentDescription:
|
|
119
|
+
'Copy the current version 2 mapping document or select the JSON manually.',
|
|
120
|
+
exportDocumentLabel: 'Current mapping document JSON',
|
|
121
|
+
importDocumentJson: 'Import JSON',
|
|
122
|
+
importDocumentTitle: 'Import mapping document',
|
|
123
|
+
closeDocumentImport: 'Close import',
|
|
124
|
+
importDocumentDescription:
|
|
125
|
+
'Paste a current version 2 mapping document. A valid import replaces the complete mapping in one step.',
|
|
126
|
+
importDocumentLabel: 'Mapping document JSON',
|
|
127
|
+
importDocumentPlaceholder: 'Paste mapping document JSON',
|
|
128
|
+
applyDocumentImport: 'Validate and import',
|
|
129
|
+
cancelDocumentImport: 'Cancel',
|
|
130
|
+
documentCopied: 'Mapping document copied.',
|
|
131
|
+
documentCopyFailed: 'The mapping document could not be copied.',
|
|
132
|
+
documentImportUnavailable: 'Import is unavailable for this mapping.',
|
|
133
|
+
documentImportInvalidJson: 'Enter valid mapping document JSON.',
|
|
134
|
+
documentImportUnsupportedVersion: 'This mapping document version is not supported.',
|
|
135
|
+
documentImportInvalidDocument: 'This mapping document is invalid.',
|
|
136
|
+
documentImportDuplicateId: 'This mapping document contains duplicate identities.',
|
|
137
|
+
documentImportIncompatibleSource: 'This mapping document uses an unavailable source field.',
|
|
138
|
+
documentImportIncompatibleTarget: 'This mapping document uses an unavailable target field.',
|
|
139
|
+
documentImportUnavailableTransform: 'This mapping document uses an unavailable convert.',
|
|
79
140
|
previewTitle: 'Sample preview',
|
|
80
141
|
previewLoading: 'Updating preview…',
|
|
81
142
|
previewError: 'Preview failed',
|
|
@@ -114,6 +175,34 @@ export const fieldRemapChromeLabelKeys = {
|
|
|
114
175
|
hideHiddenFields: 'fieldRemap.hideHiddenFields',
|
|
115
176
|
emptyDetailTitle: 'fieldRemap.emptyDetailTitle',
|
|
116
177
|
emptyDetailDescription: 'fieldRemap.emptyDetailDescription',
|
|
178
|
+
readOnlyEmptyDetailTitle: 'fieldRemap.readOnlyEmptyDetailTitle',
|
|
179
|
+
readOnlyEmptyDetailDescription: 'fieldRemap.readOnlyEmptyDetailDescription',
|
|
180
|
+
detailModalTitle: 'fieldRemap.detailModalTitle',
|
|
181
|
+
closeDetailModal: 'fieldRemap.closeDetailModal',
|
|
182
|
+
exportDocumentJson: 'fieldRemap.exportDocumentJson',
|
|
183
|
+
copyDocumentJson: 'fieldRemap.copyDocumentJson',
|
|
184
|
+
exportDocumentTitle: 'fieldRemap.exportDocumentTitle',
|
|
185
|
+
closeDocumentExport: 'fieldRemap.closeDocumentExport',
|
|
186
|
+
exportDocumentDescription: 'fieldRemap.exportDocumentDescription',
|
|
187
|
+
exportDocumentLabel: 'fieldRemap.exportDocumentLabel',
|
|
188
|
+
importDocumentJson: 'fieldRemap.importDocumentJson',
|
|
189
|
+
importDocumentTitle: 'fieldRemap.importDocumentTitle',
|
|
190
|
+
closeDocumentImport: 'fieldRemap.closeDocumentImport',
|
|
191
|
+
importDocumentDescription: 'fieldRemap.importDocumentDescription',
|
|
192
|
+
importDocumentLabel: 'fieldRemap.importDocumentLabel',
|
|
193
|
+
importDocumentPlaceholder: 'fieldRemap.importDocumentPlaceholder',
|
|
194
|
+
applyDocumentImport: 'fieldRemap.applyDocumentImport',
|
|
195
|
+
cancelDocumentImport: 'fieldRemap.cancelDocumentImport',
|
|
196
|
+
documentCopied: 'fieldRemap.documentCopied',
|
|
197
|
+
documentCopyFailed: 'fieldRemap.documentCopyFailed',
|
|
198
|
+
documentImportUnavailable: 'fieldRemap.documentImportUnavailable',
|
|
199
|
+
documentImportInvalidJson: 'fieldRemap.documentImportInvalidJson',
|
|
200
|
+
documentImportUnsupportedVersion: 'fieldRemap.documentImportUnsupportedVersion',
|
|
201
|
+
documentImportInvalidDocument: 'fieldRemap.documentImportInvalidDocument',
|
|
202
|
+
documentImportDuplicateId: 'fieldRemap.documentImportDuplicateId',
|
|
203
|
+
documentImportIncompatibleSource: 'fieldRemap.documentImportIncompatibleSource',
|
|
204
|
+
documentImportIncompatibleTarget: 'fieldRemap.documentImportIncompatibleTarget',
|
|
205
|
+
documentImportUnavailableTransform: 'fieldRemap.documentImportUnavailableTransform',
|
|
117
206
|
previewTitle: 'fieldRemap.previewTitle',
|
|
118
207
|
previewLoading: 'fieldRemap.previewLoading',
|
|
119
208
|
previewError: 'fieldRemap.previewError',
|
|
@@ -162,6 +251,34 @@ export function resolveFieldRemapChromeLabels(
|
|
|
162
251
|
hideHiddenFields: resolve('hideHiddenFields'),
|
|
163
252
|
emptyDetailTitle: resolve('emptyDetailTitle'),
|
|
164
253
|
emptyDetailDescription: resolve('emptyDetailDescription'),
|
|
254
|
+
readOnlyEmptyDetailTitle: resolve('readOnlyEmptyDetailTitle'),
|
|
255
|
+
readOnlyEmptyDetailDescription: resolve('readOnlyEmptyDetailDescription'),
|
|
256
|
+
detailModalTitle: resolve('detailModalTitle'),
|
|
257
|
+
closeDetailModal: resolve('closeDetailModal'),
|
|
258
|
+
exportDocumentJson: resolve('exportDocumentJson'),
|
|
259
|
+
copyDocumentJson: resolve('copyDocumentJson'),
|
|
260
|
+
exportDocumentTitle: resolve('exportDocumentTitle'),
|
|
261
|
+
closeDocumentExport: resolve('closeDocumentExport'),
|
|
262
|
+
exportDocumentDescription: resolve('exportDocumentDescription'),
|
|
263
|
+
exportDocumentLabel: resolve('exportDocumentLabel'),
|
|
264
|
+
importDocumentJson: resolve('importDocumentJson'),
|
|
265
|
+
importDocumentTitle: resolve('importDocumentTitle'),
|
|
266
|
+
closeDocumentImport: resolve('closeDocumentImport'),
|
|
267
|
+
importDocumentDescription: resolve('importDocumentDescription'),
|
|
268
|
+
importDocumentLabel: resolve('importDocumentLabel'),
|
|
269
|
+
importDocumentPlaceholder: resolve('importDocumentPlaceholder'),
|
|
270
|
+
applyDocumentImport: resolve('applyDocumentImport'),
|
|
271
|
+
cancelDocumentImport: resolve('cancelDocumentImport'),
|
|
272
|
+
documentCopied: resolve('documentCopied'),
|
|
273
|
+
documentCopyFailed: resolve('documentCopyFailed'),
|
|
274
|
+
documentImportUnavailable: resolve('documentImportUnavailable'),
|
|
275
|
+
documentImportInvalidJson: resolve('documentImportInvalidJson'),
|
|
276
|
+
documentImportUnsupportedVersion: resolve('documentImportUnsupportedVersion'),
|
|
277
|
+
documentImportInvalidDocument: resolve('documentImportInvalidDocument'),
|
|
278
|
+
documentImportDuplicateId: resolve('documentImportDuplicateId'),
|
|
279
|
+
documentImportIncompatibleSource: resolve('documentImportIncompatibleSource'),
|
|
280
|
+
documentImportIncompatibleTarget: resolve('documentImportIncompatibleTarget'),
|
|
281
|
+
documentImportUnavailableTransform: resolve('documentImportUnavailableTransform'),
|
|
165
282
|
previewTitle: resolve('previewTitle'),
|
|
166
283
|
previewLoading: resolve('previewLoading'),
|
|
167
284
|
previewError: resolve('previewError'),
|