@workbench-kit/shell-react 0.0.2-prototype.0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/package.json +51 -0
- package/src/chat/ai-mock-proposals.ts +39 -0
- package/src/chat/command-input.ts +52 -0
- package/src/chat/command-policy.ts +54 -0
- package/src/chat/command-surface.tsx +155 -0
- package/src/chat/use-chat-command-proposals.ts +194 -0
- package/src/chat/view-data.ts +20 -0
- package/src/chat/view.tsx +220 -0
- package/src/commands/inspector-surface.tsx +46 -0
- package/src/commands/use-command-descriptors.ts +41 -0
- package/src/commands/use-context-key-revision.ts +18 -0
- package/src/commands/view-data.ts +17 -0
- package/src/commands/view.tsx +43 -0
- package/src/devtools/WorkbenchDevtoolsPanel.tsx +124 -0
- package/src/devtools/WorkbenchDevtoolsShell.tsx +17 -0
- package/src/devtools/index.ts +11 -0
- package/src/devtools/use-workbench-devtools-snapshot.ts +84 -0
- package/src/devtools/workbench-devtools-snapshot.ts +140 -0
- package/src/devtools/workbench-devtools.css +82 -0
- package/src/editor/area-dnd.ts +69 -0
- package/src/editor/area-model.ts +71 -0
- package/src/editor/area.css +454 -0
- package/src/editor/area.tsx +676 -0
- package/src/editor/host-surface.tsx +691 -0
- package/src/editor/markdown-document-view-data.ts +16 -0
- package/src/editor/pane-visibility.ts +130 -0
- package/src/editor/resource.ts +36 -0
- package/src/editor/state-storage.ts +182 -0
- package/src/editor/tab-context-menu.ts +118 -0
- package/src/editor/use-editor.ts +138 -0
- package/src/editor/view-providers.tsx +504 -0
- package/src/editor/workspace-file-availability.ts +15 -0
- package/src/editor/workspace-reconcile.tsx +31 -0
- package/src/explorer/context-menu.ts +114 -0
- package/src/explorer/create-command-workspace-explorer-port.ts +86 -0
- package/src/explorer/reveal.ts +112 -0
- package/src/explorer/search-view-data.ts +15 -0
- package/src/explorer/search-view.tsx +65 -0
- package/src/explorer/use-active-workspace-path.ts +15 -0
- package/src/explorer/view-data.ts +58 -0
- package/src/explorer/view.tsx +227 -0
- package/src/extensions/context-menu.ts +52 -0
- package/src/extensions/management-model.ts +313 -0
- package/src/extensions/use-extension-management.ts +241 -0
- package/src/extensions/view-data.ts +19 -0
- package/src/extensions/view.tsx +63 -0
- package/src/field-remap/chrome-labels.ts +74 -0
- package/src/field-remap/convert-note-editor.tsx +206 -0
- package/src/field-remap/convert-palette.tsx +126 -0
- package/src/field-remap/demo.tsx +105 -0
- package/src/field-remap/detail-panel.tsx +659 -0
- package/src/field-remap/editor-surface.tsx +36 -0
- package/src/field-remap/flow-adapter.ts +838 -0
- package/src/field-remap/flow-ops.ts +442 -0
- package/src/field-remap/flow.tsx +948 -0
- package/src/field-remap/graph.tsx +559 -0
- package/src/field-remap/index.ts +88 -0
- package/src/field-remap/io-class-browse.tsx +145 -0
- package/src/field-remap/jsonata-transform.ts +166 -0
- package/src/field-remap/panel.tsx +466 -0
- package/src/field-remap/samples.ts +507 -0
- package/src/field-remap/shape-io-editor.tsx +170 -0
- package/src/field-remap/table-mapping-adapter.ts +59 -0
- package/src/field-remap/transform-options-editor.tsx +312 -0
- package/src/field-remap/tree.tsx +404 -0
- package/src/field-remap/view-data.ts +16 -0
- package/src/field-remap/view.css +1367 -0
- package/src/field-remap/view.tsx +86 -0
- package/src/index.ts +294 -0
- package/src/jdw/document-view-data.ts +31 -0
- package/src/jdw/json-form-source-patch.ts +323 -0
- package/src/jdw/lab-view-data.ts +25 -0
- package/src/jdw/lab-view.css +21 -0
- package/src/jdw/lab-view.tsx +79 -0
- package/src/jdw/widget-form-view.tsx +68 -0
- package/src/jdw/widget-preview-view.tsx +54 -0
- package/src/management/keybinding-overrides-storage.ts +49 -0
- package/src/management/keybinding-settings.tsx +16 -0
- package/src/management/palette-commands.ts +38 -0
- package/src/management/preference-settings-storage.ts +50 -0
- package/src/management/settings-ids.ts +12 -0
- package/src/management/settings.tsx +96 -0
- package/src/management/use-command-management.ts +191 -0
- package/src/management/use-keybinding-management.ts +130 -0
- package/src/shell/model.tsx +149 -0
- package/src/shell/provider.tsx +606 -0
- package/src/shell/secondary-actions.tsx +59 -0
- package/src/shell/settings-constants.ts +9 -0
- package/src/shell/settings.tsx +599 -0
- package/src/shell/shell.tsx +680 -0
- package/src/shell/titlebar-layout-controls.tsx +4 -0
- package/src/shell/view-host.tsx +212 -0
- package/src/workbench/appearance-storage.ts +107 -0
- package/src/workbench/command-host.tsx +243 -0
- package/src/workbench/command-palette.ts +208 -0
- package/src/workbench/keybinding-bridge.ts +54 -0
- package/src/workbench/layout-storage.ts +134 -0
- package/src/workbench/profile-modal.tsx +118 -0
- package/src/workbench/shell-command-registration.ts +58 -0
- package/src/workbench/startup-gate.tsx +136 -0
- package/src/workbench/status-sections.ts +43 -0
- package/src/workbench/use-persisted-appearance.ts +33 -0
- package/src/workbench/user-commands.ts +46 -0
- package/src/workbench/workspace-view-state.ts +35 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseWorkbenchSettingsConfig,
|
|
3
|
+
type WorkbenchSettingsConfig,
|
|
4
|
+
} from '@workbench-kit/workbench-config';
|
|
5
|
+
import {
|
|
6
|
+
createBrowserWorkbenchStorage,
|
|
7
|
+
type WorkbenchStorageReader,
|
|
8
|
+
type WorkbenchStorageWriter,
|
|
9
|
+
} from '@workbench-kit/workbench-core';
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY =
|
|
12
|
+
'workbench-kit/.workbench/settings.local';
|
|
13
|
+
|
|
14
|
+
export function isWorkbenchLocalPreferencePersistenceAvailable(): boolean {
|
|
15
|
+
return createBrowserWorkbenchStorage({ kind: 'local' }) !== undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function readPersistedLocalPreferences(
|
|
19
|
+
storageKey = DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY,
|
|
20
|
+
storage?: WorkbenchStorageReader,
|
|
21
|
+
): WorkbenchSettingsConfig {
|
|
22
|
+
const resolvedStorage = storage ?? createBrowserWorkbenchStorage({ kind: 'local' });
|
|
23
|
+
if (!resolvedStorage) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const raw = resolvedStorage.getItem(storageKey);
|
|
29
|
+
if (!raw) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return parseWorkbenchSettingsConfig(JSON.parse(raw) as unknown);
|
|
34
|
+
} catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function writePersistedLocalPreferences(
|
|
40
|
+
values: WorkbenchSettingsConfig,
|
|
41
|
+
storageKey = DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY,
|
|
42
|
+
storage?: WorkbenchStorageWriter,
|
|
43
|
+
): void {
|
|
44
|
+
const resolvedStorage = storage ?? createBrowserWorkbenchStorage({ kind: 'local' });
|
|
45
|
+
if (!resolvedStorage) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
resolvedStorage.setItem(storageKey, JSON.stringify(values, null, 2));
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const WORKBENCH_COMMANDS_SETTINGS_CATEGORY_ID =
|
|
2
|
+
'workbench-kit.shell.command-management' as const;
|
|
3
|
+
export const WORKBENCH_KEYBINDINGS_SETTINGS_CATEGORY_ID =
|
|
4
|
+
'workbench-kit.shell.keybinding-management' as const;
|
|
5
|
+
export const WORKBENCH_ACCOUNTS_SETTINGS_CATEGORY_ID =
|
|
6
|
+
'workbench-kit.shell.account-management' as const;
|
|
7
|
+
export const WORKBENCH_EXTENSIONS_SETTINGS_CATEGORY_ID =
|
|
8
|
+
'workbench-kit.shell.extension-management' as const;
|
|
9
|
+
export const MANAGE_COMMANDS_COMMAND_ID = 'workbench-kit.shell.commands.manage' as const;
|
|
10
|
+
export const MANAGE_KEYBINDINGS_COMMAND_ID = 'workbench-kit.shell.keybindings.manage' as const;
|
|
11
|
+
export const MANAGE_EXTENSIONS_COMMAND_ID = 'workbench-kit.shell.extensions.manage' as const;
|
|
12
|
+
export const MANAGE_ACCOUNTS_COMMAND_ID = 'workbench-kit.builtin.accounts.manage' as const;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
AccountManagementPanel,
|
|
4
|
+
CommandManagementPanel,
|
|
5
|
+
ExtensionManagementPanel,
|
|
6
|
+
type AccountManagementEntry,
|
|
7
|
+
} from '@workbench-kit/react/workbench/management';
|
|
8
|
+
import {
|
|
9
|
+
isExtensionInstallTrusted,
|
|
10
|
+
type ExtensionCatalogTrustPolicy,
|
|
11
|
+
} from '@workbench-kit/workbench-core';
|
|
12
|
+
|
|
13
|
+
import { useCommandManagementModel } from './use-command-management.js';
|
|
14
|
+
import { useExtensionManagementModel } from '../extensions/use-extension-management.js';
|
|
15
|
+
import { WorkbenchKeybindingManagementSettings } from './keybinding-settings.js';
|
|
16
|
+
|
|
17
|
+
export { WorkbenchKeybindingManagementSettings };
|
|
18
|
+
|
|
19
|
+
export interface WorkbenchAccountManagementInput {
|
|
20
|
+
accounts: readonly AccountManagementEntry[];
|
|
21
|
+
activeAccountId?: string | undefined;
|
|
22
|
+
automationHint?: string | undefined;
|
|
23
|
+
emptyLabel?: string | undefined;
|
|
24
|
+
onSignOut?: ((accountId: string) => void) | undefined;
|
|
25
|
+
sessionLabel?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Component-only module so Vite Fast Refresh can accept this boundary. */
|
|
29
|
+
export function WorkbenchCommandManagementSettings() {
|
|
30
|
+
const { groups, lastRun, runCommand, totalCount } = useCommandManagementModel();
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<CommandManagementPanel
|
|
34
|
+
groups={groups}
|
|
35
|
+
lastRun={lastRun}
|
|
36
|
+
summaryLabel={`${totalCount} registered command${totalCount === 1 ? '' : 's'} · auto-updated from extensions`}
|
|
37
|
+
onRunCommand={runCommand}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function WorkbenchExtensionManagementSettings({
|
|
43
|
+
catalogTrustPolicy,
|
|
44
|
+
catalogUrl,
|
|
45
|
+
}: {
|
|
46
|
+
catalogTrustPolicy?: ExtensionCatalogTrustPolicy | undefined;
|
|
47
|
+
catalogUrl?: string | undefined;
|
|
48
|
+
}) {
|
|
49
|
+
const {
|
|
50
|
+
browseEntries,
|
|
51
|
+
catalogError,
|
|
52
|
+
catalogLoading,
|
|
53
|
+
installCatalogEntry,
|
|
54
|
+
installedEntries,
|
|
55
|
+
installTrustRecords,
|
|
56
|
+
rememberInstallTrust,
|
|
57
|
+
toggleInstalledEntry,
|
|
58
|
+
} = useExtensionManagementModel({ catalogTrustPolicy, catalogUrl });
|
|
59
|
+
|
|
60
|
+
const isInstallTrusted = useCallback(
|
|
61
|
+
(entry: (typeof browseEntries)[number]) =>
|
|
62
|
+
isExtensionInstallTrusted(
|
|
63
|
+
entry.id,
|
|
64
|
+
entry.installPlan?.permissions ?? [],
|
|
65
|
+
installTrustRecords,
|
|
66
|
+
),
|
|
67
|
+
[installTrustRecords],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<ExtensionManagementPanel
|
|
72
|
+
browseEntries={browseEntries}
|
|
73
|
+
catalogError={catalogError}
|
|
74
|
+
catalogLoading={catalogLoading}
|
|
75
|
+
installedEntries={installedEntries}
|
|
76
|
+
isInstallTrusted={isInstallTrusted}
|
|
77
|
+
onInstall={installCatalogEntry}
|
|
78
|
+
onRememberInstallTrust={rememberInstallTrust}
|
|
79
|
+
onToggleEnabled={toggleInstalledEntry}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function WorkbenchAccountManagementSettings({
|
|
85
|
+
accountManagement,
|
|
86
|
+
}: {
|
|
87
|
+
accountManagement: WorkbenchAccountManagementInput;
|
|
88
|
+
}) {
|
|
89
|
+
return (
|
|
90
|
+
<AccountManagementPanel
|
|
91
|
+
automationHint="Linked accounts are project integrations exposed by extensions or host providers. Your Workbench service profile is managed from the profile menu."
|
|
92
|
+
emptyLabel="No linked project accounts are configured."
|
|
93
|
+
{...accountManagement}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { useEffect, useMemo, useReducer, useState } from 'react';
|
|
2
|
+
import type { ExtensionCommandFeatureSpec, ExtensionRegistry } from '@workbench-kit/workbench-core';
|
|
3
|
+
import {
|
|
4
|
+
buildCommandManagementGroups,
|
|
5
|
+
countCommandManagementEntries,
|
|
6
|
+
type CommandManagementRunState,
|
|
7
|
+
} from '@workbench-kit/react/workbench/management';
|
|
8
|
+
import { createWorkbenchShellCommands } from '@workbench-kit/react/workbench';
|
|
9
|
+
|
|
10
|
+
import { useWorkbench } from '../shell/provider.js';
|
|
11
|
+
import {
|
|
12
|
+
collectExtensionCommandFeaturesById,
|
|
13
|
+
resolveShellCommandActivities,
|
|
14
|
+
} from '../workbench/command-palette.js';
|
|
15
|
+
|
|
16
|
+
export function useCommandManagementModel() {
|
|
17
|
+
const { executeCommand, extensionRegistry } = useWorkbench();
|
|
18
|
+
const [lastRun, setLastRun] = useState<CommandManagementRunState | undefined>();
|
|
19
|
+
const [refreshToken, refreshRegistry] = useReducer((count: number) => count + 1, 0);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const commandDisposable = extensionRegistry.commands.onDidRegisterCommand(() => {
|
|
23
|
+
refreshRegistry();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return () => {
|
|
27
|
+
commandDisposable.dispose();
|
|
28
|
+
};
|
|
29
|
+
}, [extensionRegistry]);
|
|
30
|
+
|
|
31
|
+
const groups = useMemo(
|
|
32
|
+
() => buildCommandManagementModelGroups(extensionRegistry, refreshToken),
|
|
33
|
+
[extensionRegistry, refreshToken],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const totalCount = countCommandManagementEntries(groups);
|
|
37
|
+
|
|
38
|
+
const runCommand = async (commandId: string) => {
|
|
39
|
+
setLastRun({
|
|
40
|
+
commandId,
|
|
41
|
+
status: 'running',
|
|
42
|
+
timestamp: Date.now(),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await executeCommand(commandId);
|
|
47
|
+
setLastRun({
|
|
48
|
+
commandId,
|
|
49
|
+
status: 'success',
|
|
50
|
+
timestamp: Date.now(),
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
setLastRun({
|
|
54
|
+
commandId,
|
|
55
|
+
message: error instanceof Error ? error.message : 'Command failed.',
|
|
56
|
+
status: 'error',
|
|
57
|
+
timestamp: Date.now(),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
groups,
|
|
64
|
+
lastRun,
|
|
65
|
+
refreshRegistry,
|
|
66
|
+
runCommand,
|
|
67
|
+
totalCount,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function buildCommandManagementModelGroups(
|
|
72
|
+
extensionRegistry: ExtensionRegistry,
|
|
73
|
+
_refreshToken = 0,
|
|
74
|
+
) {
|
|
75
|
+
const shellCommands = createWorkbenchShellCommands({
|
|
76
|
+
activities: resolveShellCommandActivities(extensionRegistry),
|
|
77
|
+
includeSettings: true,
|
|
78
|
+
includeSidebarToggle: true,
|
|
79
|
+
});
|
|
80
|
+
const shellCommandIds = new Set(shellCommands.map((command) => command.id));
|
|
81
|
+
const commandFeaturesById = collectExtensionCommandFeaturesById(extensionRegistry);
|
|
82
|
+
|
|
83
|
+
return buildCommandManagementGroups({
|
|
84
|
+
extensionCommands: collectExtensionCommandEntries(
|
|
85
|
+
extensionRegistry,
|
|
86
|
+
shellCommandIds,
|
|
87
|
+
commandFeaturesById,
|
|
88
|
+
),
|
|
89
|
+
keybindingsByCommandId: collectKeybindingsByCommandId(extensionRegistry),
|
|
90
|
+
menuSurfacesByCommandId: collectMenuSurfacesByCommandId(extensionRegistry),
|
|
91
|
+
shellCommands: shellCommands.map((command) => ({
|
|
92
|
+
category: command.category ?? 'Workbench',
|
|
93
|
+
handler:
|
|
94
|
+
extensionRegistry.commands.getCommand(command.id)?.handler ??
|
|
95
|
+
command.handler ??
|
|
96
|
+
command.run,
|
|
97
|
+
id: command.id,
|
|
98
|
+
label: typeof command.label === 'string' ? command.label : (command.title ?? command.id),
|
|
99
|
+
})),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function collectExtensionCommandEntries(
|
|
104
|
+
extensionRegistry: ExtensionRegistry,
|
|
105
|
+
skippedCommandIds: ReadonlySet<string>,
|
|
106
|
+
commandFeaturesById: ReadonlyMap<string, ExtensionCommandFeatureSpec>,
|
|
107
|
+
) {
|
|
108
|
+
const entries: Array<{
|
|
109
|
+
category?: string | undefined;
|
|
110
|
+
description?: string | undefined;
|
|
111
|
+
extensionId: string;
|
|
112
|
+
extensionLabel: string;
|
|
113
|
+
handler?: unknown;
|
|
114
|
+
id: string;
|
|
115
|
+
label: string;
|
|
116
|
+
}> = [];
|
|
117
|
+
const seen = new Set<string>();
|
|
118
|
+
|
|
119
|
+
for (const feature of extensionRegistry.getFeatureSpecs()) {
|
|
120
|
+
const extensionLabel = feature.displayName;
|
|
121
|
+
|
|
122
|
+
for (const contribution of feature.commands) {
|
|
123
|
+
const command = extensionRegistry.commands.getCommand(contribution.id);
|
|
124
|
+
if (!command || seen.has(contribution.id) || skippedCommandIds.has(contribution.id)) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
seen.add(contribution.id);
|
|
129
|
+
entries.push({
|
|
130
|
+
category: command.category ?? contribution.category,
|
|
131
|
+
description: contribution.description,
|
|
132
|
+
extensionId: feature.id,
|
|
133
|
+
extensionLabel,
|
|
134
|
+
handler: command.handler,
|
|
135
|
+
id: command.id,
|
|
136
|
+
label: command.title ?? contribution.title ?? command.id,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (const command of extensionRegistry.commands.getCommands()) {
|
|
142
|
+
if (seen.has(command.id) || skippedCommandIds.has(command.id)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
entries.push({
|
|
147
|
+
category: command.category,
|
|
148
|
+
description: commandFeaturesById.get(command.id)?.description,
|
|
149
|
+
extensionId: 'runtime',
|
|
150
|
+
extensionLabel: 'Runtime',
|
|
151
|
+
handler: command.handler,
|
|
152
|
+
id: command.id,
|
|
153
|
+
label: command.title ?? command.id,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return entries;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function collectKeybindingsByCommandId(extensionRegistry: ExtensionRegistry) {
|
|
161
|
+
const keybindingsByCommandId: Record<string, string> = {};
|
|
162
|
+
|
|
163
|
+
for (const keybinding of extensionRegistry.keybindings.getKeybindings()) {
|
|
164
|
+
if (!keybindingsByCommandId[keybinding.command]) {
|
|
165
|
+
keybindingsByCommandId[keybinding.command] = keybinding.key;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return keybindingsByCommandId;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function collectMenuSurfacesByCommandId(extensionRegistry: ExtensionRegistry) {
|
|
173
|
+
const menuSurfacesByCommandId = new Map<string, Set<string>>();
|
|
174
|
+
|
|
175
|
+
for (const menuItem of extensionRegistry.menus.getMenuItems()) {
|
|
176
|
+
if (!menuItem.menu || !menuItem.command) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const surfaces = menuSurfacesByCommandId.get(menuItem.command) ?? new Set<string>();
|
|
181
|
+
surfaces.add(menuItem.menu);
|
|
182
|
+
menuSurfacesByCommandId.set(menuItem.command, surfaces);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return Object.fromEntries(
|
|
186
|
+
[...menuSurfacesByCommandId.entries()].map(([commandId, surfaces]) => [
|
|
187
|
+
commandId,
|
|
188
|
+
[...surfaces].sort(),
|
|
189
|
+
]),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
buildKeybindingManagementEntries,
|
|
4
|
+
type KeybindingDefinition,
|
|
5
|
+
} from '@workbench-kit/platform';
|
|
6
|
+
import { createWorkbenchShellCommands } from '@workbench-kit/react/workbench';
|
|
7
|
+
import type { ExtensionRegistry } from '@workbench-kit/workbench-core';
|
|
8
|
+
|
|
9
|
+
import { useWorkbench } from '../shell/provider.js';
|
|
10
|
+
import { resolveShellCommandActivities } from '../workbench/command-palette.js';
|
|
11
|
+
|
|
12
|
+
export function useKeybindingManagementModel() {
|
|
13
|
+
const {
|
|
14
|
+
extensionRegistry,
|
|
15
|
+
keybindingOverrides,
|
|
16
|
+
resetCommandKeybindingOverride,
|
|
17
|
+
setCommandKeybindingOverride,
|
|
18
|
+
} = useWorkbench();
|
|
19
|
+
|
|
20
|
+
const defaults = useMemo(
|
|
21
|
+
() => extensionRegistry.keybindings.getKeybindings(),
|
|
22
|
+
[extensionRegistry, keybindingOverrides.length],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const entries = useMemo(
|
|
26
|
+
() =>
|
|
27
|
+
buildKeybindingManagementEntries({
|
|
28
|
+
commands: collectKeybindingManagementCommands(extensionRegistry),
|
|
29
|
+
defaults,
|
|
30
|
+
overrides: keybindingOverrides,
|
|
31
|
+
}),
|
|
32
|
+
[defaults, extensionRegistry, keybindingOverrides],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const overrideCount = keybindingOverrides.length;
|
|
36
|
+
|
|
37
|
+
const setKeybinding = (commandId: string, key: string | undefined) => {
|
|
38
|
+
const defaultKey = defaults.find((binding) => binding.command === commandId)?.key;
|
|
39
|
+
if (!key || key === defaultKey) {
|
|
40
|
+
resetCommandKeybindingOverride(commandId);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setCommandKeybindingOverride(commandId, key);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
entries,
|
|
49
|
+
overrideCount,
|
|
50
|
+
resetKeybinding: resetCommandKeybindingOverride,
|
|
51
|
+
setKeybinding,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function collectKeybindingManagementCommands(extensionRegistry: ExtensionRegistry) {
|
|
56
|
+
const commands: Array<{
|
|
57
|
+
category?: string | undefined;
|
|
58
|
+
id: string;
|
|
59
|
+
label: string;
|
|
60
|
+
sourceLabel?: string | undefined;
|
|
61
|
+
}> = [];
|
|
62
|
+
const seen = new Set<string>();
|
|
63
|
+
const shellCommands = createWorkbenchShellCommands({
|
|
64
|
+
activities: resolveShellCommandActivities(extensionRegistry),
|
|
65
|
+
includeSettings: true,
|
|
66
|
+
includeSidebarToggle: true,
|
|
67
|
+
});
|
|
68
|
+
const shellCommandIds = new Set(shellCommands.map((command) => command.id));
|
|
69
|
+
|
|
70
|
+
for (const extension of extensionRegistry.getExtensions()) {
|
|
71
|
+
const extensionLabel = extension.manifest.displayName ?? extension.manifest.id;
|
|
72
|
+
|
|
73
|
+
for (const contribution of extension.manifest.contributes?.commands ?? []) {
|
|
74
|
+
const command = extensionRegistry.commands.getCommand(contribution.command);
|
|
75
|
+
if (!command || seen.has(contribution.command) || shellCommandIds.has(contribution.command)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
seen.add(contribution.command);
|
|
80
|
+
commands.push({
|
|
81
|
+
category: command.category ?? contribution.category,
|
|
82
|
+
id: command.id,
|
|
83
|
+
label: command.title ?? contribution.title ?? command.id,
|
|
84
|
+
sourceLabel: extensionLabel,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const command of extensionRegistry.commands.getCommands()) {
|
|
90
|
+
if (seen.has(command.id) || shellCommandIds.has(command.id)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
commands.push({
|
|
95
|
+
category: command.category,
|
|
96
|
+
id: command.id,
|
|
97
|
+
label: command.title ?? command.id,
|
|
98
|
+
sourceLabel: 'Runtime',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const command of shellCommands) {
|
|
103
|
+
if (seen.has(command.id)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const label =
|
|
108
|
+
typeof command.label === 'function'
|
|
109
|
+
? command.id
|
|
110
|
+
: typeof command.label === 'string'
|
|
111
|
+
? command.label
|
|
112
|
+
: command.id;
|
|
113
|
+
|
|
114
|
+
seen.add(command.id);
|
|
115
|
+
commands.push({
|
|
116
|
+
category: 'Workbench',
|
|
117
|
+
id: command.id,
|
|
118
|
+
label,
|
|
119
|
+
sourceLabel: 'Workbench Shell',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return commands;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function toKeybindingOverrideDefinitions(
|
|
127
|
+
overrides: readonly KeybindingDefinition[],
|
|
128
|
+
): KeybindingDefinition[] {
|
|
129
|
+
return overrides.map((binding) => ({ ...binding }));
|
|
130
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ActivityBarItem, StatusBarSectionModel } from '@workbench-kit/react/workbench/shell';
|
|
3
|
+
import type {
|
|
4
|
+
ExtensionDependencyDiagnosticSeverity,
|
|
5
|
+
WorkbenchActivityContribution,
|
|
6
|
+
WorkbenchViewContainerContribution,
|
|
7
|
+
WorkbenchViewContribution,
|
|
8
|
+
} from '@workbench-kit/workbench-core';
|
|
9
|
+
|
|
10
|
+
export interface WorkbenchShellActivityItemsInput {
|
|
11
|
+
readonly activeViewContainerId?: string | undefined;
|
|
12
|
+
readonly activities: readonly WorkbenchActivityContribution[];
|
|
13
|
+
readonly viewContainers: readonly WorkbenchViewContainerContribution[];
|
|
14
|
+
readonly views: readonly WorkbenchViewContribution[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface WorkbenchShellStatusSectionsInput {
|
|
18
|
+
readonly dependencyDiagnostics: readonly {
|
|
19
|
+
readonly severity: ExtensionDependencyDiagnosticSeverity;
|
|
20
|
+
}[];
|
|
21
|
+
readonly extensionCount: number;
|
|
22
|
+
readonly missingExtensionIds: readonly string[];
|
|
23
|
+
readonly profile?: { readonly displayName: string } | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createWorkbenchShellActivityItems({
|
|
27
|
+
activeViewContainerId,
|
|
28
|
+
activities,
|
|
29
|
+
viewContainers,
|
|
30
|
+
views,
|
|
31
|
+
}: WorkbenchShellActivityItemsInput): ActivityBarItem[] {
|
|
32
|
+
if (activities.length > 0) {
|
|
33
|
+
return activities
|
|
34
|
+
.map<ActivityBarItem & { order?: number }>((activity) => ({
|
|
35
|
+
active: activity.viewContainerId === activeViewContainerId,
|
|
36
|
+
icon: resolveWorkbenchShellIcon(activity.icon),
|
|
37
|
+
id: activity.viewContainerId,
|
|
38
|
+
label: activity.title,
|
|
39
|
+
order: activity.order,
|
|
40
|
+
}))
|
|
41
|
+
.sort(
|
|
42
|
+
(left, right) =>
|
|
43
|
+
(left.order ?? Number.MAX_SAFE_INTEGER) - (right.order ?? Number.MAX_SAFE_INTEGER),
|
|
44
|
+
)
|
|
45
|
+
.map(({ order: _order, ...item }) => item);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const containersById = new Map(viewContainers.map((container) => [container.id, container]));
|
|
49
|
+
const firstViewsByContainerId = new Map<string, WorkbenchViewContribution>();
|
|
50
|
+
views.forEach((view) => {
|
|
51
|
+
if (!firstViewsByContainerId.has(view.containerId)) {
|
|
52
|
+
firstViewsByContainerId.set(view.containerId, view);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return [...firstViewsByContainerId.entries()].map<ActivityBarItem>(([containerId, firstView]) => {
|
|
57
|
+
const container = containersById.get(containerId);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
active: containerId === activeViewContainerId,
|
|
61
|
+
icon: resolveWorkbenchShellIcon(container?.icon ?? firstView.name ?? containerId),
|
|
62
|
+
id: containerId,
|
|
63
|
+
label: container?.title ?? firstView.name ?? containerId,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function createDefaultWorkbenchStatusSections({
|
|
69
|
+
dependencyDiagnostics,
|
|
70
|
+
extensionCount,
|
|
71
|
+
missingExtensionIds,
|
|
72
|
+
profile,
|
|
73
|
+
}: WorkbenchShellStatusSectionsInput): StatusBarSectionModel[] {
|
|
74
|
+
const errorDependencyDiagnostics = dependencyDiagnostics.filter(
|
|
75
|
+
(diagnostic) => diagnostic.severity === 'error',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return [
|
|
79
|
+
{
|
|
80
|
+
id: 'workbench',
|
|
81
|
+
items: [
|
|
82
|
+
{
|
|
83
|
+
id: 'extensions',
|
|
84
|
+
label: `extensions: ${extensionCount}`,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
hidden: missingExtensionIds.length === 0,
|
|
88
|
+
id: 'missing-extensions',
|
|
89
|
+
label: `missing: ${missingExtensionIds.length}`,
|
|
90
|
+
status: 'waiting',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
hidden: dependencyDiagnostics.length === 0,
|
|
94
|
+
id: 'extension-dependencies',
|
|
95
|
+
label: `deps: ${dependencyDiagnostics.length}`,
|
|
96
|
+
status: errorDependencyDiagnostics.length > 0 ? 'failed' : 'waiting',
|
|
97
|
+
title:
|
|
98
|
+
errorDependencyDiagnostics.length > 0
|
|
99
|
+
? `${errorDependencyDiagnostics.length} extension dependency error${
|
|
100
|
+
errorDependencyDiagnostics.length === 1 ? '' : 's'
|
|
101
|
+
}`
|
|
102
|
+
: `${dependencyDiagnostics.length} extension dependency warning${
|
|
103
|
+
dependencyDiagnostics.length === 1 ? '' : 's'
|
|
104
|
+
}`,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
align: 'end',
|
|
110
|
+
id: 'workbench-meta',
|
|
111
|
+
items: [
|
|
112
|
+
...(profile
|
|
113
|
+
? [
|
|
114
|
+
{
|
|
115
|
+
icon: 'account' as const,
|
|
116
|
+
id: 'workbench.account',
|
|
117
|
+
label: profile.displayName,
|
|
118
|
+
title: 'Open profile',
|
|
119
|
+
},
|
|
120
|
+
]
|
|
121
|
+
: []),
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function resolveWorkbenchShellIcon(icon: ReactNode | string | undefined): ReactNode {
|
|
128
|
+
if (icon === undefined) {
|
|
129
|
+
return <span aria-hidden="true">W</span>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (typeof icon !== 'string') {
|
|
133
|
+
return icon;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const className = getWorkbenchShellCodiconClassName(icon);
|
|
137
|
+
if (className) {
|
|
138
|
+
return <i aria-hidden="true" className={`codicon ${className}`} />;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return <span aria-hidden="true">W</span>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function getWorkbenchShellCodiconClassName(icon: string): string | undefined {
|
|
145
|
+
const token = icon.trim();
|
|
146
|
+
if (!token) return undefined;
|
|
147
|
+
|
|
148
|
+
return token.startsWith('codicon-') ? token : `codicon-${token}`;
|
|
149
|
+
}
|