@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.
- package/package.json +33 -9
- package/src/index.ts +545 -43
- package/src/jdw/JdwPreview.tsx +38 -0
- package/src/jdw/JsonCodeEditorPane.tsx +274 -0
- package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +29 -0
- package/src/jdw/builtins/renderBuiltinWidgetNode.tsx +126 -0
- package/src/jdw/createBuiltinJdwRegistry.ts +111 -0
- package/src/jdw/cssRenderBackend.tsx +134 -0
- package/src/jdw/document.ts +38 -0
- package/src/jdw/fixtures/jdw-fixtures.ts +52 -0
- package/src/jdw/fixtures/jdw-sample-screens.ts +522 -0
- package/src/jdw/index.ts +45 -0
- package/src/jdw/parse.ts +1 -0
- package/src/jdw/renderJdw.tsx +32 -0
- package/src/json-config/JsonConfigWorkbench.tsx +316 -0
- package/src/json-config/index.ts +6 -0
- package/src/json-config/json-config-editor-state.ts +42 -0
- package/src/layout/Panel.tsx +146 -1
- package/src/layout/SideBarViewFrame.tsx +367 -328
- package/src/layout/WorkbenchCanvas.tsx +849 -0
- package/src/layout/WorkbenchFullscreen.tsx +368 -0
- package/src/layout/WorkbenchLayout.tsx +5 -0
- package/src/layout/WorkbenchLayoutBase.tsx +294 -0
- package/src/layout/WorkbenchPropertyPanel.tsx +497 -0
- package/src/layout/WorkbenchSidebarActions.tsx +274 -0
- package/src/layout/WorkbenchTree.tsx +351 -0
- package/src/layout/layoutHelpers.ts +25 -0
- package/src/modal/ConfirmDialog.tsx +61 -53
- package/src/modal/Modal.tsx +295 -35
- package/src/overlay/ContextMenu.tsx +146 -145
- package/src/primitives/AbsoluteBox.tsx +46 -0
- package/src/primitives/Badge.tsx +12 -12
- package/src/primitives/Button.tsx +44 -14
- package/src/primitives/Checkbox.tsx +11 -5
- package/src/primitives/EmptyState.tsx +27 -26
- package/src/primitives/Field.tsx +38 -38
- package/src/primitives/IconButton.tsx +5 -2
- package/src/primitives/List.tsx +111 -0
- package/src/primitives/NumberInput.tsx +38 -0
- package/src/primitives/ScrollArea.tsx +42 -0
- package/src/primitives/Select.tsx +1 -12
- package/src/primitives/StatusBar.tsx +46 -0
- package/src/primitives/TextArea.tsx +36 -0
- package/src/primitives/TextInput.tsx +11 -2
- package/src/primitives/WorkbenchChrome.tsx +424 -0
- package/src/primitives/WorkbenchEditor.tsx +394 -0
- package/src/primitives/index.ts +105 -0
- package/src/primitives/select/Select.tsx +266 -0
- package/src/primitives/select/index.ts +2 -0
- package/src/primitives/select/options.ts +38 -0
- package/src/primitives/select/overlay.ts +63 -0
- package/src/primitives/select/select.app.css +9 -0
- package/src/primitives/select/select.css +140 -0
- package/src/primitives/select/types.ts +18 -0
- package/src/primitives/styles.css +955 -0
- package/src/scrollbars.css +69 -0
- package/src/styles.css +7738 -1762
- package/src/utils/codicon.ts +17 -0
- package/src/widget-asset/WidgetAssetEditor.tsx +140 -0
- package/src/widget-asset/WidgetAssetMetadataForm.tsx +82 -0
- package/src/widget-asset/WidgetAssetModeControls.tsx +42 -0
- package/src/widget-asset/WidgetAssetSourceEditor.tsx +50 -0
- package/src/widget-asset/WidgetAssetWorkbench.tsx +112 -0
- package/src/widget-asset/index.ts +17 -0
- package/src/widget-asset/widget-asset-document.ts +37 -0
- package/src/widget-asset/widget-asset-mode.ts +3 -0
- package/src/widget-studio/builtin-widget-asset-catalog.ts +12 -0
- package/src/widget-studio/create-widget-studio-workspace-editor.tsx +79 -0
- package/src/widget-studio/index.ts +7 -0
- package/src/widget-studio/resolve-widget-studio-asset-catalog.ts +26 -0
- package/src/widget-tree/WidgetAssetPalette.tsx +77 -0
- package/src/widget-tree/WidgetInspectorPanel.tsx +266 -0
- package/src/widget-tree/WidgetSourceEditor.tsx +56 -0
- package/src/widget-tree/WidgetTreeLab.tsx +272 -0
- package/src/widget-tree/WidgetTreeModeControls.tsx +44 -0
- package/src/widget-tree/WidgetTreeSidePanel.tsx +49 -0
- package/src/widget-tree/WidgetTreeView.tsx +97 -0
- package/src/widget-tree/WidgetTreeWorkbench.tsx +111 -0
- package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +154 -0
- package/src/widget-tree/demo-registry.ts +32 -0
- package/src/widget-tree/demo-widget-assets.ts +4 -0
- package/src/widget-tree/index.ts +27 -0
- package/src/widget-tree/widget-tree-document.ts +20 -0
- package/src/widget-tree/widget-tree-layout.ts +31 -0
- package/src/widget-tree/widget-tree-mode.ts +7 -0
- package/src/widget-tree/widget-tree-play-helpers.ts +74 -0
- package/src/workbench/ActivityBar.tsx +52 -52
- package/src/workbench/ArtifactShell.tsx +397 -0
- package/src/workbench/CommandPalette.tsx +819 -0
- package/src/workbench/ConfirmationFlow.tsx +245 -0
- package/src/workbench/MarkdownPreview.tsx +151 -0
- package/src/workbench/ShortcutCommandBridge.tsx +368 -0
- package/src/workbench/SplitView.tsx +136 -136
- package/src/workbench/StatusBar.tsx +178 -123
- package/src/workbench/StructuredArtifactEditor.tsx +205 -0
- package/src/workbench/Timeline.tsx +302 -0
- package/src/workbench/WorkbenchCanvasShell.tsx +941 -0
- package/src/workbench/WorkbenchShell.tsx +74 -70
- package/src/workbench/WorkbenchStandaloneShell.tsx +318 -291
- package/src/workbench/auth/WorkbenchAuthGate.tsx +61 -0
- package/src/workbench/auth/WorkbenchLoginView.tsx +635 -0
- package/src/workbench/auth/index.ts +19 -0
- package/src/workbench/chat/ChatComposer.tsx +165 -148
- package/src/workbench/chat/ChatMessageItem.tsx +10 -1
- package/src/workbench/chat/index.ts +9 -0
- package/src/workbench/chat/slashCommand.ts +28 -0
- package/src/workbench/chat/useChatRuntimeState.ts +76 -0
- package/src/workbench/chat/useSlashCommandSuggest.ts +101 -0
- package/src/workbench/commands.ts +520 -490
- package/src/workbench/index.ts +443 -98
- package/src/workbench/keyboard.ts +53 -0
- package/src/workbench/schema/index.ts +48 -0
- package/src/workbench/schema/workbenchDocument.ts +31 -0
- package/src/workbench/schema/workbenchDocumentActions.ts +6 -0
- package/src/workbench/schema/workbenchDocumentAdapter.ts +9 -0
- package/src/workbench/schema/workbenchDocumentPatch.ts +13 -0
- package/src/workbench/schema/workbenchDocumentRenderer.tsx +702 -0
- package/src/workbench/settings/NavigationPanel.tsx +47 -0
- package/src/workbench/settings/SchemaForm.tsx +420 -0
- package/src/workbench/settings/SectionedPanel.tsx +415 -0
- package/src/workbench/settings/StructuredDataForm.tsx +539 -0
- package/src/workbench/settings/StructuredDataSchemaPanel.tsx +559 -0
- package/src/workbench/settings/StructuredDataSchemaPanelEmbed.tsx +21 -0
- package/src/workbench/settings/StructuredDataSchemaPanelFrame.tsx +76 -0
- package/src/workbench/settings/StructuredDataTableView.tsx +127 -0
- package/src/workbench/settings/WorkbenchPanelRegion.tsx +46 -0
- package/src/workbench/settings/WorkbenchSettingsModal.tsx +21 -12
- package/src/workbench/settings/WorkbenchSettingsNav.tsx +38 -22
- package/src/workbench/settings/index.ts +135 -0
- package/src/workbench/settings/panel-region.css +21 -0
- package/src/workbench/settings/sectionedPanelScrollSpy.ts +143 -0
- package/src/workbench/settings/structured-data-schema-panel.css +330 -0
- package/src/workbench/settings/structured-data-table-view.css +85 -0
- package/src/workbench/settings/structuredDataSchema.ts +886 -0
- package/src/workbench/shell.ts +15 -0
- package/src/workbench/shellState.ts +203 -203
- package/src/workbench/standalone.ts +101 -94
- package/src/workbench/status.ts +129 -0
- package/src/workbench/theme.ts +44 -0
- package/src/workbench/workspace/MultiProviderExplorer.tsx +625 -0
- package/src/workbench/workspace/WorkspaceDraftsContext.tsx +111 -0
- package/src/workbench/workspace/WorkspaceEditor.tsx +47 -12
- package/src/workbench/workspace/WorkspaceEditorPanel.tsx +334 -353
- package/src/workbench/workspace/WorkspaceExplorer.tsx +532 -524
- package/src/workbench/workspace/WorkspaceFileIcon.tsx +32 -2
- package/src/workbench/workspace/WorkspaceSearchPanel.tsx +113 -113
- package/src/workbench/workspace/WorkspaceSearchResults.tsx +47 -47
- package/src/workbench/workspace/index.ts +156 -98
- package/src/workbench/workspace/mimeType.ts +45 -0
- package/src/workbench/workspace/path.ts +10 -10
- package/src/workbench/workspace/search.ts +6 -6
- package/src/workbench/workspace/tree.ts +1 -1
- package/src/workbench/workspace/types.ts +10 -10
- package/src/workbench/workspace/useVirtualWorkspace.ts +111 -98
- 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
|
+
}
|