@workbench-kit/shell-react 0.0.2-prototype.0.2.10 → 0.0.2-prototype.0.2.13

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 CHANGED
@@ -63,13 +63,15 @@ for Flow-only embeds and custom bundler setups. The full barrel
63
63
  `FieldRemapFlowMapper` (and `FieldRemapPanel` pass-through) accept optional chrome
64
64
  hooks so hosts avoid CSS/DOM workarounds:
65
65
 
66
- | Prop | Behavior |
67
- | --------------------------------------------------------------- | --------------------------------------------------------------------- |
68
- | `showMinimap` | Default `true`. When `false`, MiniMap is not mounted. |
69
- | `onPaneContextMenu` / `onNodeContextMenu` / `onEdgeContextMenu` | Native event + selection payload; host owns menu UI. |
70
- | `flowActionsRef` | `{ fitView(options?) }` using the same defaults as Controls fit-view. |
71
- | `labels` / `t` | Override edge-list / Convert palette chrome (e.g. “Field maps”). |
72
- | `ioChrome` (Panel) | `'browse' \| 'edit' \| 'none'` prefer browse for inspect-only I/O. |
66
+ | Prop | Behavior |
67
+ | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
68
+ | `showMinimap` | Default `true`. When `false`, MiniMap is not mounted. |
69
+ | `onShowMinimapChange` | When set, adds a MiniMap toggle inside Flow Controls (+/−/fit). |
70
+ | `includeHidden` / `onIncludeHiddenChange` | When callback set, adds a hidden-fields toggle in the same Controls. Host/Panel still project shapes. |
71
+ | `onPaneContextMenu` / `onNodeContextMenu` / `onEdgeContextMenu` | Native event + selection payload; host owns menu UI. |
72
+ | `flowActionsRef` | `{ fitView(options?) }` using the same defaults as Controls fit-view. |
73
+ | `labels` / `t` | Override edge-list / Convert palette chrome (e.g. “Field maps”). |
74
+ | `ioChrome` (Panel) | `'browse' \| 'edit' \| 'none'` — prefer browse for inspect-only I/O. |
73
75
 
