@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.
Files changed (105) hide show
  1. package/README.md +98 -0
  2. package/package.json +51 -0
  3. package/src/chat/ai-mock-proposals.ts +39 -0
  4. package/src/chat/command-input.ts +52 -0
  5. package/src/chat/command-policy.ts +54 -0
  6. package/src/chat/command-surface.tsx +155 -0
  7. package/src/chat/use-chat-command-proposals.ts +194 -0
  8. package/src/chat/view-data.ts +20 -0
  9. package/src/chat/view.tsx +220 -0
  10. package/src/commands/inspector-surface.tsx +46 -0
  11. package/src/commands/use-command-descriptors.ts +41 -0
  12. package/src/commands/use-context-key-revision.ts +18 -0
  13. package/src/commands/view-data.ts +17 -0
  14. package/src/commands/view.tsx +43 -0
  15. package/src/devtools/WorkbenchDevtoolsPanel.tsx +124 -0
  16. package/src/devtools/WorkbenchDevtoolsShell.tsx +17 -0
  17. package/src/devtools/index.ts +11 -0
  18. package/src/devtools/use-workbench-devtools-snapshot.ts +84 -0
  19. package/src/devtools/workbench-devtools-snapshot.ts +140 -0
  20. package/src/devtools/workbench-devtools.css +82 -0
  21. package/src/editor/area-dnd.ts +69 -0
  22. package/src/editor/area-model.ts +71 -0
  23. package/src/editor/area.css +454 -0
  24. package/src/editor/area.tsx +676 -0
  25. package/src/editor/host-surface.tsx +691 -0
  26. package/src/editor/markdown-document-view-data.ts +16 -0
  27. package/src/editor/pane-visibility.ts +130 -0
  28. package/src/editor/resource.ts +36 -0
  29. package/src/editor/state-storage.ts +182 -0
  30. package/src/editor/tab-context-menu.ts +118 -0
  31. package/src/editor/use-editor.ts +138 -0
  32. package/src/editor/view-providers.tsx +504 -0
  33. package/src/editor/workspace-file-availability.ts +15 -0
  34. package/src/editor/workspace-reconcile.tsx +31 -0
  35. package/src/explorer/context-menu.ts +114 -0
  36. package/src/explorer/create-command-workspace-explorer-port.ts +86 -0
  37. package/src/explorer/reveal.ts +112 -0
  38. package/src/explorer/search-view-data.ts +15 -0
  39. package/src/explorer/search-view.tsx +65 -0
  40. package/src/explorer/use-active-workspace-path.ts +15 -0
  41. package/src/explorer/view-data.ts +58 -0
  42. package/src/explorer/view.tsx +227 -0
  43. package/src/extensions/context-menu.ts +52 -0
  44. package/src/extensions/management-model.ts +313 -0
  45. package/src/extensions/use-extension-management.ts +241 -0
  46. package/src/extensions/view-data.ts +19 -0
  47. package/src/extensions/view.tsx +63 -0
  48. package/src/field-remap/chrome-labels.ts +74 -0
  49. package/src/field-remap/convert-note-editor.tsx +206 -0
  50. package/src/field-remap/convert-palette.tsx +126 -0
  51. package/src/field-remap/demo.tsx +105 -0
  52. package/src/field-remap/detail-panel.tsx +659 -0
  53. package/src/field-remap/editor-surface.tsx +36 -0
  54. package/src/field-remap/flow-adapter.ts +838 -0
  55. package/src/field-remap/flow-ops.ts +442 -0
  56. package/src/field-remap/flow.tsx +948 -0
  57. package/src/field-remap/graph.tsx +559 -0
  58. package/src/field-remap/index.ts +88 -0
  59. package/src/field-remap/io-class-browse.tsx +145 -0
  60. package/src/field-remap/jsonata-transform.ts +166 -0
  61. package/src/field-remap/panel.tsx +466 -0
  62. package/src/field-remap/samples.ts +507 -0
  63. package/src/field-remap/shape-io-editor.tsx +170 -0
  64. package/src/field-remap/table-mapping-adapter.ts +59 -0
  65. package/src/field-remap/transform-options-editor.tsx +312 -0
  66. package/src/field-remap/tree.tsx +404 -0
  67. package/src/field-remap/view-data.ts +16 -0
  68. package/src/field-remap/view.css +1367 -0
  69. package/src/field-remap/view.tsx +86 -0
  70. package/src/index.ts +294 -0
  71. package/src/jdw/document-view-data.ts +31 -0
  72. package/src/jdw/json-form-source-patch.ts +323 -0
  73. package/src/jdw/lab-view-data.ts +25 -0
  74. package/src/jdw/lab-view.css +21 -0
  75. package/src/jdw/lab-view.tsx +79 -0
  76. package/src/jdw/widget-form-view.tsx +68 -0
  77. package/src/jdw/widget-preview-view.tsx +54 -0
  78. package/src/management/keybinding-overrides-storage.ts +49 -0
  79. package/src/management/keybinding-settings.tsx +16 -0
  80. package/src/management/palette-commands.ts +38 -0
  81. package/src/management/preference-settings-storage.ts +50 -0
  82. package/src/management/settings-ids.ts +12 -0
  83. package/src/management/settings.tsx +96 -0
  84. package/src/management/use-command-management.ts +191 -0
  85. package/src/management/use-keybinding-management.ts +130 -0
  86. package/src/shell/model.tsx +149 -0
  87. package/src/shell/provider.tsx +606 -0
  88. package/src/shell/secondary-actions.tsx +59 -0
  89. package/src/shell/settings-constants.ts +9 -0
  90. package/src/shell/settings.tsx +599 -0
  91. package/src/shell/shell.tsx +680 -0
  92. package/src/shell/titlebar-layout-controls.tsx +4 -0
  93. package/src/shell/view-host.tsx +212 -0
  94. package/src/workbench/appearance-storage.ts +107 -0
  95. package/src/workbench/command-host.tsx +243 -0
  96. package/src/workbench/command-palette.ts +208 -0
  97. package/src/workbench/keybinding-bridge.ts +54 -0
  98. package/src/workbench/layout-storage.ts +134 -0
  99. package/src/workbench/profile-modal.tsx +118 -0
  100. package/src/workbench/shell-command-registration.ts +58 -0
  101. package/src/workbench/startup-gate.tsx +136 -0
  102. package/src/workbench/status-sections.ts +43 -0
  103. package/src/workbench/use-persisted-appearance.ts +33 -0
  104. package/src/workbench/user-commands.ts +46 -0
  105. package/src/workbench/workspace-view-state.ts +35 -0
