@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,59 @@
1
+ import type { MappingEdge } from '@workbench-kit/field-remap';
2
+ import type { FieldItemInput, Mapping } from 'react-table-mapping';
3
+
4
+ /** One column: field name (static string cell). */
5
+ export const FIELD_REMAP_NAME_COLUMNS = [{ title: 'Field', key: 'name' }] as const;
6
+
7
+ export interface FieldRemapLeaf {
8
+ /** Kit field/slot id (may contain dots, e.g. `a.user_name`). */
9
+ readonly id: string;
10
+ readonly label: string;
11
+ }
12
+
13
+ /**
14
+ * react-table-mapping builds connectors with `querySelector('#connector-…'+id)`.
15
+ * Dots in ids break CSS selectors (`#a.user_name` → id=a + class=user_name).
16
+ * Encode kit ids for the OSS UI and decode when adapting mappings.
17
+ */
18
+ export function toTableMappingFieldId(kitFieldId: string): string {
19
+ return kitFieldId.split('.').join('__');
20
+ }
21
+
22
+ export function fromTableMappingFieldId(tableFieldId: string): string {
23
+ return tableFieldId.split('__').join('.');
24
+ }
25
+
26
+ /** Build react-table-mapping source/target rows from leaf field descriptors. */
27
+ export function toTableMappingFields(leaves: readonly FieldRemapLeaf[]): FieldItemInput[] {
28
+ return leaves.map((leaf) => {
29
+ const id = toTableMappingFieldId(leaf.id);
30
+ return {
31
+ id,
32
+ key: id,
33
+ name: {
34
+ type: 'string' as const,
35
+ columnKey: 'name',
36
+ value: leaf.label,
37
+ },
38
+ };
39
+ });
40
+ }
41
+
42
+ /** OSS Mapping[] → kit MappingEdge[] (identity / pass-through). */
43
+ export function tableMappingsToEdges(mappings: readonly Mapping[]): MappingEdge[] {
44
+ return mappings.map((mapping) => ({
45
+ id: mapping.id,
46
+ sourceFieldId: fromTableMappingFieldId(mapping.source),
47
+ targetSlotId: fromTableMappingFieldId(mapping.target),
48
+ transformIds: ['identity'] as const,
49
+ }));
50
+ }
51
+
52
+ /** kit MappingEdge[] → OSS Mapping[] for controlled mappings. */
53
+ export function edgesToTableMappings(edges: readonly MappingEdge[]): Mapping[] {
54
+ return edges.map((edge) => ({
55
+ id: edge.id,
56
+ source: toTableMappingFieldId(edge.sourceFieldId),
57
+ target: toTableMappingFieldId(edge.targetSlotId),
58
+ }));
59
+ }
@@ -0,0 +1,312 @@
1
+ import { useEffect, useId, useState, type JSX } from 'react';
2
+ import { Button } from '@workbench-kit/react/primitives';
3
+ import type { TransformOptionField } from '@workbench-kit/field-remap';
4
+
5
+ export interface TransformOptionsEditorProps {
6
+ readonly fields: readonly TransformOptionField[];
7
+ readonly value: Readonly<Record<string, unknown>>;
8
+ readonly onChange: (next: Record<string, unknown>) => void;
9
+ readonly disabled?: boolean | undefined;
10
+ /** Prefix for stable test ids (`field-remap-option-<key>`). */
11
+ readonly testIdPrefix?: string | undefined;
12
+ }
13
+
14
+ function asStringMap(value: unknown): Record<string, string> {
15
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
16
+ return {};
17
+ }
18
+ const next: Record<string, string> = {};
19
+ for (const [key, entry] of Object.entries(value as Record<string, unknown>)) {
20
+ if (typeof entry === 'string') {
21
+ next[key] = entry;
22
+ } else if (entry !== undefined && entry !== null) {
23
+ next[key] = String(entry);
24
+ }
25
+ }
26
+ return next;
27
+ }
28
+
29
+ function StringMapEditor({
30
+ field,
31
+ value,
32
+ disabled,
33
+ onChange,
34
+ testId,
35
+ }: {
36
+ readonly field: TransformOptionField;
37
+ readonly value: unknown;
38
+ readonly disabled: boolean;
39
+ readonly onChange: (next: Record<string, string>) => void;
40
+ readonly testId: string;
41
+ }): JSX.Element {
42
+ const map = asStringMap(value);
43
+ const rows = Object.entries(map);
44
+ const [draftKey, setDraftKey] = useState('');
45
+ const [draftValue, setDraftValue] = useState('');
46
+
47
+ const commitRow = (key: string, entryValue: string, previousKey?: string) => {
48
+ const trimmedKey = key.trim();
49
+ const next = { ...map };
50
+ if (previousKey && previousKey !== trimmedKey) {
51
+ delete next[previousKey];
52
+ }
53
+ if (!trimmedKey) {
54
+ onChange(next);
55
+ return;
56
+ }
57
+ next[trimmedKey] = entryValue;
58
+ onChange(next);
59
+ };
60
+
61
+ return (
62
+ <div className="workbench-field-remap-options__string-map" data-testid={testId}>
63
+ <ul className="workbench-field-remap-options__string-map-rows">
64
+ {rows.map(([key, entryValue]) => (
65
+ <li key={key}>
66
+ <input
67
+ type="text"
68
+ aria-label={`${field.label} key`}
69
+ disabled={disabled}
70
+ value={key}
71
+ onChange={(event) => commitRow(event.target.value, entryValue, key)}
72
+ />
73
+ <input
74
+ type="text"
75
+ aria-label={`${field.label} value for ${key}`}
76
+ disabled={disabled}
77
+ value={entryValue}
78
+ onChange={(event) => commitRow(key, event.target.value, key)}
79
+ />
80
+ <Button
81
+ compact
82
+ type="button"
83
+ disabled={disabled}
84
+ aria-label={`Remove ${key}`}
85
+ onClick={() => {
86
+ const next = { ...map };
87
+ delete next[key];
88
+ onChange(next);
89
+ }}
90
+ >
91
+ ×
92
+ </Button>
93
+ </li>
94
+ ))}
95
+ </ul>
96
+ <div className="workbench-field-remap-options__string-map-add">
97
+ <input
98
+ type="text"
99
+ aria-label={`New ${field.label} key`}
100
+ disabled={disabled}
101
+ placeholder="Key"
102
+ value={draftKey}
103
+ onChange={(event) => setDraftKey(event.target.value)}
104
+ />
105
+ <input
106
+ type="text"
107
+ aria-label={`New ${field.label} value`}
108
+ disabled={disabled}
109
+ placeholder="Value"
110
+ value={draftValue}
111
+ onChange={(event) => setDraftValue(event.target.value)}
112
+ />
113
+ <Button
114
+ compact
115
+ type="button"
116
+ disabled={disabled || !draftKey.trim()}
117
+ onClick={() => {
118
+ commitRow(draftKey, draftValue);
119
+ setDraftKey('');
120
+ setDraftValue('');
121
+ }}
122
+ >
123
+ Add
124
+ </Button>
125
+ </div>
126
+ </div>
127
+ );
128
+ }
129
+
130
+ function JsonOptionEditor({
131
+ field,
132
+ value,
133
+ disabled,
134
+ onChange,
135
+ testId,
136
+ }: {
137
+ readonly field: TransformOptionField;
138
+ readonly value: unknown;
139
+ readonly disabled: boolean;
140
+ readonly onChange: (next: unknown) => void;
141
+ readonly testId: string;
142
+ }): JSX.Element {
143
+ const [draft, setDraft] = useState(() =>
144
+ value === undefined ? '' : JSON.stringify(value, null, 2),
145
+ );
146
+ const [error, setError] = useState<string | undefined>();
147
+
148
+ useEffect(() => {
149
+ setDraft(value === undefined ? '' : JSON.stringify(value, null, 2));
150
+ setError(undefined);
151
+ }, [value]);
152
+
153
+ return (
154
+ <div className="workbench-field-remap-options__json">
155
+ <textarea
156
+ aria-label={field.label}
157
+ data-testid={testId}
158
+ disabled={disabled}
159
+ rows={5}
160
+ value={draft}
161
+ onChange={(event) => {
162
+ setDraft(event.target.value);
163
+ setError(undefined);
164
+ }}
165
+ onBlur={(event) => {
166
+ // Read from the DOM so blur after a same-tick edit is not stale.
167
+ const trimmed = event.currentTarget.value.trim();
168
+ if (!trimmed) {
169
+ setError(undefined);
170
+ onChange(undefined);
171
+ return;
172
+ }
173
+ try {
174
+ const parsed: unknown = JSON.parse(trimmed);
175
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
176
+ setError('JSON must be a plain object.');
177
+ return;
178
+ }
179
+ setError(undefined);
180
+ setDraft(JSON.stringify(parsed, null, 2));
181
+ onChange(parsed);
182
+ } catch {
183
+ setError('Invalid JSON.');
184
+ }
185
+ }}
186
+ />
187
+ {error ? (
188
+ <p className="workbench-field-remap-options__error" role="alert">
189
+ {error}
190
+ </p>
191
+ ) : null}
192
+ </div>
193
+ );
194
+ }
195
+
196
+ /**
197
+ * Presentational editor for `TransformOptionField` kinds (string / number / boolean /
198
+ * stringMap / json). Emits a full options record; callers typically pass through
199
+ * `patchOptionStep` / `sanitizeOptionRecord`.
200
+ */
201
+ export function TransformOptionsEditor({
202
+ fields,
203
+ value,
204
+ onChange,
205
+ disabled = false,
206
+ testIdPrefix = 'field-remap-option',
207
+ }: TransformOptionsEditorProps): JSX.Element {
208
+ const baseId = useId();
209
+
210
+ if (fields.length === 0) {
211
+ return (
212
+ <p className="workbench-field-remap-options__empty" data-testid={`${testIdPrefix}-empty`}>
213
+ No options for this transform.
214
+ </p>
215
+ );
216
+ }
217
+
218
+ const setKey = (key: string, nextValue: unknown) => {
219
+ const next: Record<string, unknown> = { ...value };
220
+ if (nextValue === undefined) {
221
+ delete next[key];
222
+ } else {
223
+ next[key] = nextValue;
224
+ }
225
+ onChange(next);
226
+ };
227
+
228
+ return (
229
+ <div className="workbench-field-remap-options" data-testid={`${testIdPrefix}-editor`}>
230
+ {fields.map((field) => {
231
+ const controlId = `${baseId}-${field.key}`;
232
+ const testId = `${testIdPrefix}-${field.key}`;
233
+ const current = value[field.key];
234
+
235
+ return (
236
+ <label
237
+ key={field.key}
238
+ className="workbench-field-remap-options__field"
239
+ htmlFor={field.kind === 'boolean' ? controlId : undefined}
240
+ >
241
+ <span className="workbench-field-remap-options__label">{field.label}</span>
242
+ {field.kind === 'string' ? (
243
+ <input
244
+ id={controlId}
245
+ type="text"
246
+ data-testid={testId}
247
+ disabled={disabled}
248
+ aria-label={field.label}
249
+ value={
250
+ typeof current === 'string' ? current : current == null ? '' : String(current)
251
+ }
252
+ onChange={(event) => setKey(field.key, event.target.value)}
253
+ />
254
+ ) : null}
255
+ {field.kind === 'number' ? (
256
+ <input
257
+ id={controlId}
258
+ type="number"
259
+ data-testid={testId}
260
+ disabled={disabled}
261
+ aria-label={field.label}
262
+ value={typeof current === 'number' ? current : ''}
263
+ onChange={(event) => {
264
+ const raw = event.target.value;
265
+ if (raw === '') {
266
+ setKey(field.key, undefined);
267
+ return;
268
+ }
269
+ const parsed = Number(raw);
270
+ if (Number.isFinite(parsed)) {
271
+ setKey(field.key, parsed);
272
+ }
273
+ }}
274
+ />
275
+ ) : null}
276
+ {field.kind === 'boolean' ? (
277
+ <input
278
+ id={controlId}
279
+ type="checkbox"
280
+ data-testid={testId}
281
+ disabled={disabled}
282
+ aria-label={field.label}
283
+ checked={Boolean(current)}
284
+ onChange={(event) => setKey(field.key, event.target.checked)}
285
+ />
286
+ ) : null}
287
+ {field.kind === 'stringMap' ? (
288
+ <StringMapEditor
289
+ field={field}
290
+ value={current}
291
+ disabled={disabled}
292
+ testId={testId}
293
+ onChange={(next) =>
294
+ setKey(field.key, Object.keys(next).length > 0 ? next : undefined)
295
+ }
296
+ />
297
+ ) : null}
298
+ {field.kind === 'json' ? (
299
+ <JsonOptionEditor
300
+ field={field}
301
+ value={current}
302
+ disabled={disabled}
303
+ testId={testId}
304
+ onChange={(next) => setKey(field.key, next)}
305
+ />
306
+ ) : null}
307
+ </label>
308
+ );
309
+ })}
310
+ </div>
311
+ );
312
+ }