74
76
  ```tsx
75
77
  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.10",
3
+ "version": "0.0.2-prototype.0.2.13",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -21,13 +21,13 @@
21
21
  "@xyflow/react": "^12.11.2",
22
22
  "jsonata": "^2.2.0",
23
23
  "react-table-mapping": "^1.0.3",
24
- "@workbench-kit/platform": "0.0.2-prototype.0.2.10",
25
- "@workbench-kit/tokens": "0.0.2-prototype.0.2.10",
26
- "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.10",
27
- "@workbench-kit/react": "0.0.2-prototype.0.2.10",
28
- "@workbench-kit/field-remap": "0.0.2-prototype.0.2.10",
29
- "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.10",
30
- "@workbench-kit/workspace": "0.0.2-prototype.0.2.10"
24
+ "@workbench-kit/field-remap": "0.0.2-prototype.0.2.13",
25
+ "@workbench-kit/react": "0.0.2-prototype.0.2.13",
26
+ "@workbench-kit/platform": "0.0.2-prototype.0.2.13",
27
+ "@workbench-kit/tokens": "0.0.2-prototype.0.2.13",
28
+ "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.13",
29
+ "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.13",
30
+ "@workbench-kit/workspace": "0.0.2-prototype.0.2.13"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "react": "^19.0.0",
@@ -9,6 +9,8 @@ import {
9
9
  type WorkbenchStorageWriter,
10
10
  } from '@workbench-kit/workbench-core';
11
11
 
12
+ import { isRecord } from '../is-record.js';
13
+
12
14
  export const DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY = 'workbench-kit/.workbench/editors';
13
15
 
14
16
  export function isWorkbenchEditorStatePersistenceAvailable(): boolean {
@@ -176,7 +178,3 @@ function cloneEditorLayoutForStorage(layout: EditorLayoutNode): EditorLayoutNode
176
178
  children: layout.children.map(cloneEditorLayoutForStorage),
177
179
  };
178
180
  }
179
-
180
- function isRecord(value: unknown): value is Record<string, unknown> {
181
- return typeof value === 'object' && value !== null;
182
- }
@@ -19,6 +19,7 @@ import type {
19
19
  EditorDocumentViewRenderContext,
20
20
  } from '@workbench-kit/workbench-core';
21
21
 
22
+ import { isRecord } from '../is-record.js';
22
23
  import { replaceJsonValueAtPath, type JsonFormPath } from '../jdw/json-form-source-patch.js';
23
24
 
24
25
  type JsonPath = JsonFormPath;
@@ -280,7 +281,7 @@ function JsonValueFormField({
280
281
  );
281
282
  }
282
283
 
283
- if (isJsonRecord(value)) {
284
+ if (isRecord(value)) {
284
285
  const entries = Object.entries(value);
285
286
 
286
287
  return (
@@ -433,10 +434,6 @@ function parseJsonObject(content: string): Record<string, unknown> | null {
433
434
  }
434
435
  }
435
436
 
436
- function isJsonRecord(value: unknown): value is Record<string, unknown> {
437
- return typeof value === 'object' && value !== null && !Array.isArray(value);
438
- }
439
-
440
437
  function isJdwWidgetJson(content: string): boolean {
441
438
  const parsed = parseJsonWidgetData(content);
442
439
  return parsed.value !== null;
@@ -482,7 +479,7 @@ function getJsonPathValue(value: unknown, path: JsonPath): unknown {
482
479
  continue;
483
480
  }
484
481
 
485
- if (isJsonRecord(cursor) && typeof segment === 'string') {
482
+ if (isRecord(cursor) && typeof segment === 'string') {
486
483
  cursor = cursor[segment];
487
484
  continue;
488
485
  }
@@ -21,6 +21,10 @@ export interface FieldRemapChromeLabels {
21
21
  readonly placeConvert: string;
22
22
  readonly operatorsTitle: string;
23
23
  readonly operatorsDescription: string;
24
+ readonly showMinimap: string;
25
+ readonly hideMinimap: string;
26
+ readonly showHiddenFields: string;
27
+ readonly hideHiddenFields: string;
24
28
  }
25
29
 
26
30
  export const defaultFieldRemapChromeLabels: FieldRemapChromeLabels = {
@@ -34,6 +38,10 @@ export const defaultFieldRemapChromeLabels: FieldRemapChromeLabels = {
34
38
  operatorsTitle: 'n→m operators',
35
39
  operatorsDescription:
36
40
  'Create combine (n→1) or split (1→n), then wire ports or edit in the side rail.',
41
+ showMinimap: 'Show minimap',
42
+ hideMinimap: 'Hide minimap',
43
+ showHiddenFields: 'Show hidden fields',
44
+ hideHiddenFields: 'Hide hidden fields',
37
45
  };
38
46
 
39
47
  /** Stable capability ids for optional `t()` injection (not free prose). */
@@ -46,6 +54,10 @@ export const fieldRemapChromeLabelKeys = {
46
54
  placeConvert: 'fieldRemap.placeConvert',
47
55
  operatorsTitle: 'fieldRemap.operatorsTitle',
48
56
  operatorsDescription: 'fieldRemap.operatorsDescription',
57
+ showMinimap: 'fieldRemap.showMinimap',
58
+ hideMinimap: 'fieldRemap.hideMinimap',
59
+ showHiddenFields: 'fieldRemap.showHiddenFields',
60
+ hideHiddenFields: 'fieldRemap.hideHiddenFields',
49
61
  } as const satisfies Record<keyof FieldRemapChromeLabels, string>;
50
62
 
51
63
  export function resolveFieldRemapChromeLabels(
@@ -70,5 +82,9 @@ export function resolveFieldRemapChromeLabels(
70
82
  placeConvert: resolve('placeConvert'),
71
83
  operatorsTitle: resolve('operatorsTitle'),
72
84
  operatorsDescription: resolve('operatorsDescription'),
85
+ showMinimap: resolve('showMinimap'),
86
+ hideMinimap: resolve('hideMinimap'),
87
+ showHiddenFields: resolve('showHiddenFields'),
88
+ hideHiddenFields: resolve('hideHiddenFields'),
73
89
  };
74
90
  }
@@ -57,14 +57,6 @@ export function SampleFieldRemapDemo({
57
57
  data-testid="field-remap-host-chrome"
58
58
  style={{ display: 'flex', gap: '0.5rem', marginBottom: '0.75rem', flexWrap: 'wrap' }}
59
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
60
  <Button
69
61
  compact
70
62
  type="button"
@@ -84,6 +76,7 @@ export function SampleFieldRemapDemo({
84
76
  key={`${sample.id}:${ioChrome ?? 'default'}:${browseSeedShapes ? 'seed' : 'plain'}`}
85
77
  sample={sample}
86
78
  showMinimap={showMinimap}
79
+ onShowMinimapChange={setShowMinimap}
87
80
  ioChrome={ioChrome}
88
81
  editableShapes={ioChrome === 'browse' ? false : undefined}
89
82
  sources={browseShapes?.sources}
@@ -12,6 +12,7 @@ import {
12
12
  } from 'react';
13
13
  import {
14
14
  Background,
15
+ ControlButton,
15
16
  Controls,
16
17
  Handle,
17
18
  MiniMap,
@@ -333,6 +334,22 @@ export interface FieldRemapFlowMapperProps {
333
334
  * backward compatibility with existing samples.
334
335
  */
335
336
  readonly showMinimap?: boolean | undefined;
337
+ /**
338
+ * When set, Flow Controls (+/−/fit) include a MiniMap toggle button in the same
339
+ * panel. Hosts should prefer this over a separate toolbar control.
340
+ */
341
+ readonly onShowMinimapChange?: ((show: boolean) => void) | undefined;
342
+ /**
343
+ * When false (default), hosts should project shapes with hidden fields omitted
344
+ * before passing `sources` / `targets`. Flow itself does not filter — this flag
345
+ * drives the Controls toggle pressed state only.
346
+ */
347
+ readonly includeHidden?: boolean | undefined;
348
+ /**
349
+ * When set, Flow Controls include a hidden-fields toggle next to zoom / MiniMap.
350
+ * Pair with host (or Panel) shape projection on `includeHidden`.
351
+ */
352
+ readonly onIncludeHiddenChange?: ((includeHidden: boolean) => void) | undefined;
336
353
  /** Pane context menu; host owns menu UI. Receives current Field Remap selection. */
337
354
  readonly onPaneContextMenu?:
338
355
  | ((event: MouseEvent | globalThis.MouseEvent, ctx: { selection: FieldRemapSelection }) => void)
@@ -375,6 +392,9 @@ function FieldRemapFlowCanvas({
375
392
  selection: selectionProp,
376
393
  onSelectionChange: onSelectionChangeProp,
377
394
  showMinimap = true,
395
+ onShowMinimapChange,
396
+ includeHidden = false,
397
+ onIncludeHiddenChange,
378
398
  onPaneContextMenu,
379
399
  onNodeContextMenu,
380
400
  onEdgeContextMenu,
@@ -713,6 +733,7 @@ function FieldRemapFlowCanvas({
713
733
  className="workbench-field-remap-flow"
714
734
  data-testid="field-remap-mapper"
715
735
  data-minimap={showMinimap ? 'on' : 'off'}
736
+ data-hidden-fields={includeHidden ? 'on' : 'off'}
716
737
  onKeyDown={onKeyDown}
717
738
  >
718
739
  <p className="workbench-field-remap-mapper__hint" data-testid="field-remap-hint">
@@ -784,7 +805,93 @@ function FieldRemapFlowCanvas({
784
805
  >
785
806
  <FieldRemapFlowActionsBridge flowActionsRef={flowActionsRef} />
786
807
  <Background gap={16} color="var(--xy-background-pattern-color)" />
787
- <Controls showInteractive={false} fitViewOptions={DEFAULT_FIT_VIEW_OPTIONS} />
808
+ <Controls showInteractive={false} fitViewOptions={DEFAULT_FIT_VIEW_OPTIONS}>
809
+ {onShowMinimapChange ? (
810
+ <ControlButton
811
+ aria-label={showMinimap ? chromeLabels.hideMinimap : chromeLabels.showMinimap}
812
+ className={
813
+ showMinimap
814
+ ? 'workbench-field-remap-flow__minimap-toggle is-active'
815
+ : 'workbench-field-remap-flow__minimap-toggle'
816
+ }
817
+ data-testid="field-remap-toggle-minimap"
818
+ title={showMinimap ? chromeLabels.hideMinimap : chromeLabels.showMinimap}
819
+ onClick={() => {
820
+ onShowMinimapChange(!showMinimap);
821
+ }}
822
+ >
823
+ <svg
824
+ aria-hidden="true"
825
+ fill="none"
826
+ height="16"
827
+ stroke="currentColor"
828
+ strokeLinecap="round"
829
+ strokeLinejoin="round"
830
+ strokeWidth="1.75"
831
+ viewBox="0 0 24 24"
832
+ width="16"
833
+ >
834
+ <path d="M3 6.5 9 4l6 2.5L21 4v13.5L15 20l-6-2.5L3 20z" />
835
+ <path d="M9 4v13.5" />
836
+ <path d="M15 6.5V20" />
837
+ </svg>
838
+ </ControlButton>
839
+ ) : null}
840
+ {onIncludeHiddenChange ? (
841
+ <ControlButton
842
+ aria-label={
843
+ includeHidden ? chromeLabels.hideHiddenFields : chromeLabels.showHiddenFields
844
+ }
845
+ aria-pressed={includeHidden}
846
+ className={
847
+ includeHidden
848
+ ? 'workbench-field-remap-flow__hidden-toggle is-active'
849
+ : 'workbench-field-remap-flow__hidden-toggle'
850
+ }
851
+ data-testid="field-remap-toggle-hidden-fields"
852
+ title={
853
+ includeHidden ? chromeLabels.hideHiddenFields : chromeLabels.showHiddenFields
854
+ }
855
+ onClick={() => {
856
+ onIncludeHiddenChange(!includeHidden);
857
+ }}
858
+ >
859
+ {includeHidden ? (
860
+ <svg
861
+ aria-hidden="true"
862
+ fill="none"
863
+ height="16"
864
+ stroke="currentColor"
865
+ strokeLinecap="round"
866
+ strokeLinejoin="round"
867
+ strokeWidth="1.75"
868
+ viewBox="0 0 24 24"
869
+ width="16"
870
+ >
871
+ <path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z" />
872
+ <circle cx="12" cy="12" r="3" />
873
+ </svg>
874
+ ) : (
875
+ <svg
876
+ aria-hidden="true"
877
+ fill="none"
878
+ height="16"
879
+ stroke="currentColor"
880
+ strokeLinecap="round"
881
+ strokeLinejoin="round"
882
+ strokeWidth="1.75"
883
+ viewBox="0 0 24 24"
884
+ width="16"
885
+ >
886
+ <path d="M3 3l18 18" />
887
+ <path d="M10.6 10.6a3 3 0 0 0 4.2 4.2" />
888
+ <path d="M9.9 5.1A10.6 10.6 0 0 1 12 5c6.5 0 10 7 10 7a17.4 17.4 0 0 1-3.2 4.4" />
889
+ <path d="M6.1 6.1C3.9 7.7 2 12 2 12s3.5 7 10 7a10.4 10.4 0 0 0 4.2-.9" />
890
+ </svg>
891
+ )}
892
+ </ControlButton>
893
+ ) : null}
894
+ </Controls>
788
895
  {showMinimap ? (
789
896
  <MiniMap
790
897
  pannable
@@ -83,6 +83,8 @@ export interface FieldRemapPanelProps {
83
83
  readonly targetShape?: unknown;
84
84
  /** Forwarded to {@link FieldRemapFlowMapper} (default true). */
85
85
  readonly showMinimap?: boolean | undefined;
86
+ /** Forwarded to {@link FieldRemapFlowMapper} Controls MiniMap toggle. */
87
+ readonly onShowMinimapChange?: FieldRemapFlowMapperProps['onShowMinimapChange'];
86
88
  readonly onPaneContextMenu?: FieldRemapFlowMapperProps['onPaneContextMenu'];
87
89
  readonly onNodeContextMenu?: FieldRemapFlowMapperProps['onNodeContextMenu'];
88
90
  readonly onEdgeContextMenu?: FieldRemapFlowMapperProps['onEdgeContextMenu'];
@@ -136,6 +138,7 @@ export function FieldRemapPanel({
136
138
  sourceSample: sourceSampleProp,
137
139
  targetShape: targetShapeProp,
138
140
  showMinimap,
141
+ onShowMinimapChange,
139
142
  onPaneContextMenu,
140
143
  onNodeContextMenu,
141
144
  onEdgeContextMenu,
@@ -396,14 +399,6 @@ export function FieldRemapPanel({
396
399
  className="workbench-field-remap-demo__shapes"
397
400
  data-testid="field-remap-io-browse-wrap"
398
401
  >
399
- <label className="workbench-field-remap-io-browse__toggle">
400
- <input
401
- checked={includeHidden}
402
- onChange={(event) => setIncludeHidden(event.target.checked)}
403
- type="checkbox"
404
- />
405
- Show hidden fields
406
- </label>
407
402
  <FieldRemapIoClassBrowse
408
403
  includeHidden={includeHidden}
409
404
  sources={sourceFields}
@@ -425,6 +420,9 @@ export function FieldRemapPanel({
425
420
  sourceTitle={sample.sourceLabel}
426
421
  targetTitle={sample.targetLabel}
427
422
  showMinimap={showMinimap}
423
+ onShowMinimapChange={onShowMinimapChange}
424
+ includeHidden={includeHidden}
425
+ onIncludeHiddenChange={setIncludeHidden}
428
426
  onPaneContextMenu={onPaneContextMenu}
429
427
  onNodeContextMenu={onNodeContextMenu}
430
428
  onEdgeContextMenu={onEdgeContextMenu}
@@ -483,6 +483,23 @@
483
483
  max-height: 12px;
484
484
  }
485
485
 
486
+ .workbench-field-remap-flow__canvas
487
+ .react-flow__controls-button.workbench-field-remap-flow__minimap-toggle
488
+ svg,
489
+ .workbench-field-remap-flow__canvas
490
+ .react-flow__controls-button.workbench-field-remap-flow__hidden-toggle
491
+ svg {
492
+ fill: none;
493
+ stroke: currentColor;
494
+ }
495
+
496
+ .workbench-field-remap-flow__canvas
497
+ .react-flow__controls-button.workbench-field-remap-flow__minimap-toggle.is-active,
498
+ .workbench-field-remap-flow__canvas
499
+ .react-flow__controls-button.workbench-field-remap-flow__hidden-toggle.is-active {
500
+ color: var(--vscode-focusBorder, var(--color-accent, #3794ff));
501
+ }
502
+
486
503
  .workbench-field-remap-flow__canvas .react-flow__minimap {
487
504
  margin: 0.5rem;
488
505
  border: 1px solid var(--vscode-panel-border, var(--color-border));
@@ -0,0 +1,4 @@
1
+ /** Package-internal plain-object guard (not a public export). */
2
+ export function isRecord(value: unknown): value is Record<string, unknown> {
3
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
4
+ }
@@ -1,3 +1,5 @@
1
+ import { isRecord } from '../is-record.js';
2
+
1
3
  export type JsonFormPath = readonly (string | number)[];
2
4
 
3
5
  export interface JsonTextRange {
@@ -49,7 +51,7 @@ function updateJsonPathValue(value: unknown, path: JsonFormPath, nextValue: unkn
49
51
  return nextArray;
50
52
  }
51
53
 
52
- if (isJsonRecord(value) && typeof head === 'string') {
54
+ if (isRecord(value) && typeof head === 'string') {
53
55
  return {
54
56
  ...value,
55
57
  [head]: updateJsonPathValue(value[head], tail, nextValue),
@@ -59,10 +61,6 @@ function updateJsonPathValue(value: unknown, path: JsonFormPath, nextValue: unkn
59
61
  return value;
60
62
  }
61
63
 
62
- function isJsonRecord(value: unknown): value is Record<string, unknown> {
63
- return typeof value === 'object' && value !== null && !Array.isArray(value);
64
- }
65
-
66
64
  export function findJsonValueRangeAtPath(source: string, path: JsonFormPath): JsonTextRange | null {
67
65
  const start = skipWhitespace(source, 0);
68
66
  if (start >= source.length) {
@@ -23,6 +23,7 @@ import {
23
23
  } from '@workbench-kit/workbench-core';
24
24
  import type { PreferenceScope } from '@workbench-kit/workbench-config';
25
25
 
26
+ import { isRecord } from '../is-record.js';
26
27
  import { useWorkbench } from './provider.js';
27
28
  import { WORKBENCH_PREFERENCE_SCOPES } from './settings-constants.js';
28
29
 
@@ -593,7 +594,3 @@ function titleFromExtensionId(extensionId: string): string {
593
594
  function slugId(value: string): string {
594
595
  return value.toLowerCase().replace(/[^a-z0-9_-]+/g, '-');
595
596
  }
596
-
597
- function isRecord(value: unknown): value is Record<string, unknown> {
598
- return typeof value === 'object' && value !== null && !Array.isArray(value);
599
- }