@workbench-kit/react 0.0.2-prototype.0.2.13 → 0.0.2-prototype.0.2.14

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.
Files changed (30) hide show
  1. package/README.md +29 -0
  2. package/package.json +10 -10
  3. package/src/index.ts +16 -0
  4. package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +1 -3
  5. package/src/jdw/createBuiltinJdwRegistry.ts +1 -4
  6. package/src/jdw/cssRenderBackend.tsx +1 -4
  7. package/src/modal/modalPosition.ts +3 -6
  8. package/src/overlay/measureAnchoredOverlayPanel.ts +4 -6
  9. package/src/primitives/select/Select.tsx +1 -13
  10. package/src/test-utils/workbenchMonacoMock.tsx +20 -0
  11. package/src/utils/clamp.ts +4 -0
  12. package/src/utils/normalizeKeyToken.ts +13 -0
  13. package/src/utils/readNumber.ts +4 -0
  14. package/src/widget-tree/WidgetTreeCanvasPreview.tsx +1 -4
  15. package/src/workbench/commands/ShortcutCommandBridge.tsx +1 -13
  16. package/src/workbench/commands/WorkbenchQuickOpen.tsx +410 -0
  17. package/src/workbench/commands/createWorkspaceFilesQuickOpenProvider.ts +86 -0
  18. package/src/workbench/commands/quick-open-model.ts +57 -0
  19. package/src/workbench/index.ts +16 -0
  20. package/src/workbench/management/ExtensionManagementPanel.tsx +1 -34
  21. package/src/workbench/management/ExtensionManagementSidebar.tsx +1 -34
  22. package/src/workbench/management/IntegrationSettingsSurface.tsx +2 -5
  23. package/src/workbench/management/KeybindingCaptureField.tsx +1 -9
  24. package/src/workbench/management/WorkbenchNotice.tsx +2 -5
  25. package/src/workbench/management/extension-management-filters.ts +38 -0
  26. package/src/workbench/settings/StructuredDataForm.tsx +1 -4
  27. package/src/workbench/settings/structuredDataFormModel.ts +2 -1
  28. package/src/workbench/shell/WorkbenchShell.tsx +40 -1
  29. package/src/workbench/shell/WorkbenchShellTitleBarLayoutControls.tsx +4 -7
  30. package/src/workbench/shell/activity-bar.css +47 -0
package/README.md CHANGED
@@ -63,6 +63,35 @@ Full walkthrough: [Getting Started](../../docs/guides/getting-started.md).
63
63
 
64
64
  Import **only** through `exports`. Do not deep-import `src/` paths.
65
65
 
66
+ ## Quick Open providers
67
+
68
+ `WorkbenchQuickOpen` is a file/provider overlay distinct from the command palette.
69
+ Hosts (or `@workbench-kit/shell-react` `WorkbenchCommandHost`) pass pluggable
70
+ providers; the default workspace-files provider wraps
71
+ `searchWorkspaceFiles` from `@workbench-kit/workspace`.
72
+
73
+ ```ts
74
+ import {
75
+ WorkbenchQuickOpen,
76
+ createWorkspaceFilesQuickOpenProvider,
77
+ type QuickOpenProvider,
78
+ } from '@workbench-kit/react/workbench';
79
+
80
+ const providers: QuickOpenProvider[] = [
81
+ createWorkspaceFilesQuickOpenProvider({
82
+ files: workspaceFiles,
83
+ recentPaths: ['src/App.tsx'],
84
+ }),
85
+ ];
86
+ ```
87
+
88
+ Contract:
89
+
90
+ - `provider.search(query)` returns items (sync or `Promise`)
91
+ - Empty query → all files, with optional `recentPaths` elevated first
92
+ - Enter selects the active item; Escape closes (modal focus trap)
93
+ - Shell default: Ctrl/Cmd+P opens Quick Open; Ctrl/Cmd+Shift+P opens the command palette
94
+
66
95
  ## Learn more
