@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,145 @@
1
+ import { useMemo, type JSX } from 'react';
2
+ import {
3
+ projectSourceFields,
4
+ projectTargetSlots,
5
+ type SourceField,
6
+ type TargetSlot,
7
+ } from '@workbench-kit/field-remap';
8
+
9
+ export type FieldRemapIoChrome = 'browse' | 'edit' | 'none';
10
+
11
+ export interface FieldRemapIoClassBrowseProps {
12
+ readonly sources: readonly SourceField[];
13
+ readonly targets: readonly TargetSlot[];
14
+ /**
15
+ * When `false` (default), omit `hidden` ports/fields.
16
+ * When `true`, keep them and show a Hidden badge.
17
+ */
18
+ readonly includeHidden?: boolean;
19
+ readonly sourcesTitle?: string;
20
+ readonly targetsTitle?: string;
21
+ readonly className?: string;
22
+ readonly emptyLabel?: string;
23
+ }
24
+
25
+ export function resolveFieldRemapIoChrome(
26
+ ioChrome: FieldRemapIoChrome | undefined,
27
+ editableShapes: boolean,
28
+ ): FieldRemapIoChrome {
29
+ if (ioChrome) {
30
+ return ioChrome;
31
+ }
32
+ return editableShapes ? 'edit' : 'none';
33
+ }
34
+
35
+ function formatClassRef(classRef: { readonly id: string; readonly version: number }): string {
36
+ return `${classRef.id}@${classRef.version}`;
37
+ }
38
+
39
+ function FieldTree({
40
+ nodes,
41
+ emptyLabel,
42
+ }: {
43
+ readonly nodes: readonly (SourceField | TargetSlot)[];
44
+ readonly emptyLabel: string;
45
+ }): JSX.Element {
46
+ if (nodes.length === 0) {
47
+ return <p className="workbench-field-remap-io-browse__empty">{emptyLabel}</p>;
48
+ }
49
+ return (
50
+ <ul className="workbench-field-remap-io-browse__fields">
51
+ {nodes.map((node) => (
52
+ <li key={node.id}>
53
+ <div className="workbench-field-remap-io-browse__row">
54
+ <code className="workbench-field-remap-io-browse__path">{node.path ?? node.label}</code>
55
+ <span className="workbench-field-remap-io-browse__meta">
56
+ {node.dataType ? <span>{node.dataType}</span> : null}
57
+ {node.classRef ? (
58
+ <span className="workbench-field-remap-io-browse__badge" title="classRef">
59
+ {formatClassRef(node.classRef)}
60
+ </span>
61
+ ) : null}
62
+ {node.hidden === true ? (
63
+ <span className="workbench-field-remap-io-browse__badge" title="hidden">
64
+ Hidden
65
+ </span>
66
+ ) : null}
67
+ </span>
68
+ </div>
69
+ {node.children?.length ? (
70
+ <FieldTree emptyLabel={emptyLabel} nodes={node.children} />
71
+ ) : null}
72
+ </li>
73
+ ))}
74
+ </ul>
75
+ );
76
+ }
77
+
78
+ function PortSection({
79
+ title,
80
+ nodes,
81
+ emptyLabel,
82
+ }: {
83
+ readonly title: string;
84
+ readonly nodes: readonly (SourceField | TargetSlot)[];
85
+ readonly emptyLabel: string;
86
+ }): JSX.Element {
87
+ return (
88
+ <section aria-label={title} className="workbench-field-remap-io-browse__section">
89
+ <h3 className="workbench-field-remap-io-browse__section-title">{title}</h3>
90
+ {nodes.length === 0 ? (
91
+ <p className="workbench-field-remap-io-browse__empty">{emptyLabel}</p>
92
+ ) : (
93
+ nodes.map((node) => (
94
+ <div className="workbench-field-remap-io-browse__port" key={node.id}>
95
+ <p className="workbench-field-remap-io-browse__port-title">
96
+ <span className="workbench-field-remap-io-browse__port-id">{node.label}</span>
97
+ <span className="workbench-field-remap-io-browse__port-meta">
98
+ {node.classRef ? formatClassRef(node.classRef) : node.id}
99
+ {node.hidden === true ? ' · Hidden' : ''}
100
+ </span>
101
+ </p>
102
+ <FieldTree
103
+ emptyLabel={emptyLabel}
104
+ nodes={node.children?.length ? node.children : [node]}
105
+ />
106
+ </div>
107
+ ))
108
+ )}
109
+ </section>
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Read-only I/O chrome: browse source/target trees with optional `classRef` and `hidden`.
115
+ * Prefer this over {@link FieldRemapShapeIoEditor} when hosts author topology on Flow and
116
+ * only need to inspect declared shapes.
117
+ */
118
+ export function FieldRemapIoClassBrowse({
119
+ sources,
120
+ targets,
121
+ includeHidden = false,
122
+ sourcesTitle = 'Inputs',
123
+ targetsTitle = 'Outputs',
124
+ className,
125
+ emptyLabel = 'No fields',
126
+ }: FieldRemapIoClassBrowseProps): JSX.Element {
127
+ const projectedSources = useMemo(
128
+ () => projectSourceFields(sources, { includeHidden }),
129
+ [includeHidden, sources],
130
+ );
131
+ const projectedTargets = useMemo(
132
+ () => projectTargetSlots(targets, { includeHidden }),
133
+ [includeHidden, targets],
134
+ );
135
+
136
+ return (
137
+ <div
138
+ className={['workbench-field-remap-io-browse', className].filter(Boolean).join(' ')}
139
+ data-testid="field-remap-io-browse"
140
+ >
141
+ <PortSection emptyLabel={emptyLabel} nodes={projectedSources} title={sourcesTitle} />
142
+ <PortSection emptyLabel={emptyLabel} nodes={projectedTargets} title={targetsTitle} />
143
+ </div>
144
+ );
145
+ }
@@ -0,0 +1,166 @@
1
+ import jsonata from 'jsonata';
2
+ import type { ValueTransformDefinition } from '@workbench-kit/field-remap';
3
+
4
+ /** Host-registered JSONata expression transform (Stedi-style advanced mapping). */
5
+ export const JSONATA_TRANSFORM_ID = 'expr:jsonata' as const;
6
+
7
+ /** Default wall-clock budget for a single expression evaluation. */
8
+ export const DEFAULT_JSONATA_TIMEOUT_MS = 2_000;
9
+
10
+ /** Default maximum expression source length (characters). */
11
+ export const DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH = 4_096;
12
+
13
+ export type JsonataTransformErrorPolicy = 'passthrough' | 'throw';
14
+
15
+ export interface CreateJsonataValueTransformOptions {
16
+ /** Wall-clock timeout for `evaluate` (default {@link DEFAULT_JSONATA_TIMEOUT_MS}). */
17
+ readonly timeoutMs?: number;
18
+ /**
19
+ * Reject expressions longer than this many characters
20
+ * (default {@link DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH}).
21
+ */
22
+ readonly maxExpressionLength?: number;
23
+ /**
24
+ * - `throw` (default): surface timeout / compile / evaluate failures to the host
25
+ * - `passthrough`: return the original value (legacy silent identity)
26
+ */
27
+ readonly onError?: JsonataTransformErrorPolicy;
28
+ }
29
+
30
+ export function createJsonataValueTransform(
31
+ options: CreateJsonataValueTransformOptions = {},
32
+ ): ValueTransformDefinition {
33
+ const timeoutMs = options.timeoutMs ?? DEFAULT_JSONATA_TIMEOUT_MS;
34
+ const maxExpressionLength = options.maxExpressionLength ?? DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH;
35
+ const onError = options.onError ?? 'throw';
36
+
37
+ return {
38
+ id: JSONATA_TRANSFORM_ID,
39
+ label: 'JSONata expression',
40
+ description:
41
+ 'Evaluate a JSONata expression against the source value (host-registered; bounded).',
42
+ category: 'expression',
43
+ inputTypes: ['string', 'number', 'boolean', 'object', 'array', 'unknown'],
44
+ outputType: 'unknown',
45
+ optionFields: [
46
+ {
47
+ key: 'expression',
48
+ label: 'Expression',
49
+ kind: 'string',
50
+ },
51
+ ],
52
+ apply: async (value, context) => {
53
+ const expression =
54
+ typeof context.options?.expression === 'string' ? context.options.expression.trim() : '';
55
+ if (!expression) {
56
+ return value;
57
+ }
58
+
59
+ if (expression.length > maxExpressionLength) {
60
+ return handleJsonataError(
61
+ new Error(
62
+ `JSONata expression exceeds max length (${expression.length} > ${maxExpressionLength}).`,
63
+ ),
64
+ value,
65
+ onError,
66
+ );
67
+ }
68
+
69
+ try {
70
+ const compiled = jsonata(expression);
71
+ // jsonata@2.x evaluate returns a Promise (1.x was synchronous).
72
+ return await raceJsonataEvaluation(compiled.evaluate(value), {
73
+ timeoutMs,
74
+ signal: context.signal,
75
+ });
76
+ } catch (error) {
77
+ return handleJsonataError(error, value, onError);
78
+ }
79
+ },
80
+ };
81
+ }
82
+
83
+ /** Default bounded transform (`onError: 'throw'`, 2s timeout, 4k expression cap). */
84
+ export const jsonataValueTransform: ValueTransformDefinition = createJsonataValueTransform();
85
+
86
+ export class JsonataTransformTimeoutError extends Error {
87
+ override readonly name = 'JsonataTransformTimeoutError';
88
+
89
+ constructor(timeoutMs: number) {
90
+ super(`JSONata evaluation timed out after ${timeoutMs}ms.`);
91
+ }
92
+ }
93
+
94
+ /** Race a JSONata evaluation against timeout / AbortSignal (exported for unit tests). */
95
+ export async function raceJsonataEvaluation<T>(
96
+ evaluation: PromiseLike<T>,
97
+ bounds: { readonly timeoutMs: number; readonly signal?: AbortSignal },
98
+ ): Promise<T> {
99
+ const { timeoutMs, signal } = bounds;
100
+ if (signal?.aborted) {
101
+ throw signal.reason instanceof Error
102
+ ? signal.reason
103
+ : Object.assign(new Error('The operation was aborted.'), { name: 'AbortError' });
104
+ }
105
+
106
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
107
+ return evaluation;
108
+ }
109
+
110
+ return new Promise<T>((resolve, reject) => {
111
+ let settled = false;
112
+ const timer = setTimeout(() => {
113
+ if (settled) return;
114
+ settled = true;
115
+ cleanup();
116
+ reject(new JsonataTransformTimeoutError(timeoutMs));
117
+ }, timeoutMs);
118
+
119
+ const onAbort = () => {
120
+ if (settled) return;
121
+ settled = true;
122
+ cleanup();
123
+ reject(
124
+ signal?.reason instanceof Error
125
+ ? signal.reason
126
+ : Object.assign(new Error('The operation was aborted.'), { name: 'AbortError' }),
127
+ );
128
+ };
129
+
130
+ const cleanup = () => {
131
+ clearTimeout(timer);
132
+ signal?.removeEventListener('abort', onAbort);
133
+ };
134
+
135
+ signal?.addEventListener('abort', onAbort, { once: true });
136
+
137
+ Promise.resolve(evaluation).then(
138
+ (result) => {
139
+ if (settled) return;
140
+ settled = true;
141
+ cleanup();
142
+ resolve(result);
143
+ },
144
+ (error: unknown) => {
145
+ if (settled) return;
146
+ settled = true;
147
+ cleanup();
148
+ reject(error);
149
+ },
150
+ );
151
+ });
152
+ }
153
+
154
+ function handleJsonataError(
155
+ error: unknown,
156
+ value: unknown,
157
+ onError: JsonataTransformErrorPolicy,
158
+ ): unknown {
159
+ if (onError === 'passthrough') {
160
+ return value;
161
+ }
162
+ if (error instanceof Error) {
163
+ throw error;
164
+ }
165
+ throw new Error(String(error));
166
+ }