@workbench-kit/react 0.0.1-prototype.0 → 0.0.2-prototype.0.1.1

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 (155) hide show
  1. package/package.json +33 -9
  2. package/src/index.ts +545 -43
  3. package/src/jdw/JdwPreview.tsx +38 -0
  4. package/src/jdw/JsonCodeEditorPane.tsx +274 -0
  5. package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +29 -0
  6. package/src/jdw/builtins/renderBuiltinWidgetNode.tsx +126 -0
  7. package/src/jdw/createBuiltinJdwRegistry.ts +111 -0
  8. package/src/jdw/cssRenderBackend.tsx +134 -0
  9. package/src/jdw/document.ts +38 -0
  10. package/src/jdw/fixtures/jdw-fixtures.ts +52 -0
  11. package/src/jdw/fixtures/jdw-sample-screens.ts +522 -0
  12. package/src/jdw/index.ts +45 -0
  13. package/src/jdw/parse.ts +1 -0
  14. package/src/jdw/renderJdw.tsx +32 -0
  15. package/src/json-config/JsonConfigWorkbench.tsx +316 -0
  16. package/src/json-config/index.ts +6 -0
  17. package/src/json-config/json-config-editor-state.ts +42 -0
  18. package/src/layout/Panel.tsx +146 -1
  19. package/src/layout/SideBarViewFrame.tsx +367 -328
  20. package/src/layout/WorkbenchCanvas.tsx +849 -0
  21. package/src/layout/WorkbenchFullscreen.tsx +368 -0
  22. package/src/layout/WorkbenchLayout.tsx +5 -0
  23. package/src/layout/WorkbenchLayoutBase.tsx +294 -0
  24. package/src/layout/WorkbenchPropertyPanel.tsx +497 -0
  25. package/src/layout/WorkbenchSidebarActions.tsx +274 -0
  26. package/src/layout/WorkbenchTree.tsx +351 -0
  27. package/src/layout/layoutHelpers.ts +25 -0
  28. package/src/modal/ConfirmDialog.tsx +61 -53
  29. package/src/modal/Modal.tsx +295 -35
  30. package/src/overlay/ContextMenu.tsx +146 -145
  31. package/src/primitives/AbsoluteBox.tsx +46 -0
  32. package/src/primitives/Badge.tsx +12 -12
  33. package/src/primitives/Button.tsx +44 -14
  34. package/src/primitives/Checkbox.tsx +11 -5
  35. package/src/primitives/EmptyState.tsx +27 -26
  36. package/src/primitives/Field.tsx +38 -38
  37. package/src/primitives/IconButton.tsx +5 -2
  38. package/src/primitives/List.tsx +111 -0
  39. package/src/primitives/NumberInput.tsx +38 -0
  40. package/src/primitives/ScrollArea.tsx +42 -0
  41. package/src/primitives/Select.tsx +1 -12
  42. package/src/primitives/StatusBar.tsx +46 -0
  43. package/src/primitives/TextArea.tsx +36 -0
  44. package/src/primitives/TextInput.tsx +11 -2
  45. package/src/primitives/WorkbenchChrome.tsx +424 -0
  46. package/src/primitives/WorkbenchEditor.tsx +394 -0
  47. package/src/primitives/index.ts +105 -0
  48. package/src/primitives/select/Select.tsx +266 -0
  49. package/src/primitives/select/index.ts +2 -0
  50. package/src/primitives/select/options.ts +38 -0
  51. package/src/primitives/select/overlay.ts +63 -0
  52. package/src/primitives/select/select.app.css +9 -0
  53. package/src/primitives/select/select.css +140 -0
  54. package/src/primitives/select/types.ts +18 -0
  55. package/src/primitives/styles.css +955 -0
  56. package/src/scrollbars.css +69 -0
  57. package/src/styles.css +7738 -1762
  58. package/src/utils/codicon.ts +17 -0
  59. package/src/widget-asset/WidgetAssetEditor.tsx +140 -0
  60. package/src/widget-asset/WidgetAssetMetadataForm.tsx +82 -0
  61. package/src/widget-asset/WidgetAssetModeControls.tsx +42 -0
  62. package/src/widget-asset/WidgetAssetSourceEditor.tsx +50 -0
  63. package/src/widget-asset/WidgetAssetWorkbench.tsx +112 -0
  64. package/src/widget-asset/index.ts +17 -0
  65. package/src/widget-asset/widget-asset-document.ts +37 -0
  66. package/src/widget-asset/widget-asset-mode.ts +3 -0
  67. package/src/widget-studio/builtin-widget-asset-catalog.ts +12 -0
  68. package/src/widget-studio/create-widget-studio-workspace-editor.tsx +79 -0
  69. package/src/widget-studio/index.ts +7 -0
  70. package/src/widget-studio/resolve-widget-studio-asset-catalog.ts +26 -0
  71. package/src/widget-tree/WidgetAssetPalette.tsx +77 -0
  72. package/src/widget-tree/WidgetInspectorPanel.tsx +266 -0
  73. package/src/widget-tree/WidgetSourceEditor.tsx +56 -0
  74. package/src/widget-tree/WidgetTreeLab.tsx +272 -0
  75. package/src/widget-tree/WidgetTreeModeControls.tsx +44 -0
  76. package/src/widget-tree/WidgetTreeSidePanel.tsx +49 -0
  77. package/src/widget-tree/WidgetTreeView.tsx +97 -0
  78. package/src/widget-tree/WidgetTreeWorkbench.tsx +111 -0
  79. package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +154 -0
  80. package/src/widget-tree/demo-registry.ts +32 -0
  81. package/src/widget-tree/demo-widget-assets.ts +4 -0
  82. package/src/widget-tree/index.ts +27 -0
  83. package/src/widget-tree/widget-tree-document.ts +20 -0
  84. package/src/widget-tree/widget-tree-layout.ts +31 -0
  85. package/src/widget-tree/widget-tree-mode.ts +7 -0
  86. package/src/widget-tree/widget-tree-play-helpers.ts +74 -0
  87. package/src/workbench/ActivityBar.tsx +52 -52
  88. package/src/workbench/ArtifactShell.tsx +397 -0
  89. package/src/workbench/CommandPalette.tsx +819 -0
  90. package/src/workbench/ConfirmationFlow.tsx +245 -0
  91. package/src/workbench/MarkdownPreview.tsx +151 -0
  92. package/src/workbench/ShortcutCommandBridge.tsx +368 -0
  93. package/src/workbench/SplitView.tsx +136 -136
  94. package/src/workbench/StatusBar.tsx +178 -123
  95. package/src/workbench/StructuredArtifactEditor.tsx +205 -0
  96. package/src/workbench/Timeline.tsx +302 -0
  97. package/src/workbench/WorkbenchCanvasShell.tsx +941 -0
  98. package/src/workbench/WorkbenchShell.tsx +74 -70
  99. package/src/workbench/WorkbenchStandaloneShell.tsx +318 -291
  100. package/src/workbench/auth/WorkbenchAuthGate.tsx +61 -0
  101. package/src/workbench/auth/WorkbenchLoginView.tsx +635 -0
  102. package/src/workbench/auth/index.ts +19 -0
  103. package/src/workbench/chat/ChatComposer.tsx +165 -148
  104. package/src/workbench/chat/ChatMessageItem.tsx +10 -1
  105. package/src/workbench/chat/index.ts +9 -0
  106. package/src/workbench/chat/slashCommand.ts +28 -0
  107. package/src/workbench/chat/useChatRuntimeState.ts +76 -0
  108. package/src/workbench/chat/useSlashCommandSuggest.ts +101 -0
  109. package/src/workbench/commands.ts +520 -490
  110. package/src/workbench/index.ts +443 -98
  111. package/src/workbench/keyboard.ts +53 -0
  112. package/src/workbench/schema/index.ts +48 -0
  113. package/src/workbench/schema/workbenchDocument.ts +31 -0
  114. package/src/workbench/schema/workbenchDocumentActions.ts +6 -0
  115. package/src/workbench/schema/workbenchDocumentAdapter.ts +9 -0
  116. package/src/workbench/schema/workbenchDocumentPatch.ts +13 -0
  117. package/src/workbench/schema/workbenchDocumentRenderer.tsx +702 -0
  118. package/src/workbench/settings/NavigationPanel.tsx +47 -0
  119. package/src/workbench/settings/SchemaForm.tsx +420 -0
  120. package/src/workbench/settings/SectionedPanel.tsx +415 -0
  121. package/src/workbench/settings/StructuredDataForm.tsx +539 -0
  122. package/src/workbench/settings/StructuredDataSchemaPanel.tsx +559 -0
  123. package/src/workbench/settings/StructuredDataSchemaPanelEmbed.tsx +21 -0
  124. package/src/workbench/settings/StructuredDataSchemaPanelFrame.tsx +76 -0
  125. package/src/workbench/settings/StructuredDataTableView.tsx +127 -0
  126. package/src/workbench/settings/WorkbenchPanelRegion.tsx +46 -0
  127. package/src/workbench/settings/WorkbenchSettingsModal.tsx +21 -12
  128. package/src/workbench/settings/WorkbenchSettingsNav.tsx +38 -22
  129. package/src/workbench/settings/index.ts +135 -0
  130. package/src/workbench/settings/panel-region.css +21 -0
  131. package/src/workbench/settings/sectionedPanelScrollSpy.ts +143 -0
  132. package/src/workbench/settings/structured-data-schema-panel.css +330 -0
  133. package/src/workbench/settings/structured-data-table-view.css +85 -0
  134. package/src/workbench/settings/structuredDataSchema.ts +886 -0
  135. package/src/workbench/shell.ts +15 -0
  136. package/src/workbench/shellState.ts +203 -203
  137. package/src/workbench/standalone.ts +101 -94
  138. package/src/workbench/status.ts +129 -0
  139. package/src/workbench/theme.ts +44 -0
  140. package/src/workbench/workspace/MultiProviderExplorer.tsx +625 -0
  141. package/src/workbench/workspace/WorkspaceDraftsContext.tsx +111 -0
  142. package/src/workbench/workspace/WorkspaceEditor.tsx +47 -12
  143. package/src/workbench/workspace/WorkspaceEditorPanel.tsx +334 -353
  144. package/src/workbench/workspace/WorkspaceExplorer.tsx +532 -524
  145. package/src/workbench/workspace/WorkspaceFileIcon.tsx +32 -2
  146. package/src/workbench/workspace/WorkspaceSearchPanel.tsx +113 -113
  147. package/src/workbench/workspace/WorkspaceSearchResults.tsx +47 -47
  148. package/src/workbench/workspace/index.ts +156 -98
  149. package/src/workbench/workspace/mimeType.ts +45 -0
  150. package/src/workbench/workspace/path.ts +10 -10
  151. package/src/workbench/workspace/search.ts +6 -6
  152. package/src/workbench/workspace/tree.ts +1 -1
  153. package/src/workbench/workspace/types.ts +10 -10
  154. package/src/workbench/workspace/useVirtualWorkspace.ts +111 -98
  155. package/src/workbench/workspace/workspaceJsonDiagnostics.ts +83 -0