67
96
 
68
97
  | Doc | Purpose |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workbench-kit/react",
3
- "version": "0.0.2-prototype.0.2.13",
3
+ "version": "0.0.2-prototype.0.2.14",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -58,15 +58,15 @@
58
58
  "@vscode/codicons": "^0.0.45",
59
59
  "remark-gfm": "^4.0.1",
60
60
  "rehype-sanitize": "^6.0.0",
61
- "@workbench-kit/adapters": "0.0.2-prototype.0.2.13",
62
- "@workbench-kit/runtime": "0.0.2-prototype.0.2.13",
63
- "@workbench-kit/platform": "0.0.2-prototype.0.2.13",
64
- "@workbench-kit/services": "0.0.2-prototype.0.2.13",
65
- "@workbench-kit/contracts": "0.0.2-prototype.0.2.13",
66
- "@workbench-kit/jdw": "0.0.2-prototype.0.2.13",
67
- "@workbench-kit/workspace": "0.0.2-prototype.0.2.13",
68
- "@workbench-kit/tokens": "0.0.2-prototype.0.2.13",
69
- "@workbench-kit/monaco": "0.0.2-prototype.0.2.13"
61
+ "@workbench-kit/contracts": "0.0.2-prototype.0.2.14",
62
+ "@workbench-kit/jdw": "0.0.2-prototype.0.2.14",
63
+ "@workbench-kit/adapters": "0.0.2-prototype.0.2.14",
64
+ "@workbench-kit/monaco": "0.0.2-prototype.0.2.14",
65
+ "@workbench-kit/runtime": "0.0.2-prototype.0.2.14",
66
+ "@workbench-kit/services": "0.0.2-prototype.0.2.14",
67
+ "@workbench-kit/workspace": "0.0.2-prototype.0.2.14",
68
+ "@workbench-kit/platform": "0.0.2-prototype.0.2.14",
69
+ "@workbench-kit/tokens": "0.0.2-prototype.0.2.14"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "react": "^19.0.0",
package/src/index.ts CHANGED
@@ -59,6 +59,15 @@ export {
59
59
  groupWorkbenchCommands,
60
60
  isWorkbenchCommandRunnable,
61
61
  } from './workbench/commands/CommandPalette';
