@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,819 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useEffect,
|
|
3
|
+
useId,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
type ComponentPropsWithRef,
|
|
8
|
+
type KeyboardEvent,
|
|
9
|
+
type ReactNode,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import type { CommandMenuItem, ResolvedCommandMenuCommandItem } from '@workbench-kit/platform';
|
|
12
|
+
import { EmptyState } from '../primitives/EmptyState';
|
|
13
|
+
import { cxCodicon } from '../utils/codicon';
|
|
14
|
+
import { cx } from '../utils/cx';
|
|
15
|
+
import {
|
|
16
|
+
getWorkbenchStatusLabel,
|
|
17
|
+
isWorkbenchStatusBusy,
|
|
18
|
+
isWorkbenchStatusDisabled,
|
|
19
|
+
type WorkbenchStatus,
|
|
20
|
+
} from './status';
|
|
21
|
+
|
|
22
|
+
export type WorkbenchCommandStatus = WorkbenchStatus;
|
|
23
|
+
|
|
24
|
+
export interface WorkbenchCommandExecution {
|
|
25
|
+
kind: 'agent' | 'local' | 'remote' | 'composite' | (string & {});
|
|
26
|
+
label?: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type WorkbenchCommandFeedback = 'none' | 'status' | 'timeline';
|
|
30
|
+
export type WorkbenchCommandOutput = 'none' | 'message' | 'event' | 'artifact';
|
|
31
|
+
export type WorkbenchCommandSideEffect = 'none' | 'workspace-write' | 'external-write';
|
|
32
|
+
export type WorkbenchCommandRunSource = 'grouped-list' | 'list' | 'palette' | 'suggest';
|
|
33
|
+
export type WorkbenchCommandGroupBy =
|
|
34
|
+
| 'category'
|
|
35
|
+
| 'danger'
|
|
36
|
+
| 'execution'
|
|
37
|
+
| 'feedback'
|
|
38
|
+
| 'keyword'
|
|
39
|
+
| 'output'
|
|
40
|
+
| 'sideEffect'
|
|
41
|
+
| 'status';
|
|
42
|
+
|
|
43
|
+
export interface WorkbenchCommandDescriptor {
|
|
44
|
+
category?: string | undefined;
|
|
45
|
+
danger?: boolean | undefined;
|
|
46
|
+
description?: string | undefined;
|
|
47
|
+
disabled?: boolean | undefined;
|
|
48
|
+
disabledReason?: string | undefined;
|
|
49
|
+
execution?: WorkbenchCommandExecution | undefined;
|
|
50
|
+
feedback?: WorkbenchCommandFeedback | undefined;
|
|
51
|
+
icon?: string | undefined;
|
|
52
|
+
id: string;
|
|
53
|
+
keywords?: readonly string[] | undefined;
|
|
54
|
+
label: string;
|
|
55
|
+
metadata?: Record<string, unknown> | undefined;
|
|
56
|
+
output?: WorkbenchCommandOutput | undefined;
|
|
57
|
+
shortcut?: string | undefined;
|
|
58
|
+
sideEffect?: WorkbenchCommandSideEffect | undefined;
|
|
59
|
+
status?: WorkbenchCommandStatus | undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface WorkbenchCommandRunContext {
|
|
63
|
+
groupId?: string | undefined;
|
|
64
|
+
groupLabel?: string | undefined;
|
|
65
|
+
index: number;
|
|
66
|
+
query: string;
|
|
67
|
+
source: WorkbenchCommandRunSource;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface WorkbenchCommandFilterInput {
|
|
71
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
72
|
+
limit?: number | undefined;
|
|
73
|
+
query?: string | undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface WorkbenchCommandNavigationInput {
|
|
77
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
78
|
+
currentIndex: number;
|
|
79
|
+
direction: 'next' | 'previous';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WorkbenchCommandGroup {
|
|
83
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
84
|
+
id: string;
|
|
85
|
+
label: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface WorkbenchCommandGroupingInput {
|
|
89
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
90
|
+
fallbackGroupLabel?: string | undefined;
|
|
91
|
+
groupBy?: WorkbenchCommandGroupBy | undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type WorkbenchCommandDescriptorOverrides = Partial<
|
|
95
|
+
Omit<WorkbenchCommandDescriptor, 'danger' | 'disabled' | 'icon' | 'id' | 'label' | 'shortcut'>
|
|
96
|
+
>;
|
|
97
|
+
|
|
98
|
+
const commandExecutionLabels: Record<string, string> = {
|
|
99
|
+
agent: 'Agent',
|
|
100
|
+
composite: 'Composite',
|
|
101
|
+
local: 'Local',
|
|
102
|
+
remote: 'Remote',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const commandFeedbackLabels: Record<WorkbenchCommandFeedback, string> = {
|
|
106
|
+
none: 'No feedback',
|
|
107
|
+
status: 'Status',
|
|
108
|
+
timeline: 'Timeline',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const commandOutputLabels: Record<WorkbenchCommandOutput, string> = {
|
|
112
|
+
artifact: 'Artifact',
|
|
113
|
+
event: 'Event',
|
|
114
|
+
message: 'Message',
|
|
115
|
+
none: 'No output',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const commandSideEffectLabels: Record<WorkbenchCommandSideEffect, string> = {
|
|
119
|
+
'external-write': 'External write',
|
|
120
|
+
none: 'No side effect',
|
|
121
|
+
'workspace-write': 'Workspace write',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
function normalizedSearchText(value: string) {
|
|
125
|
+
return value.trim().toLocaleLowerCase();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function commandSearchText(command: WorkbenchCommandDescriptor) {
|
|
129
|
+
return normalizedSearchText(
|
|
130
|
+
[
|
|
131
|
+
command.id,
|
|
132
|
+
command.label,
|
|
133
|
+
command.description,
|
|
134
|
+
command.category,
|
|
135
|
+
command.shortcut,
|
|
136
|
+
...(command.keywords ?? []),
|
|
137
|
+
]
|
|
138
|
+
.filter(Boolean)
|
|
139
|
+
.join(' '),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function getWorkbenchCommandStatusLabel(status: WorkbenchCommandStatus) {
|
|
144
|
+
return getWorkbenchStatusLabel(status);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function getWorkbenchCommandExecutionLabel(execution: WorkbenchCommandExecution) {
|
|
148
|
+
return execution.label ?? commandExecutionLabels[execution.kind] ?? execution.kind;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function isWorkbenchCommandRunnable(command: WorkbenchCommandDescriptor) {
|
|
152
|
+
return !(command.disabled || (command.status && isWorkbenchStatusDisabled(command.status)));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function normalizeWorkbenchCommandGroupId(label: string) {
|
|
156
|
+
const normalized = normalizedSearchText(label)
|
|
157
|
+
.replace(/[^\p{L}\p{N}]+/gu, '-')
|
|
158
|
+
.replace(/^-+|-+$/g, '');
|
|
159
|
+
|
|
160
|
+
return normalized || 'group';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getWorkbenchCommandGroupLabels({
|
|
164
|
+
command,
|
|
165
|
+
fallbackGroupLabel,
|
|
166
|
+
groupBy = 'category',
|
|
167
|
+
}: {
|
|
168
|
+
command: WorkbenchCommandDescriptor;
|
|
169
|
+
fallbackGroupLabel: string;
|
|
170
|
+
groupBy?: WorkbenchCommandGroupBy | undefined;
|
|
171
|
+
}) {
|
|
172
|
+
if (groupBy === 'keyword') {
|
|
173
|
+
return command.keywords?.length ? [...command.keywords] : [fallbackGroupLabel];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (groupBy === 'category') return [command.category ?? fallbackGroupLabel];
|
|
177
|
+
if (groupBy === 'danger') return [command.danger ? 'Danger' : 'Default'];
|
|
178
|
+
if (groupBy === 'execution') {
|
|
179
|
+
return [
|
|
180
|
+
command.execution ? getWorkbenchCommandExecutionLabel(command.execution) : fallbackGroupLabel,
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
if (groupBy === 'feedback')
|
|
184
|
+
return [command.feedback ? commandFeedbackLabels[command.feedback] : fallbackGroupLabel];
|
|
185
|
+
if (groupBy === 'output')
|
|
186
|
+
return [command.output ? commandOutputLabels[command.output] : fallbackGroupLabel];
|
|
187
|
+
if (groupBy === 'sideEffect') {
|
|
188
|
+
return [command.sideEffect ? commandSideEffectLabels[command.sideEffect] : fallbackGroupLabel];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const status = command.status ?? (command.disabled ? 'disabled' : undefined);
|
|
192
|
+
return [status ? getWorkbenchCommandStatusLabel(status) : fallbackGroupLabel];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function groupWorkbenchCommands({
|
|
196
|
+
commands,
|
|
197
|
+
fallbackGroupLabel = 'Other',
|
|
198
|
+
groupBy = 'category',
|
|
199
|
+
}: WorkbenchCommandGroupingInput): WorkbenchCommandGroup[] {
|
|
200
|
+
const groups = new Map<string, { commands: WorkbenchCommandDescriptor[]; label: string }>();
|
|
201
|
+
|
|
202
|
+
commands.forEach((command) => {
|
|
203
|
+
getWorkbenchCommandGroupLabels({ command, fallbackGroupLabel, groupBy }).forEach((label) => {
|
|
204
|
+
const id = normalizeWorkbenchCommandGroupId(`${groupBy}-${label}`);
|
|
205
|
+
const group = groups.get(id) ?? { commands: [], label };
|
|
206
|
+
|
|
207
|
+
group.commands.push(command);
|
|
208
|
+
groups.set(id, group);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return Array.from(groups, ([id, group]) => ({
|
|
213
|
+
commands: group.commands,
|
|
214
|
+
id,
|
|
215
|
+
label: group.label,
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function filterWorkbenchCommands({
|
|
220
|
+
commands,
|
|
221
|
+
limit,
|
|
222
|
+
query = '',
|
|
223
|
+
}: WorkbenchCommandFilterInput) {
|
|
224
|
+
const tokens = normalizedSearchText(query).split(/\s+/).filter(Boolean);
|
|
225
|
+
const filtered = tokens.length
|
|
226
|
+
? commands.filter((command) => {
|
|
227
|
+
const searchText = commandSearchText(command);
|
|
228
|
+
return tokens.every((token) => searchText.includes(token));
|
|
229
|
+
})
|
|
230
|
+
: [...commands];
|
|
231
|
+
|
|
232
|
+
return typeof limit === 'number' ? filtered.slice(0, limit) : filtered;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function getNextWorkbenchCommandIndex({
|
|
236
|
+
commands,
|
|
237
|
+
currentIndex,
|
|
238
|
+
direction,
|
|
239
|
+
}: WorkbenchCommandNavigationInput) {
|
|
240
|
+
if (commands.length === 0) return -1;
|
|
241
|
+
|
|
242
|
+
const step = direction === 'next' ? 1 : -1;
|
|
243
|
+
const startIndex =
|
|
244
|
+
currentIndex >= 0 && currentIndex < commands.length
|
|
245
|
+
? currentIndex
|
|
246
|
+
: direction === 'next'
|
|
247
|
+
? -1
|
|
248
|
+
: 0;
|
|
249
|
+
|
|
250
|
+
for (let offset = 1; offset <= commands.length; offset += 1) {
|
|
251
|
+
const nextIndex = (startIndex + step * offset + commands.length) % commands.length;
|
|
252
|
+
const command = commands[nextIndex];
|
|
253
|
+
if (command && isWorkbenchCommandRunnable(command)) return nextIndex;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return -1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function commandMenuItemToWorkbenchCommandDescriptor(
|
|
260
|
+
item: ResolvedCommandMenuCommandItem,
|
|
261
|
+
overrides: WorkbenchCommandDescriptorOverrides = {},
|
|
262
|
+
): WorkbenchCommandDescriptor {
|
|
263
|
+
const metadata = {
|
|
264
|
+
menuItemId: item.id,
|
|
265
|
+
...overrides.metadata,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
...overrides,
|
|
270
|
+
danger: item.danger,
|
|
271
|
+
disabled: item.disabled,
|
|
272
|
+
icon: item.icon,
|
|
273
|
+
id: item.commandId,
|
|
274
|
+
label: item.label,
|
|
275
|
+
metadata,
|
|
276
|
+
shortcut: item.shortcut,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function commandMenuItemsToWorkbenchCommandDescriptors(
|
|
281
|
+
items: readonly CommandMenuItem[],
|
|
282
|
+
overridesByCommandId: Record<string, WorkbenchCommandDescriptorOverrides> = {},
|
|
283
|
+
) {
|
|
284
|
+
return items.flatMap((item) =>
|
|
285
|
+
item.type === 'command'
|
|
286
|
+
? [commandMenuItemToWorkbenchCommandDescriptor(item, overridesByCommandId[item.commandId])]
|
|
287
|
+
: [],
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function commandIcon(command: WorkbenchCommandDescriptor) {
|
|
292
|
+
if (!command.icon) return null;
|
|
293
|
+
return <i aria-hidden="true" className={cxCodicon(command.icon)} />;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function commandStatus(command: WorkbenchCommandDescriptor) {
|
|
297
|
+
const status = command.status ?? (command.disabled ? 'disabled' : 'idle');
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<span
|
|
301
|
+
aria-label={getWorkbenchCommandStatusLabel(status)}
|
|
302
|
+
className="ui-workbench-command-item__status"
|
|
303
|
+
title={getWorkbenchCommandStatusLabel(status)}
|
|
304
|
+
>
|
|
305
|
+
<span aria-hidden="true" className="ui-workbench-command-item__status-dot" />
|
|
306
|
+
</span>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function commandExecution(command: WorkbenchCommandDescriptor) {
|
|
311
|
+
if (!command.execution) return null;
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<span className="ui-workbench-command-item__chip">
|
|
315
|
+
{getWorkbenchCommandExecutionLabel(command.execution)}
|
|
316
|
+
</span>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export interface WorkbenchCommandListProps extends Omit<
|
|
321
|
+
ComponentPropsWithRef<'div'>,
|
|
322
|
+
'children' | 'onSelect'
|
|
323
|
+
> {
|
|
324
|
+
activeCommandId?: string | undefined;
|
|
325
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
326
|
+
emptyLabel?: ReactNode | undefined;
|
|
327
|
+
onActiveCommandChange?: ((commandId: string) => void) | undefined;
|
|
328
|
+
onRunCommand?:
|
|
329
|
+
| ((command: WorkbenchCommandDescriptor, context: WorkbenchCommandRunContext) => void)
|
|
330
|
+
| undefined;
|
|
331
|
+
query?: string | undefined;
|
|
332
|
+
source?: WorkbenchCommandRunSource | undefined;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function WorkbenchCommandList({
|
|
336
|
+
activeCommandId,
|
|
337
|
+
className,
|
|
338
|
+
commands,
|
|
339
|
+
emptyLabel = 'No commands available',
|
|
340
|
+
id,
|
|
341
|
+
onActiveCommandChange,
|
|
342
|
+
onRunCommand,
|
|
343
|
+
query = '',
|
|
344
|
+
source = 'list',
|
|
345
|
+
...props
|
|
346
|
+
}: WorkbenchCommandListProps) {
|
|
347
|
+
const generatedId = useId();
|
|
348
|
+
const listId = id ?? generatedId;
|
|
349
|
+
|
|
350
|
+
if (commands.length === 0) {
|
|
351
|
+
return (
|
|
352
|
+
<div id={id} className={cx('ui-workbench-command-list', className)} {...props}>
|
|
353
|
+
<EmptyState compact icon="codicon-search">
|
|
354
|
+
{emptyLabel}
|
|
355
|
+
</EmptyState>
|
|
356
|
+
</div>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<div
|
|
362
|
+
id={id}
|
|
363
|
+
aria-label={props['aria-label'] ?? 'Commands'}
|
|
364
|
+
className={cx('ui-workbench-command-list', className)}
|
|
365
|
+
role="listbox"
|
|
366
|
+
{...props}
|
|
367
|
+
>
|
|
368
|
+
{commands.map((command, index) => {
|
|
369
|
+
const active = command.id === activeCommandId;
|
|
370
|
+
const disabled = !isWorkbenchCommandRunnable(command);
|
|
371
|
+
const itemId = `${listId}-${command.id}`;
|
|
372
|
+
const descriptionId = command.description ? `${itemId}-description` : undefined;
|
|
373
|
+
const disabledReasonId = command.disabledReason ? `${itemId}-disabled` : undefined;
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<button
|
|
377
|
+
key={command.id}
|
|
378
|
+
id={itemId}
|
|
379
|
+
aria-busy={command.status && isWorkbenchStatusBusy(command.status) ? true : undefined}
|
|
380
|
+
aria-describedby={
|
|
381
|
+
[descriptionId, disabledReasonId].filter(Boolean).join(' ') || undefined
|
|
382
|
+
}
|
|
383
|
+
aria-selected={active}
|
|
384
|
+
className="ui-workbench-command-item"
|
|
385
|
+
data-active={active ? 'true' : undefined}
|
|
386
|
+
data-danger={command.danger ? 'true' : undefined}
|
|
387
|
+
data-status={command.status ?? (command.disabled ? 'disabled' : 'idle')}
|
|
388
|
+
disabled={disabled}
|
|
389
|
+
role="option"
|
|
390
|
+
type="button"
|
|
391
|
+
onClick={() => onRunCommand?.(command, { index, query, source })}
|
|
392
|
+
onMouseDown={(event) => {
|
|
393
|
+
if (source === 'suggest') event.preventDefault();
|
|
394
|
+
}}
|
|
395
|
+
onMouseEnter={() => onActiveCommandChange?.(command.id)}
|
|
396
|
+
>
|
|
397
|
+
<span className="ui-workbench-command-item__icon">{commandIcon(command)}</span>
|
|
398
|
+
<span className="ui-workbench-command-item__content">
|
|
399
|
+
<span className="ui-workbench-command-item__label">{command.label}</span>
|
|
400
|
+
{command.description ? (
|
|
401
|
+
<span id={descriptionId} className="ui-workbench-command-item__description">
|
|
402
|
+
{command.description}
|
|
403
|
+
</span>
|
|
404
|
+
) : null}
|
|
405
|
+
{command.disabledReason ? (
|
|
406
|
+
<span id={disabledReasonId} className="ui-visually-hidden">
|
|
407
|
+
{command.disabledReason}
|
|
408
|
+
</span>
|
|
409
|
+
) : null}
|
|
410
|
+
</span>
|
|
411
|
+
<span className="ui-workbench-command-item__meta">
|
|
412
|
+
{command.category ? (
|
|
413
|
+
<span className="ui-workbench-command-item__category">{command.category}</span>
|
|
414
|
+
) : null}
|
|
415
|
+
{commandExecution(command)}
|
|
416
|
+
{command.shortcut ? (
|
|
417
|
+
<kbd className="ui-workbench-command-item__shortcut">{command.shortcut}</kbd>
|
|
418
|
+
) : null}
|
|
419
|
+
{commandStatus(command)}
|
|
420
|
+
</span>
|
|
421
|
+
</button>
|
|
422
|
+
);
|
|
423
|
+
})}
|
|
424
|
+
</div>
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export interface WorkbenchCommandGroupShellProps extends Omit<
|
|
429
|
+
ComponentPropsWithRef<'div'>,
|
|
430
|
+
'children' | 'onSelect'
|
|
431
|
+
> {
|
|
432
|
+
activeCommandId?: string | undefined;
|
|
433
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
434
|
+
emptyLabel?: ReactNode | undefined;
|
|
435
|
+
fallbackGroupLabel?: string | undefined;
|
|
436
|
+
groupBy?: WorkbenchCommandGroupBy | undefined;
|
|
437
|
+
groupNavLabel?: string | undefined;
|
|
438
|
+
onActiveCommandChange?: ((commandId: string) => void) | undefined;
|
|
439
|
+
onRunCommand?:
|
|
440
|
+
| ((command: WorkbenchCommandDescriptor, context: WorkbenchCommandRunContext) => void)
|
|
441
|
+
| undefined;
|
|
442
|
+
query?: string | undefined;
|
|
443
|
+
showGroupCounts?: boolean | undefined;
|
|
444
|
+
showGroupNav?: boolean | undefined;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export function WorkbenchCommandGroupShell({
|
|
448
|
+
activeCommandId,
|
|
449
|
+
className,
|
|
450
|
+
commands,
|
|
451
|
+
emptyLabel = 'No commands available',
|
|
452
|
+
fallbackGroupLabel = 'Other',
|
|
453
|
+
groupBy = 'category',
|
|
454
|
+
groupNavLabel = 'Command groups',
|
|
455
|
+
onActiveCommandChange,
|
|
456
|
+
onRunCommand,
|
|
457
|
+
query = '',
|
|
458
|
+
showGroupCounts = true,
|
|
459
|
+
showGroupNav = true,
|
|
460
|
+
...props
|
|
461
|
+
}: WorkbenchCommandGroupShellProps) {
|
|
462
|
+
const generatedId = useId().replace(/:/g, '');
|
|
463
|
+
const filteredCommands = useMemo(
|
|
464
|
+
() => filterWorkbenchCommands({ commands, query }),
|
|
465
|
+
[commands, query],
|
|
466
|
+
);
|
|
467
|
+
const groups = useMemo(
|
|
468
|
+
() => groupWorkbenchCommands({ commands: filteredCommands, fallbackGroupLabel, groupBy }),
|
|
469
|
+
[fallbackGroupLabel, filteredCommands, groupBy],
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
if (groups.length === 0) {
|
|
473
|
+
return (
|
|
474
|
+
<div
|
|
475
|
+
className={cx('ui-workbench-command-group-shell', className)}
|
|
476
|
+
data-show-group-nav={showGroupNav ? 'true' : 'false'}
|
|
477
|
+
{...props}
|
|
478
|
+
>
|
|
479
|
+
<EmptyState compact icon="codicon-symbol-keyword">
|
|
480
|
+
{emptyLabel}
|
|
481
|
+
</EmptyState>
|
|
482
|
+
</div>
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return (
|
|
487
|
+
<div
|
|
488
|
+
className={cx('ui-workbench-command-group-shell', className)}
|
|
489
|
+
data-show-group-nav={showGroupNav ? 'true' : 'false'}
|
|
490
|
+
{...props}
|
|
491
|
+
>
|
|
492
|
+
{showGroupNav ? (
|
|
493
|
+
<nav aria-label={groupNavLabel} className="ui-workbench-command-group-shell__nav">
|
|
494
|
+
{groups.map((group) => (
|
|
495
|
+
<a
|
|
496
|
+
key={group.id}
|
|
497
|
+
className="ui-workbench-command-group-shell__nav-link"
|
|
498
|
+
href={`#${generatedId}-${group.id}`}
|
|
499
|
+
>
|
|
500
|
+
<span>{group.label}</span>
|
|
501
|
+
{showGroupCounts ? <em>{group.commands.length}</em> : null}
|
|
502
|
+
</a>
|
|
503
|
+
))}
|
|
504
|
+
</nav>
|
|
505
|
+
) : null}
|
|
506
|
+
<div className="ui-workbench-command-group-shell__content">
|
|
507
|
+
{groups.map((group) => {
|
|
508
|
+
const titleId = `${generatedId}-${group.id}-title`;
|
|
509
|
+
|
|
510
|
+
return (
|
|
511
|
+
<section
|
|
512
|
+
key={group.id}
|
|
513
|
+
id={`${generatedId}-${group.id}`}
|
|
514
|
+
className="ui-workbench-command-group-shell__section"
|
|
515
|
+
aria-labelledby={titleId}
|
|
516
|
+
>
|
|
517
|
+
<div className="ui-workbench-command-group-shell__section-header">
|
|
518
|
+
<h2 id={titleId}>{group.label}</h2>
|
|
519
|
+
{showGroupCounts ? <span>{group.commands.length}</span> : null}
|
|
520
|
+
</div>
|
|
521
|
+
<WorkbenchCommandList
|
|
522
|
+
activeCommandId={activeCommandId}
|
|
523
|
+
aria-label={`${group.label} commands`}
|
|
524
|
+
commands={group.commands}
|
|
525
|
+
emptyLabel={emptyLabel}
|
|
526
|
+
query={query}
|
|
527
|
+
source="grouped-list"
|
|
528
|
+
onActiveCommandChange={onActiveCommandChange}
|
|
529
|
+
onRunCommand={(command, context) =>
|
|
530
|
+
onRunCommand?.(command, {
|
|
531
|
+
...context,
|
|
532
|
+
groupId: group.id,
|
|
533
|
+
groupLabel: group.label,
|
|
534
|
+
})
|
|
535
|
+
}
|
|
536
|
+
/>
|
|
537
|
+
</section>
|
|
538
|
+
);
|
|
539
|
+
})}
|
|
540
|
+
</div>
|
|
541
|
+
</div>
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function useControllableCommandQuery({
|
|
546
|
+
defaultQuery = '',
|
|
547
|
+
query,
|
|
548
|
+
onQueryChange,
|
|
549
|
+
}: {
|
|
550
|
+
defaultQuery?: string | undefined;
|
|
551
|
+
query?: string | undefined;
|
|
552
|
+
onQueryChange?: ((query: string) => void) | undefined;
|
|
553
|
+
}) {
|
|
554
|
+
const [uncontrolledQuery, setUncontrolledQuery] = useState(defaultQuery);
|
|
555
|
+
const resolvedQuery = query ?? uncontrolledQuery;
|
|
556
|
+
|
|
557
|
+
const setQuery = (nextQuery: string) => {
|
|
558
|
+
if (query === undefined) {
|
|
559
|
+
setUncontrolledQuery(nextQuery);
|
|
560
|
+
}
|
|
561
|
+
onQueryChange?.(nextQuery);
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
return [resolvedQuery, setQuery] as const;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export interface WorkbenchCommandPaletteProps extends Omit<
|
|
568
|
+
ComponentPropsWithRef<'div'>,
|
|
569
|
+
'children' | 'onSelect' | 'title'
|
|
570
|
+
> {
|
|
571
|
+
activeCommandId?: string | undefined;
|
|
572
|
+
closeLabel?: string | undefined;
|
|
573
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
574
|
+
defaultQuery?: string | undefined;
|
|
575
|
+
emptyLabel?: ReactNode | undefined;
|
|
576
|
+
onActiveCommandChange?: ((commandId: string) => void) | undefined;
|
|
577
|
+
onClose: () => void;
|
|
578
|
+
onQueryChange?: ((query: string) => void) | undefined;
|
|
579
|
+
onRunCommand?:
|
|
580
|
+
| ((command: WorkbenchCommandDescriptor, context: WorkbenchCommandRunContext) => void)
|
|
581
|
+
| undefined;
|
|
582
|
+
open?: boolean | undefined;
|
|
583
|
+
placeholder?: string | undefined;
|
|
584
|
+
query?: string | undefined;
|
|
585
|
+
restoreFocusOnClose?: boolean | undefined;
|
|
586
|
+
title?: ReactNode | undefined;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
export function WorkbenchCommandPalette({
|
|
590
|
+
activeCommandId,
|
|
591
|
+
className,
|
|
592
|
+
closeLabel = 'Close command palette',
|
|
593
|
+
commands,
|
|
594
|
+
defaultQuery,
|
|
595
|
+
emptyLabel = 'No commands match your search',
|
|
596
|
+
onActiveCommandChange,
|
|
597
|
+
onClose,
|
|
598
|
+
onQueryChange,
|
|
599
|
+
onRunCommand,
|
|
600
|
+
open = true,
|
|
601
|
+
placeholder = 'Search commands',
|
|
602
|
+
query,
|
|
603
|
+
restoreFocusOnClose = true,
|
|
604
|
+
title = 'Command Palette',
|
|
605
|
+
...props
|
|
606
|
+
}: WorkbenchCommandPaletteProps) {
|
|
607
|
+
const titleId = useId();
|
|
608
|
+
const listId = useId();
|
|
609
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
610
|
+
const previousFocusRef = useRef<HTMLElement | null>(null);
|
|
611
|
+
const [uncontrolledActiveCommandId, setUncontrolledActiveCommandId] = useState<string>();
|
|
612
|
+
const [resolvedQuery, setResolvedQuery] = useControllableCommandQuery({
|
|
613
|
+
defaultQuery,
|
|
614
|
+
onQueryChange,
|
|
615
|
+
query,
|
|
616
|
+
});
|
|
617
|
+
const filteredCommands = useMemo(
|
|
618
|
+
() => filterWorkbenchCommands({ commands, query: resolvedQuery }),
|
|
619
|
+
[commands, resolvedQuery],
|
|
620
|
+
);
|
|
621
|
+
const resolvedActiveCommandId = activeCommandId ?? uncontrolledActiveCommandId;
|
|
622
|
+
const activeIndex = filteredCommands.findIndex(
|
|
623
|
+
(command) => command.id === resolvedActiveCommandId,
|
|
624
|
+
);
|
|
625
|
+
const activeCommand =
|
|
626
|
+
activeIndex >= 0
|
|
627
|
+
? filteredCommands[activeIndex]
|
|
628
|
+
: filteredCommands.find((command) => isWorkbenchCommandRunnable(command));
|
|
629
|
+
|
|
630
|
+
const updateActiveCommand = (commandId: string | undefined) => {
|
|
631
|
+
if (!commandId) return;
|
|
632
|
+
if (activeCommandId === undefined) {
|
|
633
|
+
setUncontrolledActiveCommandId(commandId);
|
|
634
|
+
}
|
|
635
|
+
onActiveCommandChange?.(commandId);
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
const restoreFocus = () => {
|
|
639
|
+
if (!restoreFocusOnClose) return;
|
|
640
|
+
previousFocusRef.current?.focus();
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
const closePalette = () => {
|
|
644
|
+
restoreFocus();
|
|
645
|
+
onClose();
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
const runCommand = (command: WorkbenchCommandDescriptor, context: WorkbenchCommandRunContext) => {
|
|
649
|
+
if (!isWorkbenchCommandRunnable(command)) return;
|
|
650
|
+
onRunCommand?.(command, context);
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
useEffect(() => {
|
|
654
|
+
if (!open) return;
|
|
655
|
+
|
|
656
|
+
previousFocusRef.current = document.activeElement as HTMLElement | null;
|
|
657
|
+
const frame = window.requestAnimationFrame(() => inputRef.current?.focus());
|
|
658
|
+
|
|
659
|
+
return () => {
|
|
660
|
+
window.cancelAnimationFrame(frame);
|
|
661
|
+
restoreFocus();
|
|
662
|
+
};
|
|
663
|
+
}, [open]);
|
|
664
|
+
|
|
665
|
+
useEffect(() => {
|
|
666
|
+
if (!open) return;
|
|
667
|
+
if (filteredCommands.length === 0) return;
|
|
668
|
+
if (activeCommand && isWorkbenchCommandRunnable(activeCommand)) return;
|
|
669
|
+
|
|
670
|
+
const nextIndex = getNextWorkbenchCommandIndex({
|
|
671
|
+
commands: filteredCommands,
|
|
672
|
+
currentIndex: -1,
|
|
673
|
+
direction: 'next',
|
|
674
|
+
});
|
|
675
|
+
updateActiveCommand(filteredCommands[nextIndex]?.id);
|
|
676
|
+
}, [activeCommand, filteredCommands, open]);
|
|
677
|
+
|
|
678
|
+
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
679
|
+
if (event.key === 'Escape') {
|
|
680
|
+
event.preventDefault();
|
|
681
|
+
closePalette();
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp' && event.key !== 'Enter') return;
|
|
686
|
+
|
|
687
|
+
if (event.key === 'Enter') {
|
|
688
|
+
if (!activeCommand) return;
|
|
689
|
+
event.preventDefault();
|
|
690
|
+
runCommand(activeCommand, {
|
|
691
|
+
index: filteredCommands.findIndex((command) => command.id === activeCommand.id),
|
|
692
|
+
query: resolvedQuery,
|
|
693
|
+
source: 'palette',
|
|
694
|
+
});
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const nextIndex = getNextWorkbenchCommandIndex({
|
|
699
|
+
commands: filteredCommands,
|
|
700
|
+
currentIndex: activeIndex,
|
|
701
|
+
direction: event.key === 'ArrowDown' ? 'next' : 'previous',
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
if (nextIndex >= 0) {
|
|
705
|
+
event.preventDefault();
|
|
706
|
+
updateActiveCommand(filteredCommands[nextIndex]?.id);
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
if (!open) return null;
|
|
711
|
+
|
|
712
|
+
return (
|
|
713
|
+
<div className="ui-workbench-command-palette-overlay" onClick={closePalette}>
|
|
714
|
+
<div
|
|
715
|
+
aria-labelledby={titleId}
|
|
716
|
+
aria-modal="true"
|
|
717
|
+
className={cx('ui-workbench-command-palette', className)}
|
|
718
|
+
role="dialog"
|
|
719
|
+
{...props}
|
|
720
|
+
onClick={(event) => event.stopPropagation()}
|
|
721
|
+
>
|
|
722
|
+
<div className="ui-workbench-command-palette__header">
|
|
723
|
+
<span id={titleId} className="ui-workbench-command-palette__title">
|
|
724
|
+
{title}
|
|
725
|
+
</span>
|
|
726
|
+
<button
|
|
727
|
+
aria-label={closeLabel}
|
|
728
|
+
className="ui-workbench-command-palette__close"
|
|
729
|
+
type="button"
|
|
730
|
+
onClick={closePalette}
|
|
731
|
+
>
|
|
732
|
+
<i aria-hidden="true" className="codicon codicon-close" />
|
|
733
|
+
</button>
|
|
734
|
+
</div>
|
|
735
|
+
<div className="ui-workbench-command-palette__search">
|
|
736
|
+
<i aria-hidden="true" className="codicon codicon-search" />
|
|
737
|
+
<input
|
|
738
|
+
ref={inputRef}
|
|
739
|
+
aria-controls={listId}
|
|
740
|
+
aria-label={placeholder}
|
|
741
|
+
className="ui-workbench-command-palette__input"
|
|
742
|
+
placeholder={placeholder}
|
|
743
|
+
type="search"
|
|
744
|
+
value={resolvedQuery}
|
|
745
|
+
onChange={(event) => setResolvedQuery(event.currentTarget.value)}
|
|
746
|
+
onKeyDown={handleKeyDown}
|
|
747
|
+
/>
|
|
748
|
+
</div>
|
|
749
|
+
<WorkbenchCommandList
|
|
750
|
+
id={listId}
|
|
751
|
+
activeCommandId={activeCommand?.id}
|
|
752
|
+
aria-label="Command palette results"
|
|
753
|
+
commands={filteredCommands}
|
|
754
|
+
emptyLabel={emptyLabel}
|
|
755
|
+
query={resolvedQuery}
|
|
756
|
+
source="palette"
|
|
757
|
+
onActiveCommandChange={updateActiveCommand}
|
|
758
|
+
onRunCommand={runCommand}
|
|
759
|
+
/>
|
|
760
|
+
</div>
|
|
761
|
+
</div>
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
export interface WorkbenchCommandSuggestProps extends Omit<
|
|
766
|
+
ComponentPropsWithRef<'div'>,
|
|
767
|
+
'children' | 'onSelect'
|
|
768
|
+
> {
|
|
769
|
+
activeCommandId?: string | undefined;
|
|
770
|
+
commands: readonly WorkbenchCommandDescriptor[];
|
|
771
|
+
emptyLabel?: ReactNode | undefined;
|
|
772
|
+
onActiveCommandChange?: ((commandId: string) => void) | undefined;
|
|
773
|
+
onRunCommand?:
|
|
774
|
+
| ((command: WorkbenchCommandDescriptor, context: WorkbenchCommandRunContext) => void)
|
|
775
|
+
| undefined;
|
|
776
|
+
query?: string | undefined;
|
|
777
|
+
visible?: boolean | undefined;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
export function WorkbenchCommandSuggest({
|
|
781
|
+
activeCommandId,
|
|
782
|
+
className,
|
|
783
|
+
commands,
|
|
784
|
+
emptyLabel = 'No suggested commands',
|
|
785
|
+
onActiveCommandChange,
|
|
786
|
+
onRunCommand,
|
|
787
|
+
query = '',
|
|
788
|
+
visible = true,
|
|
789
|
+
...props
|
|
790
|
+
}: WorkbenchCommandSuggestProps) {
|
|
791
|
+
const filteredCommands = useMemo(
|
|
792
|
+
() => filterWorkbenchCommands({ commands, limit: 8, query }),
|
|
793
|
+
[commands, query],
|
|
794
|
+
);
|
|
795
|
+
const fallbackActiveCommand = filteredCommands.find((command) =>
|
|
796
|
+
isWorkbenchCommandRunnable(command),
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
if (!visible) return null;
|
|
800
|
+
|
|
801
|
+
return (
|
|
802
|
+
<div className={cx('ui-workbench-command-suggest', className)} {...props}>
|
|
803
|
+
<div className="ui-workbench-command-suggest__header">
|
|
804
|
+
<span>Commands</span>
|
|
805
|
+
{query ? <span className="ui-workbench-command-suggest__query">/{query}</span> : null}
|
|
806
|
+
</div>
|
|
807
|
+
<WorkbenchCommandList
|
|
808
|
+
activeCommandId={activeCommandId ?? fallbackActiveCommand?.id}
|
|
809
|
+
aria-label="Suggested commands"
|
|
810
|
+
commands={filteredCommands}
|
|
811
|
+
emptyLabel={emptyLabel}
|
|
812
|
+
query={query}
|
|
813
|
+
source="suggest"
|
|
814
|
+
onActiveCommandChange={onActiveCommandChange}
|
|
815
|
+
onRunCommand={onRunCommand}
|
|
816
|
+
/>
|
|
817
|
+
</div>
|
|
818
|
+
);
|
|
819
|
+
}
|