@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,220 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { ChatPanel, type ChatMessage } from '@workbench-kit/react/workbench/chat';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
createMockAiChatCommandProposals,
|
|
6
|
+
createMockAiChatResponseContent,
|
|
7
|
+
} from './ai-mock-proposals.js';
|
|
8
|
+
import {
|
|
9
|
+
readWorkbenchAiChatCommandPolicyInput,
|
|
10
|
+
mergeWorkbenchAiChatCommandPolicyInput,
|
|
11
|
+
} from './command-policy.js';
|
|
12
|
+
import {
|
|
13
|
+
useWorkbenchChatCommandSurface,
|
|
14
|
+
type WorkbenchChatCommandRunResult,
|
|
15
|
+
} from './command-surface.js';
|
|
16
|
+
import { type BuiltinChatViewMode, type BuiltinChatViewRenderData } from './view-data.js';
|
|
17
|
+
import { useWorkbenchChatCommandProposals } from './use-chat-command-proposals.js';
|
|
18
|
+
import { useWorkbenchCommandDescriptors } from '../commands/use-command-descriptors.js';
|
|
19
|
+
|
|
20
|
+
export type { BuiltinChatViewMode, BuiltinChatViewRenderData };
|
|
21
|
+
export { BUILTIN_CHAT_VIEW_RENDER_KIND, isBuiltinChatViewRenderData } from './view-data.js';
|
|
22
|
+
|
|
23
|
+
type BuiltinChatRuntimeStatus = 'idle' | 'running' | 'error';
|
|
24
|
+
|
|
25
|
+
const initialChattingMessages: readonly ChatMessage[] = [
|
|
26
|
+
{
|
|
27
|
+
content: 'Share updates here while working in the workspace.',
|
|
28
|
+
createdAt: '2026-06-18T14:29:00.000Z',
|
|
29
|
+
id: 'workbench-chatting-intro',
|
|
30
|
+
label: 'Alex',
|
|
31
|
+
source: 'assistant',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const initialAiChatMessages: readonly ChatMessage[] = [
|
|
36
|
+
{
|
|
37
|
+
content: 'Ask about the workspace or run a command with `/command.id`.',
|
|
38
|
+
createdAt: '2026-06-18T14:29:00.000Z',
|
|
39
|
+
id: 'workbench-ai-chat-intro',
|
|
40
|
+
label: 'Assistant',
|
|
41
|
+
source: 'assistant',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export function BuiltinChatView({ mode }: { mode: BuiltinChatViewMode }) {
|
|
46
|
+
if (mode === 'aiChat') {
|
|
47
|
+
return <BuiltinAiChatView />;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return <BuiltinChattingView />;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createChatCommandFeedbackMessage(result: WorkbenchChatCommandRunResult): ChatMessage {
|
|
54
|
+
const title = result.label ?? result.commandId;
|
|
55
|
+
const status =
|
|
56
|
+
result.status === 'success' ? `Ran command \`${title}\`.` : `Command failed: \`${title}\`.`;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
content: result.message ? `${status}\n\n${result.message}` : status,
|
|
60
|
+
createdAt: new Date().toISOString(),
|
|
61
|
+
id: `chat-command-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
62
|
+
label: 'Workbench',
|
|
63
|
+
source: 'assistant',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createChatMessageId(prefix: string) {
|
|
68
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function BuiltinChattingView() {
|
|
72
|
+
const [draft, setDraft] = useState('');
|
|
73
|
+
const [messages, setMessages] = useState<ChatMessage[]>(() => [...initialChattingMessages]);
|
|
74
|
+
const appendCommandResult = useCallback((result: WorkbenchChatCommandRunResult) => {
|
|
75
|
+
setMessages((currentMessages) => [
|
|
76
|
+
...currentMessages,
|
|
77
|
+
createChatCommandFeedbackMessage(result),
|
|
78
|
+
]);
|
|
79
|
+
}, []);
|
|
80
|
+
const chatCommands = useWorkbenchChatCommandSurface({
|
|
81
|
+
onCommandResult: appendCommandResult,
|
|
82
|
+
onValueChange: setDraft,
|
|
83
|
+
value: draft,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className="workbench-chat-view">
|
|
88
|
+
<ChatPanel
|
|
89
|
+
assistantLabel="Alex"
|
|
90
|
+
commandLabel="Show commands"
|
|
91
|
+
commandSuggestPopover={chatCommands.commandSuggestPopover}
|
|
92
|
+
composerRef={chatCommands.composerRef}
|
|
93
|
+
emptyLabel="Start a conversation with your team."
|
|
94
|
+
messageLayout="peer"
|
|
95
|
+
messages={messages}
|
|
96
|
+
placeholder="Message your team"
|
|
97
|
+
title="Chat"
|
|
98
|
+
userLabel="Jay"
|
|
99
|
+
value={draft}
|
|
100
|
+
onCommandClick={chatCommands.onCommandClick}
|
|
101
|
+
onKeyDown={chatCommands.onKeyDown}
|
|
102
|
+
onSubmit={(message) => {
|
|
103
|
+
if (chatCommands.runInputAsCommand(message)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
setDraft('');
|
|
108
|
+
setMessages((currentMessages) => [
|
|
109
|
+
...currentMessages,
|
|
110
|
+
{
|
|
111
|
+
content: message,
|
|
112
|
+
createdAt: new Date().toISOString(),
|
|
113
|
+
id: createChatMessageId('chatting-user'),
|
|
114
|
+
source: 'user',
|
|
115
|
+
},
|
|
116
|
+
]);
|
|
117
|
+
}}
|
|
118
|
+
onValueChange={setDraft}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function BuiltinAiChatView() {
|
|
125
|
+
const [draft, setDraft] = useState('');
|
|
126
|
+
const [messages, setMessages] = useState<ChatMessage[]>(() => [...initialAiChatMessages]);
|
|
127
|
+
const [runtimeStatus, setRuntimeStatus] = useState<BuiltinChatRuntimeStatus>('idle');
|
|
128
|
+
const commands = useWorkbenchCommandDescriptors();
|
|
129
|
+
const policyInput = useMemo(
|
|
130
|
+
() =>
|
|
131
|
+
mergeWorkbenchAiChatCommandPolicyInput(readWorkbenchAiChatCommandPolicyInput(), {
|
|
132
|
+
policyByCommandId: {
|
|
133
|
+
'workbench.togglePrimarySidebar': 'approval-required',
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
[],
|
|
137
|
+
);
|
|
138
|
+
const appendCommandResult = useCallback((result: WorkbenchChatCommandRunResult) => {
|
|
139
|
+
setMessages((currentMessages) => [
|
|
140
|
+
...currentMessages,
|
|
141
|
+
createChatCommandFeedbackMessage(result),
|
|
142
|
+
]);
|
|
143
|
+
}, []);
|
|
144
|
+
const chatCommands = useWorkbenchChatCommandSurface({
|
|
145
|
+
onCommandResult: appendCommandResult,
|
|
146
|
+
onValueChange: setDraft,
|
|
147
|
+
value: draft,
|
|
148
|
+
});
|
|
149
|
+
const { createProposalHandlers, enrichMessageProposals, processAutoPolicies } =
|
|
150
|
+
useWorkbenchChatCommandProposals({
|
|
151
|
+
commands,
|
|
152
|
+
onCommandResult: appendCommandResult,
|
|
153
|
+
policyInput,
|
|
154
|
+
});
|
|
155
|
+
const proposalHandlers = useMemo(
|
|
156
|
+
() => createProposalHandlers(setMessages),
|
|
157
|
+
[createProposalHandlers],
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div className="workbench-chat-view">
|
|
162
|
+
<ChatPanel
|
|
163
|
+
assistantLabel="Assistant"
|
|
164
|
+
commandLabel="Show commands"
|
|
165
|
+
commandSuggestPopover={chatCommands.commandSuggestPopover}
|
|
166
|
+
composerRef={chatCommands.composerRef}
|
|
167
|
+
disabled={runtimeStatus === 'error'}
|
|
168
|
+
emptyLabel="Ask about this workspace."
|
|
169
|
+
isRunning={runtimeStatus === 'running'}
|
|
170
|
+
messageLayout="assistant"
|
|
171
|
+
messages={messages}
|
|
172
|
+
placeholder="Ask about this workspace"
|
|
173
|
+
showTools
|
|
174
|
+
title="AI Chat"
|
|
175
|
+
value={draft}
|
|
176
|
+
onCancel={() => setRuntimeStatus('idle')}
|
|
177
|
+
onCommandClick={chatCommands.onCommandClick}
|
|
178
|
+
onCommandProposalAllow={proposalHandlers.onProposalAllow}
|
|
179
|
+
onCommandProposalDeny={proposalHandlers.onProposalDeny}
|
|
180
|
+
onKeyDown={chatCommands.onKeyDown}
|
|
181
|
+
onSubmit={(message) => {
|
|
182
|
+
if (chatCommands.runInputAsCommand(message)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
setDraft('');
|
|
187
|
+
setRuntimeStatus('running');
|
|
188
|
+
|
|
189
|
+
const assistantMessageId = createChatMessageId('ai-chat-assistant');
|
|
190
|
+
const rawProposals = createMockAiChatCommandProposals(message);
|
|
191
|
+
const commandProposalsForMessage = enrichMessageProposals(rawProposals);
|
|
192
|
+
const assistantMessage: ChatMessage = {
|
|
193
|
+
commandProposals: commandProposalsForMessage.length
|
|
194
|
+
? commandProposalsForMessage
|
|
195
|
+
: undefined,
|
|
196
|
+
content: createMockAiChatResponseContent(message),
|
|
197
|
+
createdAt: new Date().toISOString(),
|
|
198
|
+
id: assistantMessageId,
|
|
199
|
+
label: 'Assistant',
|
|
200
|
+
source: 'assistant',
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
setMessages((currentMessages) => [
|
|
204
|
+
...currentMessages,
|
|
205
|
+
{
|
|
206
|
+
content: message,
|
|
207
|
+
createdAt: new Date().toISOString(),
|
|
208
|
+
id: createChatMessageId('ai-chat-user'),
|
|
209
|
+
source: 'user',
|
|
210
|
+
},
|
|
211
|
+
assistantMessage,
|
|
212
|
+
]);
|
|
213
|
+
processAutoPolicies(setMessages, assistantMessageId, commandProposalsForMessage);
|
|
214
|
+
setRuntimeStatus('idle');
|
|
215
|
+
}}
|
|
216
|
+
onValueChange={setDraft}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ScrollArea } from '@workbench-kit/react/primitives';
|
|
2
|
+
import {
|
|
3
|
+
CommandInspectorPanel,
|
|
4
|
+
findCommandManagementEntry,
|
|
5
|
+
} from '@workbench-kit/react/workbench/management';
|
|
6
|
+
|
|
7
|
+
import { useCommandManagementModel } from '../management/use-command-management.js';
|
|
8
|
+
|
|
9
|
+
export interface CommandInspectorSurfaceProps {
|
|
10
|
+
commandId: string;
|
|
11
|
+
resourceUri: string;
|
|
12
|
+
tabId: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function CommandInspectorSurface({
|
|
16
|
+
commandId,
|
|
17
|
+
resourceUri,
|
|
18
|
+
tabId,
|
|
19
|
+
}: CommandInspectorSurfaceProps) {
|
|
20
|
+
const { groups, lastRun, runCommand } = useCommandManagementModel();
|
|
21
|
+
const entry = findCommandManagementEntry(groups, commandId);
|
|
22
|
+
|
|
23
|
+
if (!entry) {
|
|
24
|
+
return (
|
|
25
|
+
<section
|
|
26
|
+
aria-label="Command inspector"
|
|
27
|
+
className="workbench-command-inspector-surface"
|
|
28
|
+
data-editor-host-id={tabId}
|
|
29
|
+
data-resource-uri={resourceUri}
|
|
30
|
+
>
|
|
31
|
+
<div className="workbench-command-inspector-surface__missing" role="alert">
|
|
32
|
+
<p className="workbench-command-inspector-surface__missing-title">Command not found</p>
|
|
33
|
+
<p className="workbench-command-inspector-surface__missing-message">
|
|
34
|
+
No registry entry matches <code>{commandId}</code>.
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
</section>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<ScrollArea className="workbench-command-inspector-surface" orientation="vertical">
|
|
43
|
+
<CommandInspectorPanel entry={entry} lastRun={lastRun} onRunCommand={runCommand} />
|
|
44
|
+
</ScrollArea>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useEffect, useMemo, useReducer } from 'react';
|
|
2
|
+
import type { WorkbenchCommandDescriptor } from '@workbench-kit/react/workbench';
|
|
3
|
+
|
|
4
|
+
import { useWorkbench } from '../shell/provider.js';
|
|
5
|
+
import {
|
|
6
|
+
collectExtensionCommandFeaturesById,
|
|
7
|
+
extensionCommandToDescriptor,
|
|
8
|
+
mergeWorkbenchCommandDescriptors,
|
|
9
|
+
} from '../workbench/command-palette.js';
|
|
10
|
+
|
|
11
|
+
const EMPTY_COMMAND_DESCRIPTORS: readonly WorkbenchCommandDescriptor[] = [];
|
|
12
|
+
|
|
13
|
+
export function useWorkbenchCommandDescriptors(
|
|
14
|
+
additionalCommands: readonly WorkbenchCommandDescriptor[] = EMPTY_COMMAND_DESCRIPTORS,
|
|
15
|
+
) {
|
|
16
|
+
const { extensionRegistry } = useWorkbench();
|
|
17
|
+
const [refreshToken, refreshCommands] = useReducer((count: number) => count + 1, 0);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const disposable = extensionRegistry.commands.onDidRegisterCommand(() => {
|
|
21
|
+
refreshCommands();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return () => {
|
|
25
|
+
disposable.dispose();
|
|
26
|
+
};
|
|
27
|
+
}, [extensionRegistry.commands]);
|
|
28
|
+
|
|
29
|
+
return useMemo(() => {
|
|
30
|
+
const commandFeaturesById = collectExtensionCommandFeaturesById(extensionRegistry);
|
|
31
|
+
|
|
32
|
+
return mergeWorkbenchCommandDescriptors(
|
|
33
|
+
extensionRegistry.commands
|
|
34
|
+
.getCommands()
|
|
35
|
+
.map((command) =>
|
|
36
|
+
extensionCommandToDescriptor(command, commandFeaturesById.get(command.id)),
|
|
37
|
+
),
|
|
38
|
+
[...additionalCommands],
|
|
39
|
+
);
|
|
40
|
+
}, [additionalCommands, extensionRegistry, refreshToken]);
|
|
41
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import type { ContextKeyService } from '@workbench-kit/platform';
|
|
3
|
+
|
|
4
|
+
export function useContextKeyRevision(contextKeyService: ContextKeyService): number {
|
|
5
|
+
const [revision, setRevision] = useState(0);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const disposable = contextKeyService.onDidChangeContext(() => {
|
|
9
|
+
setRevision((current) => current + 1);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return () => {
|
|
13
|
+
disposable.dispose();
|
|
14
|
+
};
|
|
15
|
+
}, [contextKeyService]);
|
|
16
|
+
|
|
17
|
+
return revision;
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const BUILTIN_COMMANDS_VIEW_RENDER_KIND = 'workbench-kit.builtin.commands.view' as const;
|
|
2
|
+
export const BUILTIN_COMMANDS_VIEW_CONTAINER_ID = 'commands' as const;
|
|
3
|
+
|
|
4
|
+
export interface BuiltinCommandsViewRenderData {
|
|
5
|
+
readonly kind: typeof BUILTIN_COMMANDS_VIEW_RENDER_KIND;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isBuiltinCommandsViewRenderData(
|
|
9
|
+
value: unknown,
|
|
10
|
+
): value is BuiltinCommandsViewRenderData {
|
|
11
|
+
return (
|
|
12
|
+
typeof value === 'object' &&
|
|
13
|
+
value !== null &&
|
|
14
|
+
'kind' in value &&
|
|
15
|
+
(value as BuiltinCommandsViewRenderData).kind === BUILTIN_COMMANDS_VIEW_RENDER_KIND
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandManagementSidebar,
|
|
3
|
+
buildCommandInspectorUri,
|
|
4
|
+
findCommandManagementEntry,
|
|
5
|
+
} from '@workbench-kit/react/workbench/management';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
BUILTIN_COMMANDS_VIEW_RENDER_KIND,
|
|
9
|
+
isBuiltinCommandsViewRenderData,
|
|
10
|
+
type BuiltinCommandsViewRenderData,
|
|
11
|
+
} from './view-data.js';
|
|
12
|
+
import { useCommandManagementModel } from '../management/use-command-management.js';
|
|
13
|
+
import { useEditorService } from '../editor/use-editor.js';
|
|
14
|
+
|
|
15
|
+
export type { BuiltinCommandsViewRenderData };
|
|
16
|
+
export { BUILTIN_COMMANDS_VIEW_RENDER_KIND, isBuiltinCommandsViewRenderData };
|
|
17
|
+
|
|
18
|
+
export function BuiltinCommandsView() {
|
|
19
|
+
const editorService = useEditorService();
|
|
20
|
+
const { groups, lastRun, refreshRegistry, runCommand } = useCommandManagementModel();
|
|
21
|
+
|
|
22
|
+
const inspectCommand = (commandId: string) => {
|
|
23
|
+
const entry = findCommandManagementEntry(groups, commandId);
|
|
24
|
+
editorService.openEditor({
|
|
25
|
+
icon: 'codicon-terminal',
|
|
26
|
+
pinned: true,
|
|
27
|
+
preview: false,
|
|
28
|
+
resourceUri: buildCommandInspectorUri(commandId),
|
|
29
|
+
title: entry?.label ?? commandId,
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<CommandManagementSidebar
|
|
35
|
+
className="workbench-commands-view"
|
|
36
|
+
groups={groups}
|
|
37
|
+
lastRun={lastRun}
|
|
38
|
+
onInspectCommand={inspectCommand}
|
|
39
|
+
onRefresh={refreshRegistry}
|
|
40
|
+
onRunCommand={runCommand}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Button, ScrollArea } from '@workbench-kit/react/primitives';
|
|
4
|
+
|
|
5
|
+
import { useWorkbenchDevtoolsSnapshot } from './use-workbench-devtools-snapshot.js';
|
|
6
|
+
|
|
7
|
+
type DevtoolsSectionId =
|
|
8
|
+
| 'commands'
|
|
9
|
+
| 'menus'
|
|
10
|
+
| 'keybindings'
|
|
11
|
+
| 'contextKeys'
|
|
12
|
+
| 'capabilities'
|
|
13
|
+
| 'layout'
|
|
14
|
+
| 'editor'
|
|
15
|
+
| 'views'
|
|
16
|
+
| 'transactions'
|
|
17
|
+
| 'diagnostics';
|
|
18
|
+
|
|
19
|
+
const DEVTOOLS_SECTIONS: readonly {
|
|
20
|
+
readonly id: DevtoolsSectionId;
|
|
21
|
+
readonly label: string;
|
|
22
|
+
}[] = [
|
|
23
|
+
{ id: 'commands', label: 'Commands' },
|
|
24
|
+
{ id: 'menus', label: 'Menus' },
|
|
25
|
+
{ id: 'keybindings', label: 'Keybindings' },
|
|
26
|
+
{ id: 'contextKeys', label: 'Context keys' },
|
|
27
|
+
{ id: 'capabilities', label: 'Capabilities' },
|
|
28
|
+
{ id: 'layout', label: 'Layout' },
|
|
29
|
+
{ id: 'editor', label: 'Editor' },
|
|
30
|
+
{ id: 'views', label: 'Views' },
|
|
31
|
+
{ id: 'transactions', label: 'Transactions' },
|
|
32
|
+
{ id: 'diagnostics', label: 'Diagnostics' },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function formatSectionValue(
|
|
36
|
+
sectionId: DevtoolsSectionId,
|
|
37
|
+
snapshot: ReturnType<typeof useWorkbenchDevtoolsSnapshot>,
|
|
38
|
+
): string {
|
|
39
|
+
switch (sectionId) {
|
|
40
|
+
case 'commands':
|
|
41
|
+
return JSON.stringify(snapshot.commands, null, 2);
|
|
42
|
+
case 'menus':
|
|
43
|
+
return JSON.stringify(snapshot.menus, null, 2);
|
|
44
|
+
case 'keybindings':
|
|
45
|
+
return JSON.stringify(snapshot.keybindings, null, 2);
|
|
46
|
+
case 'contextKeys':
|
|
47
|
+
return JSON.stringify(snapshot.contextKeys, null, 2);
|
|
48
|
+
case 'capabilities':
|
|
49
|
+
return JSON.stringify(
|
|
50
|
+
{
|
|
51
|
+
activeExtensions: snapshot.activeExtensions,
|
|
52
|
+
capabilities: snapshot.capabilities,
|
|
53
|
+
},
|
|
54
|
+
null,
|
|
55
|
+
2,
|
|
56
|
+
);
|
|
57
|
+
case 'layout':
|
|
58
|
+
return JSON.stringify(snapshot.layout, null, 2);
|
|
59
|
+
case 'editor':
|
|
60
|
+
return JSON.stringify(snapshot.editor, null, 2);
|
|
61
|
+
case 'views':
|
|
62
|
+
return JSON.stringify(
|
|
63
|
+
{
|
|
64
|
+
activities: snapshot.activities,
|
|
65
|
+
viewContainers: snapshot.viewContainers,
|
|
66
|
+
views: snapshot.views,
|
|
67
|
+
},
|
|
68
|
+
null,
|
|
69
|
+
2,
|
|
70
|
+
);
|
|
71
|
+
case 'transactions':
|
|
72
|
+
return JSON.stringify(snapshot.transactions, null, 2);
|
|
73
|
+
case 'diagnostics':
|
|
74
|
+
return JSON.stringify(snapshot.dependencyDiagnostics, null, 2);
|
|
75
|
+
default:
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function WorkbenchDevtoolsPanel() {
|
|
81
|
+
const snapshot = useWorkbenchDevtoolsSnapshot();
|
|
82
|
+
const [activeSection, setActiveSection] = useState<DevtoolsSectionId>('commands');
|
|
83
|
+
const sectionValue = useMemo(
|
|
84
|
+
() => formatSectionValue(activeSection, snapshot),
|
|
85
|
+
[activeSection, snapshot],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<aside
|
|
90
|
+
aria-label="Workbench devtools"
|
|
91
|
+
className="workbench-devtools-panel"
|
|
92
|
+
data-testid="workbench-devtools-panel"
|
|
93
|
+
>
|
|
94
|
+
<header className="workbench-devtools-panel__header">
|
|
95
|
+
<strong>Workbench Devtools</strong>
|
|
96
|
+
<span className="workbench-devtools-panel__meta">Read-only</span>
|
|
97
|
+
</header>
|
|
98
|
+
<p className="workbench-devtools-panel__timestamp">Updated {snapshot.capturedAt}</p>
|
|
99
|
+
<nav aria-label="Devtools sections" className="workbench-devtools-panel__tabs">
|
|
100
|
+
{DEVTOOLS_SECTIONS.map((section) => (
|
|
101
|
+
<Button
|
|
102
|
+
key={section.id}
|
|
103
|
+
aria-pressed={activeSection === section.id}
|
|
104
|
+
compact
|
|
105
|
+
className="workbench-devtools-panel__tab"
|
|
106
|
+
onClick={() => {
|
|
107
|
+
setActiveSection(section.id);
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
{section.label}
|
|
111
|
+
</Button>
|
|
112
|
+
))}
|
|
113
|
+
</nav>
|
|
114
|
+
<ScrollArea
|
|
115
|
+
as="pre"
|
|
116
|
+
className="workbench-devtools-panel__output"
|
|
117
|
+
gutter="stable"
|
|
118
|
+
orientation="vertical"
|
|
119
|
+
>
|
|
120
|
+
{sectionValue}
|
|
121
|
+
</ScrollArea>
|
|
122
|
+
</aside>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { WorkbenchDevtoolsPanel } from './WorkbenchDevtoolsPanel.js';
|
|
4
|
+
import './workbench-devtools.css';
|
|
5
|
+
|
|
6
|
+
export interface WorkbenchDevtoolsShellProps {
|
|
7
|
+
readonly children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function WorkbenchDevtoolsShell({ children }: WorkbenchDevtoolsShellProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="workbench-devtools-shell" data-testid="workbench-devtools-shell">
|
|
13
|
+
<div className="workbench-devtools-shell__main">{children}</div>
|
|
14
|
+
<WorkbenchDevtoolsPanel />
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { WorkbenchDevtoolsPanel } from './WorkbenchDevtoolsPanel.js';
|
|
2
|
+
export {
|
|
3
|
+
WorkbenchDevtoolsShell,
|
|
4
|
+
type WorkbenchDevtoolsShellProps,
|
|
5
|
+
} from './WorkbenchDevtoolsShell.js';
|
|
6
|
+
export {
|
|
7
|
+
collectWorkbenchDevtoolsSnapshot,
|
|
8
|
+
type CollectWorkbenchDevtoolsSnapshotInput,
|
|
9
|
+
type WorkbenchDevtoolsSnapshot,
|
|
10
|
+
} from './workbench-devtools-snapshot.js';
|
|
11
|
+
export { useWorkbenchDevtoolsSnapshot } from './use-workbench-devtools-snapshot.js';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { useMemo, useSyncExternalStore } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
collectWorkbenchDevtoolsSnapshot,
|
|
5
|
+
type WorkbenchDevtoolsSnapshot,
|
|
6
|
+
} from './workbench-devtools-snapshot.js';
|
|
7
|
+
import { useWorkbench } from '../shell/provider.js';
|
|
8
|
+
import type { WorkbenchWorkspaceHostPort as WorkspaceHostPort } from '@workbench-kit/workspace';
|
|
9
|
+
|
|
10
|
+
export function useWorkbenchDevtoolsSnapshot(): WorkbenchDevtoolsSnapshot {
|
|
11
|
+
const { editorService, extensionRegistry, layoutService, workspaceHostPort } = useWorkbench();
|
|
12
|
+
|
|
13
|
+
const store = useMemo(() => {
|
|
14
|
+
const collectSnapshot = () =>
|
|
15
|
+
collectWorkbenchDevtoolsSnapshot({
|
|
16
|
+
editorService,
|
|
17
|
+
extensionRegistry,
|
|
18
|
+
layoutService,
|
|
19
|
+
workspaceHostPort,
|
|
20
|
+
});
|
|
21
|
+
let snapshot = collectSnapshot();
|
|
22
|
+
|
|
23
|
+
const notifyChange = (onStoreChange: () => void) => {
|
|
24
|
+
snapshot = collectSnapshot();
|
|
25
|
+
onStoreChange();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
getSnapshot: () => snapshot,
|
|
30
|
+
subscribe: (onStoreChange: () => void) => {
|
|
31
|
+
const disposables = [
|
|
32
|
+
layoutService.onDidChangeLayout(() => {
|
|
33
|
+
notifyChange(onStoreChange);
|
|
34
|
+
}),
|
|
35
|
+
editorService.onDidChangeEditors(() => {
|
|
36
|
+
notifyChange(onStoreChange);
|
|
37
|
+
}),
|
|
38
|
+
extensionRegistry.commands.onDidRegisterCommand(() => {
|
|
39
|
+
notifyChange(onStoreChange);
|
|
40
|
+
}),
|
|
41
|
+
extensionRegistry.menus.onDidRegisterMenuItem(() => {
|
|
42
|
+
notifyChange(onStoreChange);
|
|
43
|
+
}),
|
|
44
|
+
extensionRegistry.keybindings.onDidRegisterKeybinding(() => {
|
|
45
|
+
notifyChange(onStoreChange);
|
|
46
|
+
}),
|
|
47
|
+
extensionRegistry.views.onDidRegisterView(() => {
|
|
48
|
+
notifyChange(onStoreChange);
|
|
49
|
+
}),
|
|
50
|
+
extensionRegistry.views.onDidRegisterViewContainer(() => {
|
|
51
|
+
notifyChange(onStoreChange);
|
|
52
|
+
}),
|
|
53
|
+
extensionRegistry.views.onDidRegisterViewProvider(() => {
|
|
54
|
+
notifyChange(onStoreChange);
|
|
55
|
+
}),
|
|
56
|
+
extensionRegistry.activities.onDidRegisterActivity(() => {
|
|
57
|
+
notifyChange(onStoreChange);
|
|
58
|
+
}),
|
|
59
|
+
extensionRegistry.onDidActivateExtension(() => {
|
|
60
|
+
notifyChange(onStoreChange);
|
|
61
|
+
}),
|
|
62
|
+
extensionRegistry.onDidDeactivateExtension(() => {
|
|
63
|
+
notifyChange(onStoreChange);
|
|
64
|
+
}),
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const workspacePort = workspaceHostPort as WorkspaceHostPort | undefined;
|
|
68
|
+
const unsubscribeWorkspace = workspacePort?.service.onDidChangeWorkspace(() => {
|
|
69
|
+
notifyChange(onStoreChange);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return () => {
|
|
73
|
+
for (const disposable of disposables) {
|
|
74
|
+
disposable.dispose();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
unsubscribeWorkspace?.();
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}, [editorService, extensionRegistry, layoutService, workspaceHostPort]);
|
|
82
|
+
|
|
83
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
84
|
+
}
|