@@ -0,0 +1,368 @@
1
+ import { useEffect } from 'react';
2
+ import {
3
+ canExecuteCommand,
4
+ executeCommand,
5
+ type CommandRegistry,
6
+ type CommandValue,
7
+ } from '@workbench-kit/platform';
8
+
9
+ export type WorkbenchShortcutPlatform = 'linux' | 'mac' | 'unknown' | 'windows';
10
+
11
+ export interface WorkbenchShortcutCommandBinding {
12
+ commandId: string;
13
+ preventDefault?: boolean | undefined;
14
+ shortcut: string;
15
+ stopPropagation?: boolean | undefined;
16
+ }
17
+
18
+ export interface WorkbenchShortcutCommandBindingInput<TContext> {
19
+ commandIds?: readonly string[] | undefined;
20
+ context: TContext;
21
+ registry: CommandRegistry<TContext>;
22
+ }
23
+
24
+ export interface WorkbenchShortcutEventLike {
25
+ altKey?: boolean | undefined;
26
+ ctrlKey?: boolean | undefined;
27
+ key: string;
28
+ metaKey?: boolean | undefined;
29
+ preventDefault?: (() => void) | undefined;
30
+ shiftKey?: boolean | undefined;
31
+ stopPropagation?: (() => void) | undefined;
32
+ }
33
+
34
+ export interface WorkbenchShortcutMatchInput {
35
+ event: WorkbenchShortcutEventLike;
36
+ platform?: WorkbenchShortcutPlatform | undefined;
37
+ shortcut: string;
38
+ }
39
+
40
+ export interface WorkbenchShortcutCommandRunInput<
41
+ TContext,
42
+ > extends WorkbenchShortcutCommandBindingInput<TContext> {
43
+ bindings?: readonly WorkbenchShortcutCommandBinding[] | undefined;
44
+ event: WorkbenchShortcutEventLike;
45
+ platform?: WorkbenchShortcutPlatform | undefined;
46
+ preventDefault?: boolean | undefined;
47
+ preventDefaultForDisabledMatches?: boolean | undefined;
48
+ stopPropagation?: boolean | undefined;
49
+ }
50
+
51
+ export type WorkbenchShortcutCommandMissReason =
52
+ | 'disabled'
53
+ | 'missing'
54
+ | 'missing-handler'
55
+ | 'no-match';
56
+
57
+ export type WorkbenchShortcutCommandRunResult =
58
+ | {
59
+ binding: WorkbenchShortcutCommandBinding;
60
+ commandId: string;
61
+ handled: true;
62
+ reason?: undefined;
63
+ shortcut: string;
64
+ }
65
+ | {
66
+ binding?: WorkbenchShortcutCommandBinding | undefined;
67
+ commandId?: string | undefined;
68
+ handled: false;
69
+ reason: WorkbenchShortcutCommandMissReason;
70
+ shortcut?: string | undefined;
71
+ };
72
+
73
+ export interface UseWorkbenchShortcutCommandsOptions<TContext> extends Omit<
74
+ WorkbenchShortcutCommandRunInput<TContext>,
75
+ 'event'
76
+ > {
77
+ enabled?: boolean | undefined;
78
+ onShortcutCommand?: ((result: WorkbenchShortcutCommandRunResult) => void) | undefined;
79
+ target?: EventTarget | null | undefined;
80
+ useCapture?: boolean | undefined;
81
+ }
82
+
83
+ export type WorkbenchShortcutCommandBridgeProps<TContext> =
84
+ UseWorkbenchShortcutCommandsOptions<TContext>;
85
+
86
+ type ShortcutModifier = 'alt' | 'ctrl' | 'meta' | 'shift';
87
+
88
+ interface ParsedShortcut {
89
+ ctrlOrMeta: boolean;
90
+ key?: string | undefined;
91
+ modifiers: Set<ShortcutModifier>;
92
+ }
93
+
94
+ function resolveCommandValue<TContext, TValue>(
95
+ value: CommandValue<TContext, TValue> | undefined,
96
+ context: TContext,
97
+ ): TValue | undefined {
98
+ if (typeof value !== 'function') return value;
99
+ return (value as (context: TContext) => TValue)(context);
100
+ }
101
+
102
+ function getDefaultShortcutPlatform(): WorkbenchShortcutPlatform {
103
+ if (typeof navigator === 'undefined') return 'unknown';
104
+ const platform = navigator.platform.toLowerCase();
105
+ if (platform.includes('mac')) return 'mac';
106
+ if (platform.includes('win')) return 'windows';
107
+ if (platform.includes('linux')) return 'linux';
108
+ return 'unknown';
109
+ }
110
+
111
+ function normalizeKeyToken(token: string) {
112
+ const key = token.trim().toLowerCase();
113
+ if (key === 'del') return 'delete';
114
+ if (key === 'esc') return 'escape';
115
+ if (key === 'return') return 'enter';
116
+ if (key === 'spacebar' || key === 'space') return 'space';
117
+ if (key === 'arrowup' || key === 'up') return 'arrowup';
118
+ if (key === 'arrowdown' || key === 'down') return 'arrowdown';
119
+ if (key === 'arrowleft' || key === 'left') return 'arrowleft';
120
+ if (key === 'arrowright' || key === 'right') return 'arrowright';
121
+ return key;
122
+ }
123
+
124
+ function normalizeEventKey(key: string) {
125
+ if (key === ' ') return 'space';
126
+ return normalizeKeyToken(key);
127
+ }
128
+
129
+ function parseShortcut(
130
+ shortcut: string,
131
+ platform: WorkbenchShortcutPlatform,
132
+ ): ParsedShortcut | undefined {
133
+ const tokens = shortcut
134
+ .trim()
135
+ .split(/[+\s]+/)
136
+ .map((token) => token.trim())
137
+ .filter(Boolean);
138
+ if (!tokens.length) return undefined;
139
+
140
+ const parsed: ParsedShortcut = {
141
+ ctrlOrMeta: false,
142
+ modifiers: new Set(),
143
+ };
144
+
145
+ tokens.forEach((token) => {
146
+ const normalized = token.toLowerCase();
147
+ if (normalized === 'ctrl' || normalized === 'control') {
148
+ parsed.modifiers.add('ctrl');
149
+ return;
150
+ }
151
+
152
+ if (normalized === 'cmd' || normalized === 'command' || normalized === 'meta') {
153
+ parsed.modifiers.add('meta');
154
+ return;
155
+ }
156
+
157
+ if (normalized === 'alt' || normalized === 'option') {
158
+ parsed.modifiers.add('alt');
159
+ return;
160
+ }
161
+
162
+ if (normalized === 'shift') {
163
+ parsed.modifiers.add('shift');
164
+ return;
165
+ }
166
+
167
+ if (
168
+ normalized === 'ctrl/cmd' ||
169
+ normalized === 'cmd/ctrl' ||
170
+ normalized === 'ctrlcmd' ||
171
+ normalized === 'cmdorctrl'
172
+ ) {
173
+ parsed.ctrlOrMeta = true;
174
+ return;
175
+ }
176
+
177
+ if (normalized === 'mod' || normalized === 'primary') {
178
+ parsed.modifiers.add(platform === 'mac' ? 'meta' : 'ctrl');
179
+ return;
180
+ }
181
+
182
+ parsed.key = normalizeKeyToken(token);
183
+ });
184
+
185
+ return parsed.key ? parsed : undefined;
186
+ }
187
+
188
+ function hasExpectedModifiers(event: WorkbenchShortcutEventLike, parsed: ParsedShortcut) {
189
+ const ctrl = Boolean(event.ctrlKey);
190
+ const meta = Boolean(event.metaKey);
191
+
192
+ if (parsed.ctrlOrMeta) {
193
+ if (!ctrl && !meta) return false;
194
+ } else {
195
+ if (ctrl !== parsed.modifiers.has('ctrl')) return false;
196
+ if (meta !== parsed.modifiers.has('meta')) return false;
197
+ }
198
+
199
+ return (
200
+ Boolean(event.altKey) === parsed.modifiers.has('alt') &&
201
+ Boolean(event.shiftKey) === parsed.modifiers.has('shift')
202
+ );
203
+ }
204
+
205
+ export function getWorkbenchShortcutFromEvent(event: WorkbenchShortcutEventLike) {
206
+ const parts: string[] = [];
207
+ if (event.ctrlKey) parts.push('Ctrl');
208
+ if (event.metaKey) parts.push('Cmd');
209
+ if (event.altKey) parts.push('Alt');
210
+ if (event.shiftKey) parts.push('Shift');
211
+ parts.push(normalizeEventKey(event.key));
212
+ return parts.join('+');
213
+ }
214
+
215
+ export function matchesWorkbenchShortcut({
216
+ event,
217
+ platform = getDefaultShortcutPlatform(),
218
+ shortcut,
219
+ }: WorkbenchShortcutMatchInput) {
220
+ return shortcut
221
+ .split(',')
222
+ .map((candidate) => parseShortcut(candidate, platform))
223
+ .some(
224
+ (parsed) =>
225
+ Boolean(parsed) &&
226
+ parsed?.key === normalizeEventKey(event.key) &&
227
+ hasExpectedModifiers(event, parsed),
228
+ );
229
+ }
230
+
231
+ export function getWorkbenchShortcutCommandBindings<TContext>({
232
+ commandIds,
233
+ context,
234
+ registry,
235
+ }: WorkbenchShortcutCommandBindingInput<TContext>): WorkbenchShortcutCommandBinding[] {
236
+ const allowedCommandIds = commandIds ? new Set(commandIds) : undefined;
237
+
238
+ return [...registry.values()].flatMap((command) => {
239
+ if (allowedCommandIds && !allowedCommandIds.has(command.id)) return [];
240
+
241
+ const shortcut = resolveCommandValue(command.shortcut, context);
242
+ if (!shortcut) return [];
243
+
244
+ return [
245
+ {
246
+ commandId: command.id,
247
+ shortcut,
248
+ },
249
+ ];
250
+ });
251
+ }
252
+
253
+ function getUnmatchedReason<TContext>(
254
+ registry: CommandRegistry<TContext>,
255
+ commandId: string,
256
+ context: TContext,
257
+ ): WorkbenchShortcutCommandMissReason {
258
+ const command = registry.get(commandId);
259
+ if (!command) return 'missing';
260
+ if (!command.run) return 'missing-handler';
261
+ return canExecuteCommand(registry, commandId, context) ? 'no-match' : 'disabled';
262
+ }
263
+
264
+ export function runWorkbenchShortcutCommand<TContext>({
265
+ bindings,
266
+ commandIds,
267
+ context,
268
+ event,
269
+ platform,
270
+ preventDefault = true,
271
+ preventDefaultForDisabledMatches = false,
272
+ registry,
273
+ stopPropagation = false,
274
+ }: WorkbenchShortcutCommandRunInput<TContext>): WorkbenchShortcutCommandRunResult {
275
+ const resolvedBindings =
276
+ bindings ?? getWorkbenchShortcutCommandBindings({ commandIds, context, registry });
277
+ const binding = resolvedBindings.find((candidate) =>
278
+ matchesWorkbenchShortcut({ event, platform, shortcut: candidate.shortcut }),
279
+ );
280
+
281
+ if (!binding) {
282
+ return { handled: false, reason: 'no-match' };
283
+ }
284
+
285
+ if (!canExecuteCommand(registry, binding.commandId, context)) {
286
+ if (preventDefaultForDisabledMatches) event.preventDefault?.();
287
+ return {
288
+ binding,
289
+ commandId: binding.commandId,
290
+ handled: false,
291
+ reason: getUnmatchedReason(registry, binding.commandId, context),
292
+ shortcut: binding.shortcut,
293
+ };
294
+ }
295
+
296
+ if (binding.preventDefault ?? preventDefault) event.preventDefault?.();
297
+ if (binding.stopPropagation ?? stopPropagation) event.stopPropagation?.();
298
+
299
+ executeCommand(registry, binding.commandId, context);
300
+ return {
301
+ binding,
302
+ commandId: binding.commandId,
303
+ handled: true,
304
+ shortcut: binding.shortcut,
305
+ };
306
+ }
307
+
308
+ export function useWorkbenchShortcutCommands<TContext>({
309
+ bindings,
310
+ commandIds,
311
+ context,
312
+ enabled = true,
313
+ onShortcutCommand,
314
+ platform,
315
+ preventDefault,
316
+ preventDefaultForDisabledMatches,
317
+ registry,
318
+ stopPropagation,
319
+ target,
320
+ useCapture = false,
321
+ }: UseWorkbenchShortcutCommandsOptions<TContext>) {
322
+ useEffect(() => {
323
+ if (!enabled) return undefined;
324
+
325
+ const resolvedTarget = target ?? (typeof window === 'undefined' ? undefined : window);
326
+ if (!resolvedTarget) return undefined;
327
+
328
+ const listener = (event: Event) => {
329
+ const result = runWorkbenchShortcutCommand({
330
+ bindings,
331
+ commandIds,
332
+ context,
333
+ event: event as unknown as WorkbenchShortcutEventLike,
334
+ platform,
335
+ preventDefault,
336
+ preventDefaultForDisabledMatches,
337
+ registry,
338
+ stopPropagation,
339
+ });
340
+ if (result.handled) onShortcutCommand?.(result);
341
+ };
342
+
343
+ resolvedTarget.addEventListener('keydown', listener, { capture: useCapture });
344
+ return () => {
345
+ resolvedTarget.removeEventListener('keydown', listener, { capture: useCapture });
346
+ };
347
+ }, [
348
+ bindings,
349
+ commandIds,
350
+ context,
351
+ enabled,
352
+ onShortcutCommand,
353
+ platform,
354
+ preventDefault,
355
+ preventDefaultForDisabledMatches,
356
+ registry,
357
+ stopPropagation,
358
+ target,
359
+ useCapture,
360
+ ]);
361
+ }
362
+
363
+ export function WorkbenchShortcutCommandBridge<TContext>(
364
+ props: WorkbenchShortcutCommandBridgeProps<TContext>,
365
+ ) {
366
+ useWorkbenchShortcutCommands(props);
367
+ return null;
368
+ }
@@ -1,136 +1,136 @@
1
- import {
2
- useRef,
3
- useState,
4
- type CSSProperties,
5
- type KeyboardEvent,
6
- type PointerEvent,
7
- type ReactNode,
8
- } from 'react';
9
- import { cx } from '../utils/cx';
10
-
11
- export interface SplitViewProps {
12
- className?: string;
13
- defaultPrimarySizePercent?: number;
14
- keyboardStepPercent?: number;
15
- maxPrimarySizePercent?: number;
16
- minPrimarySizePercent?: number;
17
- onPrimarySizePercentChange?: (primarySizePercent: number) => void;
18
- primary: ReactNode;
19
- primarySizePercent?: number;
20
- secondary: ReactNode;
21
- }
22
-
23
- export function SplitView({
24
- className,
25
- defaultPrimarySizePercent = 40,
26
- keyboardStepPercent = 5,
27
- maxPrimarySizePercent = 85,
28
- minPrimarySizePercent = 15,
29
- onPrimarySizePercentChange,
30
- primary,
31
- primarySizePercent: controlledPrimarySizePercent,
32
- secondary,
33
- }: SplitViewProps) {
34
- const [uncontrolledPrimarySizePercent, setUncontrolledPrimarySizePercent] =
35
- useState(defaultPrimarySizePercent);
36
- const containerRef = useRef<HTMLDivElement>(null);
37
- const dragging = useRef(false);
38
- const isControlled = controlledPrimarySizePercent !== undefined;
39
-
40
- const clampPrimarySize = (value: number) =>
41
- Math.max(minPrimarySizePercent, Math.min(maxPrimarySizePercent, value));
42
-
43
- const primarySizePercent = clampPrimarySize(
44
- controlledPrimarySizePercent ?? uncontrolledPrimarySizePercent,
45
- );
46
-
47
- const commitPrimarySize = (value: number) => {
48
- const nextValue = clampPrimarySize(value);
49
- if (!isControlled) {
50
- setUncontrolledPrimarySizePercent(nextValue);
51
- }
52
- onPrimarySizePercentChange?.(nextValue);
53
- };
54
-
55
- const updatePrimarySize = (clientX: number) => {
56
- if (!containerRef.current) return;
57
- const rect = containerRef.current.getBoundingClientRect();
58
- const pct = ((clientX - rect.left) / rect.width) * 100;
59
- commitPrimarySize(pct);
60
- };
61
-
62
- const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {
63
- event.preventDefault();
64
- event.currentTarget.setPointerCapture(event.pointerId);
65
- dragging.current = true;
66
- event.currentTarget.classList.add('is-dragging');
67
- };
68
-
69
- const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {
70
- if (!dragging.current) return;
71
- updatePrimarySize(event.clientX);
72
- };
73
-
74
- const onPointerUp = (event: PointerEvent<HTMLDivElement>) => {
75
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
76
- event.currentTarget.releasePointerCapture(event.pointerId);
77
- }
78
- dragging.current = false;
79
- event.currentTarget.classList.remove('is-dragging');
80
- };
81
-
82
- const onSeparatorKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
83
- if (event.key === 'ArrowLeft') {
84
- event.preventDefault();
85
- commitPrimarySize(primarySizePercent - keyboardStepPercent);
86
- return;
87
- }
88
-
89
- if (event.key === 'ArrowRight') {
90
- event.preventDefault();
91
- commitPrimarySize(primarySizePercent + keyboardStepPercent);
92
- return;
93
- }
94
-
95
- if (event.key === 'Home') {
96
- event.preventDefault();
97
- commitPrimarySize(minPrimarySizePercent);
98
- return;
99
- }
100
-
101
- if (event.key === 'End') {
102
- event.preventDefault();
103
- commitPrimarySize(maxPrimarySizePercent);
104
- }
105
- };
106
-
107
- return (
108
- <div
109
- ref={containerRef}
110
- className={cx('ui-workbench-split-view', className)}
111
- style={
112
- {
113
- '--ui-workbench-split-primary-size': `${primarySizePercent}%`,
114
- } as CSSProperties
115
- }
116
- >
117
- <div className="ui-workbench-split-view__primary">{primary}</div>
118
- <div
119
- aria-orientation="vertical"
120
- aria-valuemax={maxPrimarySizePercent}
121
- aria-valuemin={minPrimarySizePercent}
122
- aria-valuenow={Math.round(primarySizePercent)}
123
- className="ui-workbench-split-view__separator"
124
- role="separator"
125
- tabIndex={0}
126
- onKeyDown={onSeparatorKeyDown}
127
- onPointerDown={onPointerDown}
128
- onPointerMove={onPointerMove}
129
- onPointerUp={onPointerUp}
130
- >
131
- <div className="ui-workbench-split-view__handle" />
132
- </div>
133
- <div className="ui-workbench-split-view__secondary">{secondary}</div>
134
- </div>
135
- );
136
- }
1
+ import {
2
+ useRef,
3
+ useState,
4
+ type CSSProperties,
5
+ type KeyboardEvent,
6
+ type PointerEvent,
7
+ type ReactNode,
8
+ } from 'react';
9
+ import { cx } from '../utils/cx';
10
+
11
+ export interface SplitViewProps {
12
+ className?: string;
13
+ defaultPrimarySizePercent?: number;
14
+ keyboardStepPercent?: number;
15
+ maxPrimarySizePercent?: number;
16
+ minPrimarySizePercent?: number;
17
+ onPrimarySizePercentChange?: (primarySizePercent: number) => void;
18
+ primary: ReactNode;
19
+ primarySizePercent?: number;
20
+ secondary: ReactNode;
21
+ }
22
+
23
+ export function SplitView({
24
+ className,
25
+ defaultPrimarySizePercent = 40,
26
+ keyboardStepPercent = 5,
27
+ maxPrimarySizePercent = 85,
28
+ minPrimarySizePercent = 15,
29
+ onPrimarySizePercentChange,
30
+ primary,
31
+ primarySizePercent: controlledPrimarySizePercent,
32
+ secondary,
33
+ }: SplitViewProps) {
34
+ const [uncontrolledPrimarySizePercent, setUncontrolledPrimarySizePercent] =
35
+ useState(defaultPrimarySizePercent);
36
+ const containerRef = useRef<HTMLDivElement>(null);
37
+ const dragging = useRef(false);
38
+ const isControlled = controlledPrimarySizePercent !== undefined;
39
+
40
+ const clampPrimarySize = (value: number) =>
41
+ Math.max(minPrimarySizePercent, Math.min(maxPrimarySizePercent, value));
42
+
43
+ const primarySizePercent = clampPrimarySize(
44
+ controlledPrimarySizePercent ?? uncontrolledPrimarySizePercent,
45
+ );
46
+
47
+ const commitPrimarySize = (value: number) => {
48
+ const nextValue = clampPrimarySize(value);
49
+ if (!isControlled) {
50
+ setUncontrolledPrimarySizePercent(nextValue);
51
+ }
52
+ onPrimarySizePercentChange?.(nextValue);
53
+ };
54
+
55
+ const updatePrimarySize = (clientX: number) => {
56
+ if (!containerRef.current) return;
57
+ const rect = containerRef.current.getBoundingClientRect();
58
+ const pct = ((clientX - rect.left) / rect.width) * 100;
59
+ commitPrimarySize(pct);
60
+ };
61
+
62
+ const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {
63
+ event.preventDefault();
64
+ event.currentTarget.setPointerCapture(event.pointerId);
65
+ dragging.current = true;
66
+ event.currentTarget.classList.add('is-dragging');
67
+ };
68
+
69
+ const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {
70
+ if (!dragging.current) return;
71
+ updatePrimarySize(event.clientX);
72
+ };
73
+
74
+ const onPointerUp = (event: PointerEvent<HTMLDivElement>) => {
75
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
76
+ event.currentTarget.releasePointerCapture(event.pointerId);
77
+ }
78
+ dragging.current = false;
79
+ event.currentTarget.classList.remove('is-dragging');
80
+ };
81
+
82
+ const onSeparatorKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
83
+ if (event.key === 'ArrowLeft') {
84
+ event.preventDefault();
85
+ commitPrimarySize(primarySizePercent - keyboardStepPercent);
86
+ return;
87
+ }
88
+
89
+ if (event.key === 'ArrowRight') {
90
+ event.preventDefault();
91
+ commitPrimarySize(primarySizePercent + keyboardStepPercent);
92
+ return;
93
+ }
94
+
95
+ if (event.key === 'Home') {
96
+ event.preventDefault();
97
+ commitPrimarySize(minPrimarySizePercent);
98
+ return;
99
+ }
100
+
101
+ if (event.key === 'End') {
102
+ event.preventDefault();
103
+ commitPrimarySize(maxPrimarySizePercent);
104
+ }
105
+ };
106
+
107
+ return (
108
+ <div
109
+ ref={containerRef}
110
+ className={cx('ui-workbench-split-view', className)}
111
+ style={
112
+ {
113
+ '--ui-workbench-split-primary-size': `${primarySizePercent}%`,
114
+ } as CSSProperties
115
+ }
116
+ >
117
+ <div className="ui-workbench-split-view__primary">{primary}</div>
118
+ <div
119
+ aria-orientation="vertical"
120
+ aria-valuemax={maxPrimarySizePercent}
121
+ aria-valuemin={minPrimarySizePercent}
122
+ aria-valuenow={Math.round(primarySizePercent)}
123
+ className="ui-workbench-split-view__separator"
124
+ role="separator"
125
+ tabIndex={0}
126
+ onKeyDown={onSeparatorKeyDown}
127
+ onPointerDown={onPointerDown}
128
+ onPointerMove={onPointerMove}
129
+ onPointerUp={onPointerUp}
130
+ >
131
+ <div className="ui-workbench-split-view__handle" />
132
+ </div>
133
+ <div className="ui-workbench-split-view__secondary">{secondary}</div>
134
+ </div>
135
+ );
136
+ }