@workbench-kit/shell-react 0.0.2-prototype.0.2.13 → 0.0.2-prototype.0.2.15

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.
@@ -4,8 +4,14 @@ import {
4
4
  } from '@workbench-kit/platform';
5
5
  import {
6
6
  WorkbenchCommandPalette,
7
+ WorkbenchQuickOpen,
7
8
  WorkbenchShortcutCommandBridge,
8
9
  createWorkbenchShellCommands,
10
+ createWorkspaceFilesQuickOpenProvider,
11
+ resolveQuickOpenItemPath,
12
+ type QuickOpenItem,
13
+ type QuickOpenProvider,
14
+ type QuickOpenSelectContext,
9
15
  type WorkbenchCommandDescriptor,
10
16
  type WorkbenchCommandRunContext,
11
17
  type WorkbenchShellCommandContext,
@@ -22,33 +28,94 @@ import {
22
28
  resolveShellCommandActivities,
23
29
  } from './command-palette.js';
24
30
  import { resolveExtensionKeybindingCommand } from './keybinding-bridge.js';
31
+ import { isWorkspaceResourceService, useWorkspaceResourceState } from './workspace-view-state.js';
32
+
33
+ const WORKSPACE_OPEN_COMMAND_ID = 'workspace.open' as const;
25
34
 
26
35
  export interface WorkbenchCommandHostProps {
27
36
  additionalCommands?: readonly WorkbenchCommandDescriptor[];
37
+ /** Override command palette close control label (default English). */
38
+ commandPaletteCloseLabel?: string | undefined;
39
+ /** Override command palette empty-state copy (default English). */
40
+ commandPaletteEmptyLabel?: string | undefined;
41
+ /** Override command palette search placeholder (default English). */
42
+ commandPalettePlaceholder?: string | undefined;
43
+ /** Override command palette dialog title (default English). */
44
+ commandPaletteTitle?: string | undefined;
28
45
  enableCommandPalette?: boolean;
29
46
  enableExtensionKeybindings?: boolean;
47
+ /**
48
+ * When true (default), Ctrl/Cmd+P opens Quick Open instead of the command palette.
49
+ * Ctrl/Cmd+Shift+P still opens the command palette.
50
+ */
51
+ enableQuickOpen?: boolean;
30
52
  enableShortcutBridge?: boolean;
31
53
  onOpenSettings: () => void;
54
+ /**
55
+ * Called when a Quick Open item is selected. Return `true` to skip the default
56
+ * `workspace.open` path for file items.
57
+ */
58
+ onOpenQuickOpenItem?: (item: QuickOpenItem, context: QuickOpenSelectContext) => boolean | void;
32
59
  onRunCommand?: (
33
60
  command: WorkbenchCommandDescriptor,
34
61
  context: WorkbenchCommandRunContext,
35
62
  ) => boolean | void;
63
+ /** Override Quick Open close control label (default English). */
64
+ quickOpenCloseLabel?: string | undefined;
65
+ /** Override Quick Open empty-state copy (default English). */
66
+ quickOpenEmptyLabel?: string | undefined;
67
+ /** Override Quick Open search placeholder (default English). */
68
+ quickOpenPlaceholder?: string | undefined;
69
+ /**
70
+ * Extra / replacement Quick Open providers. When omitted, the host wires a
71
+ * workspace-files provider from the registered workspace host port.
72
+ */
73
+ quickOpenProviders?: readonly QuickOpenProvider[];
74
+ /** Override Quick Open dialog title (default English). */
75
+ quickOpenTitle?: string | undefined;
76
+ /** Optional recent paths elevated when the Quick Open query is empty. */
77
+ quickOpenRecentPaths?: readonly string[] | undefined;
36
78
  }
37
79
 
38
80
  export function WorkbenchCommandHost({
39
81
  additionalCommands = [],
82
+ commandPaletteCloseLabel = 'Close command palette',
83
+ commandPaletteEmptyLabel = 'No commands match your search',
84
+ commandPalettePlaceholder = 'Search commands',
85
+ commandPaletteTitle = 'Command Palette',
40
86
  enableCommandPalette = true,
41
87
  enableExtensionKeybindings = true,
88
+ enableQuickOpen = true,
42
89
  enableShortcutBridge = true,
43
90
  onOpenSettings,
91
+ onOpenQuickOpenItem,
44
92
  onRunCommand,
93
+ quickOpenCloseLabel = 'Close Quick Open',
94
+ quickOpenEmptyLabel = 'No matching files',
95
+ quickOpenPlaceholder = 'Search files by name',
96
+ quickOpenProviders,
97
+ quickOpenRecentPaths,
98
+ quickOpenTitle = 'Quick Open',
45
99
  }: WorkbenchCommandHostProps) {
46
- const { executeCommand, extensionRegistry, keybindingOverrides, layoutService } = useWorkbench();
100
+ const {
101
+ executeCommand,
102
+ extensionRegistry,
103
+ keybindingOverrides,
104
+ layoutService,
105
+ workspaceHostPort,
106
+ } = useWorkbench();
47
107
  const [paletteOpen, setPaletteOpen] = useState(false);
48
108
  const [paletteQuery, setPaletteQuery] = useState('');
109
+ const [quickOpenOpen, setQuickOpenOpen] = useState(false);
110
+ const [quickOpenQuery, setQuickOpenQuery] = useState('');
49
111
  const [layout, setLayout] = useState(() => layoutService.getState());
50
112
  const shellContextRef = useRef<WorkbenchShellCommandContext | undefined>(undefined);
51
113
 
114
+ const workspaceService = isWorkspaceResourceService(workspaceHostPort?.service)
115
+ ? workspaceHostPort.service
116
+ : undefined;
117
+ const workspaceState = useWorkspaceResourceState(workspaceService);
118
+
52
119
  useEffect(() => {
53
120
  const disposable = layoutService.onDidChangeLayout(({ state }) => {
54
121
  setLayout(state);
@@ -127,18 +194,43 @@ export function WorkbenchCommandHost({
127
194
  shellCommands: shellCommandDefinitions,
128
195
  shellContext,
129
196
  }),
130
- [additionalCommands, extensionRegistry, shellContext],
197
+ [additionalCommands, extensionRegistry, shellContext, shellCommandDefinitions],
131
198
  );
132
199
 
200
+ const resolvedQuickOpenProviders = useMemo(() => {
201
+ if (quickOpenProviders) {
202
+ return quickOpenProviders;
203
+ }
204
+
205
+ const files = workspaceState?.files ?? workspaceService?.getState().files ?? [];
206
+ return [
207
+ createWorkspaceFilesQuickOpenProvider({
208
+ files: () => workspaceService?.getState().files ?? files,
209
+ recentPaths: quickOpenRecentPaths,
210
+ }),
211
+ ];
212
+ }, [quickOpenProviders, quickOpenRecentPaths, workspaceService, workspaceState?.files]);
213
+
133
214
  const closePalette = useCallback(() => {
134
215
  setPaletteOpen(false);
135
216
  }, []);
136
217
 
137
218
  const openPalette = useCallback((query = '') => {
219
+ setQuickOpenOpen(false);
138
220
  setPaletteQuery(query);
139
221
  setPaletteOpen(true);
140
222
  }, []);
141
223
 
224
+ const closeQuickOpen = useCallback(() => {
225
+ setQuickOpenOpen(false);
226
+ }, []);
227
+
228
+ const openQuickOpen = useCallback((query = '') => {
229
+ setPaletteOpen(false);
230
+ setQuickOpenQuery(query);
231
+ setQuickOpenOpen(true);
232
+ }, []);
233
+
142
234
  const runPaletteCommand = useCallback(
143
235
  (command: WorkbenchCommandDescriptor, context: WorkbenchCommandRunContext) => {
144
236
  const finish = () => {
@@ -155,13 +247,35 @@ export function WorkbenchCommandHost({
155
247
  [closePalette, executeCommand, onRunCommand],
156
248
  );
157
249
 
250
+ const runQuickOpenItem = useCallback(
251
+ (item: QuickOpenItem, context: QuickOpenSelectContext) => {
252
+ const finish = () => {
253
+ closeQuickOpen();
254
+ };
255
+
256
+ if (onOpenQuickOpenItem?.(item, context)) {
257
+ finish();
258
+ return;
259
+ }
260
+
261
+ const path = resolveQuickOpenItemPath(item);
262
+ if (!path) {
263
+ finish();
264
+ return;
265
+ }
266
+
267
+ void executeCommand(WORKSPACE_OPEN_COMMAND_ID, { path }).finally(finish);
268
+ },
269
+ [closeQuickOpen, executeCommand, onOpenQuickOpenItem],
270
+ );
271
+
158
272
  useEffect(() => {
159
- if (!enableCommandPalette) {
273
+ if (!enableCommandPalette && !enableQuickOpen) {
160
274
  return undefined;
161
275
  }
162
276
 
163
277
  const onKeyDown = (event: KeyboardEvent) => {
164
- if (matchesWorkbenchCommandPaletteShortcut(event)) {
278
+ if (enableCommandPalette && matchesWorkbenchCommandPaletteShortcut(event)) {
165
279
  event.preventDefault();
166
280
  openPalette('>');
167
281
  return;
@@ -169,7 +283,14 @@ export function WorkbenchCommandHost({
169
283
 
170
284
  if (matchesWorkbenchQuickAccessShortcut(event)) {
171
285
  event.preventDefault();
172
- openPalette();
286
+ if (enableQuickOpen) {
287
+ openQuickOpen();
288
+ return;
289
+ }
290
+
291
+ if (enableCommandPalette) {
292
+ openPalette();
293
+ }
173
294
  }
174
295
  };
175
296
 
@@ -177,7 +298,7 @@ export function WorkbenchCommandHost({
177
298
  return () => {
178
299
  window.removeEventListener('keydown', onKeyDown);
179
300
  };
180
- }, [enableCommandPalette, openPalette]);
301
+ }, [enableCommandPalette, enableQuickOpen, openPalette, openQuickOpen]);
181
302
 
182
303
  useEffect(() => {
183
304
  if (!enableExtensionKeybindings) {
@@ -228,16 +349,32 @@ export function WorkbenchCommandHost({
228
349
  ) : null}
229
350
  {enableCommandPalette ? (
230
351
  <WorkbenchCommandPalette
352
+ closeLabel={commandPaletteCloseLabel}
231
353
  commands={paletteCommands}
354
+ emptyLabel={commandPaletteEmptyLabel}
232
355
  open={paletteOpen}
233
- placeholder="Search commands"
356
+ placeholder={commandPalettePlaceholder}
234
357
  query={paletteQuery}
235
- title="Command Palette"
358
+ title={commandPaletteTitle}
236
359
  onClose={closePalette}
237
360
  onQueryChange={setPaletteQuery}
238
361
  onRunCommand={runPaletteCommand}
239
362
  />
240
363
  ) : null}
364
+ {enableQuickOpen ? (
365
+ <WorkbenchQuickOpen
366
+ closeLabel={quickOpenCloseLabel}
367
+ emptyLabel={quickOpenEmptyLabel}
368
+ open={quickOpenOpen}
369
+ placeholder={quickOpenPlaceholder}
370
+ providers={resolvedQuickOpenProviders}
371
+ query={quickOpenQuery}
372
+ title={quickOpenTitle}
373
+ onClose={closeQuickOpen}
374
+ onQueryChange={setQuickOpenQuery}
375
+ onSelectItem={runQuickOpenItem}
376
+ />
377
+ ) : null}
241
378
  </>
242
379
  );
243
380
  }
@@ -3,7 +3,6 @@ import {
3
3
  type WorkbenchLayoutConfig,
4
4
  } from '@workbench-kit/workbench-config';
5
5
  import {
6
- createBrowserWorkbenchStorage,
7
6
  createWorkbenchLayoutState,
8
7
  type WorkbenchStorageReader,
9
8
  type WorkbenchStorageWriter,
@@ -11,10 +10,16 @@ import {
11
10
  type WorkbenchLayoutStateInput,
12
11
  } from '@workbench-kit/workbench-core';
13
12
 
13
+ import {
14
+ readLocalJsonStorage,
15
+ resolveLocalWorkbenchStorage,
16
+ writeLocalJsonStorage,
17
+ } from '../storage/local-json-storage.js';
18
+
14
19
  export const DEFAULT_WORKBENCH_LAYOUT_STORAGE_KEY = 'workbench-kit/.workbench/layout';
15
20
 
16
21
  export function isWorkbenchLayoutPersistenceAvailable(): boolean {
17
- return createBrowserWorkbenchStorage({ kind: 'local' }) !== undefined;
22
+ return resolveLocalWorkbenchStorage() !== undefined;
18
23
  }
19
24
 
20
25
  export function workbenchLayoutConfigToInput(
@@ -30,6 +35,8 @@ export function workbenchLayoutConfigToInput(
30
35
  visible: config.auxiliaryBar.visible,
31
36
  },
32
37
  panel: {
38
+ activeViewContainer: config.panel.activeViewContainer,
39
+ sizePercent: config.panel.sizePercent,
33
40
  visible: config.panel.visible,
34
41
  },
35
42
  sideBar: {
@@ -58,6 +65,10 @@ export function workbenchLayoutStateToStorageValue(
58
65
  },
59
66
  panel: {
60
67
  visible: state.panel.visible,
68
+ ...(state.panel.activeViewContainer
69
+ ? { activeViewContainer: state.panel.activeViewContainer }
70
+ : {}),
71
+ ...(state.panel.sizePercent !== undefined ? { sizePercent: state.panel.sizePercent } : {}),
61
72
  },
62
73
  sideBar: {
63
74
  visible: state.sideBar.visible,
@@ -75,17 +86,12 @@ export function readPersistedWorkbenchLayout(
75
86
  storageKey = DEFAULT_WORKBENCH_LAYOUT_STORAGE_KEY,
76
87
  storage?: WorkbenchStorageReader,
77
88
  ): WorkbenchLayoutStateInput | undefined {
78
- const resolvedStorage = storage ?? createBrowserWorkbenchStorage({ kind: 'local' });
79
- if (!resolvedStorage) return undefined;
80
-
81
- try {
82
- const raw = resolvedStorage.getItem(storageKey);
83
- if (!raw) return undefined;
84
-
85
- return workbenchLayoutConfigToInput(parseWorkbenchLayoutConfig(JSON.parse(raw) as unknown));
86
- } catch {
87
- return undefined;
88
- }
89
+ return readLocalJsonStorage(
90
+ storageKey,
91
+ (value) => workbenchLayoutConfigToInput(parseWorkbenchLayoutConfig(value)),
92
+ () => undefined,
93
+ storage,
94
+ );
89
95
  }
90
96
 
91
97
  export function writePersistedWorkbenchLayout(
@@ -93,17 +99,9 @@ export function writePersistedWorkbenchLayout(
93
99
  storageKey = DEFAULT_WORKBENCH_LAYOUT_STORAGE_KEY,
94
100
  storage?: WorkbenchStorageWriter,
95
101
  ): void {
96
- const resolvedStorage = storage ?? createBrowserWorkbenchStorage({ kind: 'local' });
97
- if (!resolvedStorage) return;
98
-
99
- try {
100
- resolvedStorage.setItem(
101
- storageKey,
102
- JSON.stringify(workbenchLayoutStateToStorageValue(state), null, 2),
103
- );
104
- } catch {
105
- // Ignore quota and security errors so the shell keeps working offline.
106
- }
102
+ writeLocalJsonStorage(storageKey, state, storage, {
103
+ toStorageValue: workbenchLayoutStateToStorageValue,
104
+ });
107
105
  }
108
106
 
109
107
  export function resolvePersistedWorkbenchLayout(