@@ -0,0 +1,442 @@
1
+ import {
2
+ MAX_MAPPING_FAN_IN,
3
+ MAX_MAPPING_FAN_OUT,
4
+ MAX_TRANSFORM_CHAIN,
5
+ arePortsCompatible,
6
+ findSourceField,
7
+ findTargetSlot,
8
+ isTransformChainCompatible,
9
+ optionFieldsForStep,
10
+ patchOptionStep,
11
+ resizeOptionSteps,
12
+ sanitizeOptionRecord,
13
+ type CombineMappingOperator,
14
+ type FieldDataType,
15
+ type MappingEdge,
16
+ type MappingOperator,
17
+ type SourceField,
18
+ type SplitMappingOperator,
19
+ type TargetSlot,
20
+ type ValueTransformDefinition,
21
+ type ValueTransformRegistry,
22
+ } from '@workbench-kit/field-remap';
23
+
24
+ /** Canvas / bindings selection for the Flow detail panel. */
25
+ export type FieldRemapSelection =
26
+ | { readonly kind: 'edge'; readonly edgeId: string }
27
+ | { readonly kind: 'transformStep'; readonly edgeId: string; readonly stepIndex: number }
28
+ | { readonly kind: 'draft'; readonly localId: string }
29
+ | { readonly kind: 'operator'; readonly operatorId: string }
30
+ | null;
31
+
32
+ /**
33
+ * Ephemeral place-then-wire transform. Not part of `FieldRemapDocument` until
34
+ * both ports are bound and {@link finalizeDraftTransform} succeeds.
35
+ */
36
+ export type FieldRemapDraftTransform = {
37
+ readonly localId: string;
38
+ readonly transformId: string;
39
+ readonly sourceFieldId?: string;
40
+ readonly targetSlotId?: string;
41
+ };
42
+
43
+ let draftSequence = 0;
44
+
45
+ export function createDraftTransform(transformId: string): FieldRemapDraftTransform {
46
+ draftSequence += 1;
47
+ return {
48
+ localId: `d${draftSequence}-${Date.now()}`,
49
+ transformId,
50
+ };
51
+ }
52
+
53
+ export function bindDraftSource(
54
+ draft: FieldRemapDraftTransform,
55
+ sourceFieldId: string,
56
+ ): FieldRemapDraftTransform {
57
+ return { ...draft, sourceFieldId };
58
+ }
59
+
60
+ export function bindDraftTarget(
61
+ draft: FieldRemapDraftTransform,
62
+ targetSlotId: string,
63
+ ): FieldRemapDraftTransform {
64
+ return { ...draft, targetSlotId };
65
+ }
66
+
67
+ /**
68
+ * Materialize a fully wired draft into a MappingEdge (single-step chain).
69
+ * Returns null when ports are missing or the chain is type-incompatible.
70
+ */
71
+ export function finalizeDraftTransform(
72
+ draft: FieldRemapDraftTransform,
73
+ context: {
74
+ readonly registry: ValueTransformRegistry;
75
+ readonly sources: readonly SourceField[];
76
+ readonly targets: readonly TargetSlot[];
77
+ readonly existing: readonly MappingEdge[];
78
+ },
79
+ ): MappingEdge | null {
80
+ if (!draft.sourceFieldId || !draft.targetSlotId) {
81
+ return null;
82
+ }
83
+ const sourceType = findSourceField(context.sources, draft.sourceFieldId)?.dataType;
84
+ const targetType = findTargetSlot(context.targets, draft.targetSlotId)?.dataType;
85
+ if (
86
+ !arePortsCompatible({
87
+ sourceType,
88
+ targetType,
89
+ transformIds: [draft.transformId],
90
+ registry: context.registry,
91
+ })
92
+ ) {
93
+ return null;
94
+ }
95
+ const existing = context.existing.find((edge) => edge.targetSlotId === draft.targetSlotId);
96
+ return {
97
+ id: existing?.id ?? `e-${draft.sourceFieldId}-${draft.targetSlotId}-${Date.now()}`,
98
+ sourceFieldId: draft.sourceFieldId,
99
+ targetSlotId: draft.targetSlotId,
100
+ transformIds: [draft.transformId],
101
+ };
102
+ }
103
+
104
+ export function updateMappingEdge(
105
+ edges: readonly MappingEdge[],
106
+ edgeId: string,
107
+ updater: (edge: MappingEdge) => MappingEdge,
108
+ ): readonly MappingEdge[] {
109
+ return edges.map((edge) => (edge.id === edgeId ? updater(edge) : edge));
110
+ }
111
+
112
+ function migrateStepOptions(
113
+ previous: Readonly<Record<string, unknown>> | undefined,
114
+ nextTransformId: string,
115
+ registry: ValueTransformRegistry,
116
+ ): Readonly<Record<string, unknown>> | undefined {
117
+ if (!previous) {
118
+ return undefined;
119
+ }
120
+ const allowed = new Set(optionFieldsForStep(registry, nextTransformId).map((field) => field.key));
121
+ if (allowed.size === 0) {
122
+ return undefined;
123
+ }
124
+ const next: Record<string, unknown> = {};
125
+ for (const [key, value] of Object.entries(previous)) {
126
+ if (allowed.has(key)) {
127
+ next[key] = value;
128
+ }
129
+ }
130
+ return sanitizeOptionRecord(next);
131
+ }
132
+
133
+ /**
134
+ * Append a transform step to an edge. Returns `null` when the chain is full or
135
+ * the candidate breaks type compatibility.
136
+ */
137
+ export function addTransformStepToEdge(
138
+ edge: MappingEdge,
139
+ transformId: string,
140
+ context: {
141
+ readonly registry: ValueTransformRegistry;
142
+ readonly sourceType?: FieldDataType;
143
+ readonly targetType?: FieldDataType;
144
+ },
145
+ ): MappingEdge | null {
146
+ const current = edge.transformIds ?? [];
147
+ if (current.length >= MAX_TRANSFORM_CHAIN) {
148
+ return null;
149
+ }
150
+ const nextIds = [...current, transformId];
151
+ if (
152
+ !isTransformChainCompatible(context.registry, nextIds, context.sourceType, context.targetType)
153
+ ) {
154
+ return null;
155
+ }
156
+ return {
157
+ ...edge,
158
+ transformIds: nextIds,
159
+ transformOptionSteps: resizeOptionSteps(edge.transformOptionSteps, nextIds.length),
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Replace `transformIds[stepIndex]`. Clears / migrates that step's option bag.
165
+ * Returns `null` when the index is invalid or the pick is incompatible.
166
+ */
167
+ export function setTransformStepIdOnEdge(
168
+ edge: MappingEdge,
169
+ stepIndex: number,
170
+ transformId: string,
171
+ context: {
172
+ readonly registry: ValueTransformRegistry;
173
+ readonly sourceType?: FieldDataType;
174
+ readonly targetType?: FieldDataType;
175
+ },
176
+ ): MappingEdge | null {
177
+ const current = [...(edge.transformIds ?? [])];
178
+ if (stepIndex < 0 || stepIndex >= current.length) {
179
+ return null;
180
+ }
181
+ const nextIds = [...current];
182
+ nextIds[stepIndex] = transformId;
183
+ if (
184
+ !isTransformChainCompatible(context.registry, nextIds, context.sourceType, context.targetType)
185
+ ) {
186
+ return null;
187
+ }
188
+
189
+ const resolvedSteps = resizeOptionSteps(edge.transformOptionSteps, nextIds.length) ?? [];
190
+ const migrated = migrateStepOptions(resolvedSteps[stepIndex], transformId, context.registry);
191
+ const nextSteps = resolvedSteps.map((step, index) => (index === stepIndex ? migrated : step));
192
+
193
+ return {
194
+ ...edge,
195
+ transformIds: nextIds,
196
+ transformOptionSteps: resizeOptionSteps(nextSteps, nextIds.length),
197
+ };
198
+ }
199
+
200
+ export function removeTransformStepFromEdge(edge: MappingEdge, stepIndex: number): MappingEdge {
201
+ const current = [...(edge.transformIds ?? [])];
202
+ if (stepIndex < 0 || stepIndex >= current.length) {
203
+ return edge;
204
+ }
205
+ current.splice(stepIndex, 1);
206
+ const optionSteps = edge.transformOptionSteps ? [...edge.transformOptionSteps] : undefined;
207
+ if (optionSteps) {
208
+ optionSteps.splice(stepIndex, 1);
209
+ }
210
+ return {
211
+ ...edge,
212
+ transformIds: current.length > 0 ? current : undefined,
213
+ transformOptionSteps: resizeOptionSteps(optionSteps, current.length),
214
+ };
215
+ }
216
+
217
+ export function patchTransformStepOptionOnEdge(
218
+ edge: MappingEdge,
219
+ stepIndex: number,
220
+ key: string,
221
+ value: unknown,
222
+ ): MappingEdge {
223
+ const length = edge.transformIds?.length ?? 0;
224
+ if (stepIndex < 0 || stepIndex >= length) {
225
+ return edge;
226
+ }
227
+ return {
228
+ ...edge,
229
+ transformOptionSteps: patchOptionStep(
230
+ edge.transformOptionSteps,
231
+ length,
232
+ stepIndex,
233
+ key,
234
+ value,
235
+ edge.transformOptions,
236
+ ),
237
+ };
238
+ }
239
+
240
+ export function replaceTransformStepOptionsOnEdge(
241
+ edge: MappingEdge,
242
+ stepIndex: number,
243
+ options: Readonly<Record<string, unknown>>,
244
+ ): MappingEdge {
245
+ const length = edge.transformIds?.length ?? 0;
246
+ if (stepIndex < 0 || stepIndex >= length) {
247
+ return edge;
248
+ }
249
+ const base = resizeOptionSteps(edge.transformOptionSteps, length) ?? Array.from({ length });
250
+ const nextSteps = base.map((step, index) =>
251
+ index === stepIndex ? sanitizeOptionRecord(options) : step,
252
+ );
253
+ return {
254
+ ...edge,
255
+ transformOptionSteps: resizeOptionSteps(nextSteps, length),
256
+ };
257
+ }
258
+
259
+ /** Whether an edge may host list-context (`itemEdges`) editing. */
260
+ export function canEditListContext(
261
+ edge: MappingEdge,
262
+ sources: readonly SourceField[],
263
+ targets: readonly TargetSlot[],
264
+ ): boolean {
265
+ if (edge.itemEdges) {
266
+ return true;
267
+ }
268
+ const source = findSourceField(sources, edge.sourceFieldId);
269
+ const target = findTargetSlot(targets, edge.targetSlotId);
270
+ const sourceOk =
271
+ !source?.dataType || source.dataType === 'array' || source.dataType === 'unknown';
272
+ const targetOk =
273
+ !target?.dataType || target.dataType === 'array' || target.dataType === 'unknown';
274
+ return sourceOk && targetOk && Boolean(source?.children?.length && target?.children?.length);
275
+ }
276
+
277
+ export function enableListContextOnEdge(edge: MappingEdge): MappingEdge {
278
+ if (edge.itemEdges) {
279
+ return edge;
280
+ }
281
+ return { ...edge, itemEdges: [] };
282
+ }
283
+
284
+ export function setItemEdgesOnEdge(
285
+ edge: MappingEdge,
286
+ itemEdges: readonly MappingEdge[],
287
+ ): MappingEdge {
288
+ return { ...edge, itemEdges: [...itemEdges] };
289
+ }
290
+
291
+ export function upsertItemEdgeOnParent(edge: MappingEdge, itemEdge: MappingEdge): MappingEdge {
292
+ const current = edge.itemEdges ?? [];
293
+ const withoutTarget = current.filter((child) => child.targetSlotId !== itemEdge.targetSlotId);
294
+ return { ...edge, itemEdges: [...withoutTarget, itemEdge] };
295
+ }
296
+
297
+ export function removeItemEdgeFromParent(edge: MappingEdge, itemEdgeId: string): MappingEdge {
298
+ return {
299
+ ...edge,
300
+ itemEdges: (edge.itemEdges ?? []).filter((child) => child.id !== itemEdgeId),
301
+ };
302
+ }
303
+
304
+ /**
305
+ * Catalog entries that remain valid when placed at `stepIndex` (replace) or
306
+ * appended (`stepIndex === chain.length`).
307
+ */
308
+ export function listCompatibleTransforms(input: {
309
+ readonly registry: ValueTransformRegistry;
310
+ readonly edge: MappingEdge;
311
+ readonly stepIndex: number;
312
+ readonly sourceType?: FieldDataType;
313
+ readonly targetType?: FieldDataType;
314
+ readonly mode: 'replace' | 'append';
315
+ }): ValueTransformDefinition[] {
316
+ const chain = input.edge.transformIds ?? [];
317
+ if (input.mode === 'append' && chain.length >= MAX_TRANSFORM_CHAIN) {
318
+ return [];
319
+ }
320
+ if (input.mode === 'replace' && (input.stepIndex < 0 || input.stepIndex >= chain.length)) {
321
+ return [];
322
+ }
323
+
324
+ return input.registry
325
+ .list()
326
+ .filter((definition) => definition.id !== 'identity')
327
+ .filter((definition) => {
328
+ const candidate =
329
+ input.mode === 'append'
330
+ ? [...chain, definition.id]
331
+ : chain.map((id, index) => (index === input.stepIndex ? definition.id : id));
332
+ return isTransformChainCompatible(
333
+ input.registry,
334
+ candidate,
335
+ input.sourceType,
336
+ input.targetType,
337
+ );
338
+ });
339
+ }
340
+
341
+ export function edgePortTypes(
342
+ edge: MappingEdge,
343
+ sources: readonly SourceField[],
344
+ targets: readonly TargetSlot[],
345
+ ): { sourceType?: FieldDataType; targetType?: FieldDataType } {
346
+ return {
347
+ sourceType: findSourceField(sources, edge.sourceFieldId)?.dataType,
348
+ targetType: findTargetSlot(targets, edge.targetSlotId)?.dataType,
349
+ };
350
+ }
351
+
352
+ let operatorSequence = 0;
353
+
354
+ export function createCombineOperator(seed?: {
355
+ readonly inputFieldIds?: readonly string[];
356
+ readonly outputSlotId?: string;
357
+ }): CombineMappingOperator {
358
+ operatorSequence += 1;
359
+ return {
360
+ kind: 'combine',
361
+ id: `op-combine-${operatorSequence}-${Date.now()}`,
362
+ inputFieldIds: [...(seed?.inputFieldIds ?? [])].slice(0, MAX_MAPPING_FAN_IN),
363
+ outputSlotId: seed?.outputSlotId ?? '',
364
+ };
365
+ }
366
+
367
+ export function createSplitOperator(seed?: {
368
+ readonly inputFieldId?: string;
369
+ readonly outputSlotIds?: readonly string[];
370
+ }): SplitMappingOperator {
371
+ operatorSequence += 1;
372
+ return {
373
+ kind: 'split',
374
+ id: `op-split-${operatorSequence}-${Date.now()}`,
375
+ inputFieldId: seed?.inputFieldId ?? '',
376
+ outputSlotIds: [...(seed?.outputSlotIds ?? [])].slice(0, MAX_MAPPING_FAN_OUT),
377
+ };
378
+ }
379
+
380
+ export function updateMappingOperator(
381
+ operators: readonly MappingOperator[],
382
+ operatorId: string,
383
+ updater: (operator: MappingOperator) => MappingOperator,
384
+ ): MappingOperator[] {
385
+ return operators.map((operator) => (operator.id === operatorId ? updater(operator) : operator));
386
+ }
387
+
388
+ export function removeMappingOperator(
389
+ operators: readonly MappingOperator[],
390
+ operatorId: string,
391
+ ): MappingOperator[] {
392
+ return operators.filter((operator) => operator.id !== operatorId);
393
+ }
394
+
395
+ export function bindOperatorInput(operator: MappingOperator, fieldId: string): MappingOperator {
396
+ if (operator.kind === 'combine') {
397
+ if (
398
+ operator.inputFieldIds.includes(fieldId) ||
399
+ operator.inputFieldIds.length >= MAX_MAPPING_FAN_IN
400
+ ) {
401
+ return operator;
402
+ }
403
+ return { ...operator, inputFieldIds: [...operator.inputFieldIds, fieldId] };
404
+ }
405
+ return { ...operator, inputFieldId: fieldId };
406
+ }
407
+
408
+ export function bindOperatorOutput(operator: MappingOperator, slotId: string): MappingOperator {
409
+ if (operator.kind === 'combine') {
410
+ return { ...operator, outputSlotId: slotId };
411
+ }
412
+ if (
413
+ operator.outputSlotIds.includes(slotId) ||
414
+ operator.outputSlotIds.length >= MAX_MAPPING_FAN_OUT
415
+ ) {
416
+ return operator;
417
+ }
418
+ return { ...operator, outputSlotIds: [...operator.outputSlotIds, slotId] };
419
+ }
420
+
421
+ export function removeOperatorInput(operator: MappingOperator, fieldId: string): MappingOperator {
422
+ if (operator.kind === 'combine') {
423
+ return {
424
+ ...operator,
425
+ inputFieldIds: operator.inputFieldIds.filter((id) => id !== fieldId),
426
+ };
427
+ }
428
+ if (operator.inputFieldId === fieldId) {
429
+ return { ...operator, inputFieldId: '' };
430
+ }
431
+ return operator;
432
+ }
433
+
434
+ export function removeOperatorOutput(operator: MappingOperator, slotId: string): MappingOperator {
435
+ if (operator.kind === 'combine') {
436
+ return operator.outputSlotId === slotId ? { ...operator, outputSlotId: '' } : operator;
437
+ }
438
+ return {
439
+ ...operator,
440
+ outputSlotIds: operator.outputSlotIds.filter((id) => id !== slotId),
441
+ };
442
+ }