62
+ export {
63
+ DEFAULT_QUICK_OPEN_SEARCH_DEBOUNCE_MS,
64
+ WORKSPACE_FILES_QUICK_OPEN_PROVIDER_ID,
65
+ WorkbenchQuickOpen,
66
+ createWorkspaceFilesQuickOpenProvider,
67
+ getNextQuickOpenItemIndex,
68
+ isQuickOpenItemSelectable,
69
+ resolveQuickOpenItemPath,
70
+ } from './workbench/commands/WorkbenchQuickOpen';
62
71
  export {
63
72
  WorkbenchShortcutCommandBridge,
64
73
  getWorkbenchShortcutCommandBindings,
@@ -87,6 +96,13 @@ export type {
87
96
  WorkbenchCommandStatus,
88
97
  WorkbenchCommandSuggestProps,
89
98
  } from './workbench/commands/CommandPalette';
99
+ export type {
100
+ CreateWorkspaceFilesQuickOpenProviderOptions,
101
+ QuickOpenItem,
102
+ QuickOpenProvider,
103
+ QuickOpenSelectContext,
104
+ WorkbenchQuickOpenProps,
105
+ } from './workbench/commands/WorkbenchQuickOpen';
90
106
  export type {
91
107
  UseWorkbenchShortcutCommandsOptions,
92
108
  WorkbenchShortcutCommandBinding,
@@ -1,9 +1,7 @@
1
1
  import { createElement, type CSSProperties, type ReactNode } from 'react';
2
2
  import type { GenericWidget } from '@workbench-kit/jdw';
3
3
 
4
- function readNumber(value: unknown): number | undefined {
5
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
6
- }
4
+ import { readNumber } from '../../utils/readNumber';
7
5
 
8
6
  function readString(value: unknown): string | undefined {
9
7
  return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
@@ -10,6 +10,7 @@ import {
10
10
  type GenericWidget,
11
11
  } from '@workbench-kit/jdw';
12
12
 
13
+ import { readNumber } from '../utils/readNumber';
13
14
  import { renderBuiltinWidgetLeaf } from './builtins/renderBuiltinWidgetLeaf.js';
14
15
 
15
16
  const LAYOUT_INSPECTOR = [
@@ -73,10 +74,6 @@ function build(widget: WidgetTypeShape) {
73
74
  return renderBuiltinWidgetLeaf(widget as GenericWidget);
74
75
  }
75
76
 
76
- function readNumber(value: unknown): number | undefined {
77
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
78
- }
79
-
80
77
  function readString(value: unknown): string | undefined {
81
78
  return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
82
79
  }
@@ -22,6 +22,7 @@ import {
22
22
  type WidgetPath,
23
23
  } from '@workbench-kit/jdw';
24
24
 
25
+ import { readNumber } from '../utils/readNumber';
25
26
  import { renderBuiltinWidgetLeaf } from './builtins/renderBuiltinWidgetLeaf.js';
26
27
  import { BUILTIN_JDW_REGISTRY } from './createBuiltinJdwRegistry.js';
27
28
  export interface CssRenderBackendOptions {
@@ -45,10 +46,6 @@ const LAYOUT_CONTAINER_TYPES = new Set([
45
46
  'sized_box',
46
47
  ]);
47
48
 
48
- function readNumber(value: unknown): number | undefined {
49
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
50
- }
51
-
52
49
  function renderFromRegistry(
53
50
  registry: WidgetRegistryContract<unknown>,
54
51
  widget: GenericWidget,
@@ -1,3 +1,4 @@
1
+ import { clampNumber } from '../utils/clamp';
1
2
  import type { ModalBounds, ModalPosition, ModalSize } from './modalTypes';
2
3
  import type { ModalContainerBounds } from './modalContainer';
3
4
  import { readWindowViewportBounds } from './modalContainer';
@@ -17,9 +18,9 @@ export function clampModalDragPosition(
17
18
  }
18
19
 
19
20
  const maxX = Math.max(0, viewport.width - size.width);
20
- const x = clamp(nextPosition.x, 0, maxX);
21
+ const x = clampNumber(nextPosition.x, 0, maxX);
21
22
  const maxY = size.height >= viewport.height ? 0 : Math.max(0, viewport.height - size.height);
22
- const y = clamp(nextPosition.y, 0, maxY);
23
+ const y = clampNumber(nextPosition.y, 0, maxY);
23
24
 
24
25
  return { x, y };
25
26
  }
@@ -30,7 +31,3 @@ export function clampModalBoundsPosition(
30
31
  ): ModalBounds {
31
32
  return { ...bounds, ...clampModalDragPosition(bounds, bounds, viewport) };
32
33
  }
33
-
34
- function clamp(value: number, min: number, max: number): number {
35
- return Math.min(Math.max(value, min), max);
36
- }
@@ -1,3 +1,5 @@
1
+ import { clampNumber } from '../utils/clamp';
2
+
1
3
  export type AnchoredOverlayPlacement = 'side' | 'below' | 'above';
2
4
 
3
5
  export interface MeasureAnchoredOverlayPanelOptions {
@@ -26,10 +28,6 @@ const SIDE_MIN_VISIBLE_HEIGHT_PX = 240;
26
28
  /** Prefer below when at least this many pixels are available under the trigger. */
27
29
  const BELOW_MIN_SPACE_PX = 12;
28
30
 
29
- function clamp(value: number, min: number, max: number): number {
30
- return Math.min(Math.max(value, min), max);
31
- }
32
-
33
31
  /**
34
32
  * Measure a panel-sized overlay anchored to a trigger element.
35
33
  * Prefers a side flyout when the trigger sits in the left portion of the
@@ -65,7 +63,7 @@ export function measureAnchoredOverlayPanel(
65
63
 
66
64
  if (preferSide) {
67
65
  const minVisibleHeight = Math.min(SIDE_MIN_VISIBLE_HEIGHT_PX, maxHeight);
68
- const top = clamp(
66
+ const top = clampNumber(
69
67
  triggerRect.top,
70
68
  viewportPaddingPx,
71
69
  viewportHeight - viewportPaddingPx - minVisibleHeight,
@@ -80,7 +78,7 @@ export function measureAnchoredOverlayPanel(
80
78
  }
81
79
 
82
80
  // Align the panel's right edge toward the trigger's right, then clamp.
83
- const left = clamp(
81
+ const left = clampNumber(
84
82
  triggerRect.right - width,
85
83
  viewportPaddingPx,
86
84
  viewportWidth - viewportPaddingPx - width,
@@ -16,6 +16,7 @@ import type { ControlWidth } from '../text-input/TextInput';
16
16
  import { cxCodicon } from '../../utils/codicon';
17
17
  import { cx } from '../../utils/cx';
18
18
  import { getEnabledOptionIndex, parseOptions } from './options';
19
+ import { resolvePortalContainer } from '../searchable-multi-select/overlay';
19
20
  import { isTriggerVisible, measureOverlayPosition, overlayListboxStyle } from './overlay';
20
21
  import type { OverlayPosition, ParsedOption } from './types';
21
22
 
@@ -24,19 +25,6 @@ export interface SelectProps extends ComponentPropsWithRef<'select'> {
24
25
  onValueChange?: (value: string, event: ChangeEvent<HTMLSelectElement>) => void;
25
26
  }
26
27
 
27
- /**
28
- * Portaling straight to `document.body` escapes the workbench root (`[data-theme-preset]`),
29
- * which re-declares theme tokens locally and shadows whatever a contributed theme overrides
30
- * on `document.documentElement`. Prefer themed hosts / overlay roots when present.
31
- */
32
- function resolvePortalContainer(trigger: HTMLElement | null): HTMLElement {
33
- return (
34
- trigger?.closest<HTMLElement>(
35
- '[data-theme-preset], [data-theme], .ui-workbench-host-root, .ide-workbench-overlays',
36
- ) ?? document.body
37
- );
38
- }
39
-
40
28
  export function Select({
41
29
  className,
42
30
  controlWidth = 'default',
@@ -9,14 +9,34 @@ export interface MockWorkbenchMonacoEditorProps {
9
9
  value?: string;
10
10
  }
11
11
 
12
+ export interface MockWorkbenchMonacoDiffEditorProps {
13
+ language?: string;
14
+ modified?: string;
15
+ onModifiedChange?: (value: string) => void;
16
+ original?: string;
17
+ readOnly?: boolean;
18
+ theme?: string;
19
+ }
20
+
12
21
  export function createWorkbenchMonacoMockModule(
13
22
  renderEditor?: (props: MockWorkbenchMonacoEditorProps) => ReactNode,
14
23
  ) {
15
24
  const defaultRender = ({ value }: MockWorkbenchMonacoEditorProps) =>
16
25
  createElement('div', { 'data-testid': 'monaco-editor' }, value ?? 'Mocked Monaco Editor');
17
26
 
27
+ const defaultDiffRender = ({ modified, original }: MockWorkbenchMonacoDiffEditorProps) =>
28
+ createElement(
29
+ 'div',
30
+ { 'data-testid': 'monaco-diff-editor' },
31
+ original ?? '',
32
+ '\n---\n',
33
+ modified ?? '',
34
+ );
35
+
18
36
  return {
19
37
  WorkbenchMonacoEditor: renderEditor ?? defaultRender,
38
+ WorkbenchMonacoDiffEditor: defaultDiffRender,
39
+ DiffEditor: defaultDiffRender,
20
40
  useMonacoWorkbenchThemeSync: () => undefined,
21
41
  prepareMonacoWorkbenchEditor: () => undefined,
22
42
  monacoThemeForWorkspaceTheme: (theme: string) =>
@@ -0,0 +1,4 @@
1
+ /** Clamp a finite number into the inclusive `[min, max]` range. */
2
+ export function clampNumber(value: number, min: number, max: number): number {
3
+ return Math.min(Math.max(value, min), max);
4
+ }
@@ -0,0 +1,13 @@
1
+ /** Normalize a keyboard shortcut token to a stable lowercase key name. */
2
+ export function normalizeKeyToken(token: string): string {
3
+ const key = token.trim().toLowerCase();
4
+ if (key === 'del') return 'delete';
5
+ if (key === 'esc') return 'escape';
6
+ if (key === 'return') return 'enter';
7
+ if (key === 'spacebar' || key === 'space') return 'space';
8
+ if (key === 'arrowup' || key === 'up') return 'arrowup';
9
+ if (key === 'arrowdown' || key === 'down') return 'arrowdown';
10
+ if (key === 'arrowleft' || key === 'left') return 'arrowleft';
11
+ if (key === 'arrowright' || key === 'right') return 'arrowright';
12
+ return key;
13
+ }
@@ -0,0 +1,4 @@
1
+ /** Return a finite number, or `undefined` when the value is not usable as one. */
2
+ export function readNumber(value: unknown): number | undefined {
3
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
4
+ }
@@ -26,6 +26,7 @@ import {
26
26
  type WidgetResizeHandlePosition,
27
27
  } from '@workbench-kit/jdw';
28
28
 
29
+ import { readNumber } from '../utils/readNumber';
29
30
  import {
30
31
  WorkbenchCanvasFrameHandle,
31
32
  WorkbenchCanvasItemFrame,
@@ -221,10 +222,6 @@ function readPositiveInteger(value: unknown): number | null {
221
222
  return Math.floor(value);
222
223
  }
223
224
 
224
- function readNumber(value: unknown): number | undefined {
225
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
226
- }
227
-
228
225
  function appendLineRect(rect: Rect, axis: 'x' | 'y', children: readonly LayoutNodeResult[]): Rect {
229
226
  const lastChildRect = children[children.length - 1]?.rect;
230
227
  if (axis === 'x') {
@@ -5,6 +5,7 @@ import {
5
5
  type CommandRegistry,
6
6
  type CommandValue,
7
7
  } from '@workbench-kit/platform';
8
+ import { normalizeKeyToken } from '../../utils/normalizeKeyToken';
8
9
 
9
10
  export type WorkbenchShortcutPlatform = 'linux' | 'mac' | 'unknown' | 'windows';
10
11
 
@@ -105,19 +106,6 @@ function getDefaultShortcutPlatform(): WorkbenchShortcutPlatform {
105
106
  return 'unknown';
106
107
  }
107
108
 
108
- function normalizeKeyToken(token: string) {
109
- const key = token.trim().toLowerCase();
110
- if (key === 'del') return 'delete';
111
- if (key === 'esc') return 'escape';
112
- if (key === 'return') return 'enter';
113
- if (key === 'spacebar' || key === 'space') return 'space';
114
- if (key === 'arrowup' || key === 'up') return 'arrowup';
115
- if (key === 'arrowdown' || key === 'down') return 'arrowdown';
116
- if (key === 'arrowleft' || key === 'left') return 'arrowleft';
117
- if (key === 'arrowright' || key === 'right') return 'arrowright';
118
- return key;
119
- }
120
-
121
109
  function normalizeEventKey(key: string) {
122
110
  if (key === ' ') return 'space';
123
111
  return normalizeKeyToken(key);