@workbench-kit/react 0.0.2-prototype.0.2.11 → 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
@@ -2,6 +2,7 @@ import { useEffect, useId, useRef, useState, type KeyboardEvent } from 'react';
2
2
  import { formatKeybindingLabel } from '@workbench-kit/platform';
3
3
  import { Button } from '../../primitives/button';
4
4
  import { cx } from '../../utils/cx';
5
+ import { normalizeKeyToken } from '../../utils/normalizeKeyToken';
5
6
 
6
7
  export interface KeybindingCaptureFieldProps {
7
8
  ariaLabel?: string | undefined;
@@ -131,12 +132,3 @@ function normalizeKeybindingKeyFromEvent(
131
132
  parts.push(normalizeKeyToken(event.key));
132
133
  return parts.join('+');
133
134
  }
134
-
135
- function normalizeKeyToken(token: string): string {
136
- const key = token.trim().toLowerCase();
137
- if (key === 'del') return 'delete';
138
- if (key === 'esc') return 'escape';
139
- if (key === 'return') return 'enter';
140
- if (key === 'spacebar' || key === 'space') return 'space';
141
- return key.length === 1 ? key : key;
142
- }
@@ -9,6 +9,7 @@ import {
9
9
  type ReactNode,
10
10
  } from 'react';
11
11
  import { IconButton, WorkbenchBanner, WorkbenchBannerMessage } from '../../primitives/index';
12
+ import { cx } from '../../utils/cx';
12
13
 
13
14
  export type WorkbenchNoticeTone = 'error' | 'info' | 'success' | 'warning';
14
15
  export type WorkbenchNoticePosition = 'bottom-center' | 'bottom-right';
@@ -187,7 +188,7 @@ export function useWorkbenchNotice(): WorkbenchNoticeController {
187
188
  }
188
189
 
189
190
  function resolveNoticeViewportClassName(position: WorkbenchNoticePosition): string {
190
- return joinClasses(
191
+ return cx(
191
192
  'pointer-events-none fixed inset-x-0 bottom-4 z-50 flex flex-col gap-2 px-4',
192
193
  position === 'bottom-center' ? 'items-center' : 'items-end',
193
194
  );
@@ -205,7 +206,3 @@ function resolveNoticeBannerStyle(tone: WorkbenchNoticeTone): CSSProperties {
205
206
  minWidth: 'min(420px, calc(100vw - 32px))',
206
207
  };
207
208
  }
208
-
209
- function joinClasses(...classes: Array<string | undefined>): string {
210
- return classes.filter(Boolean).join(' ');
211
- }
@@ -0,0 +1,38 @@
1
+ import type { ExtensionCatalogBrowseEntry, ExtensionManagementEntry } from './types.js';
2
+
3
+ export function filterInstalledEntries(
4
+ entries: readonly ExtensionManagementEntry[],
5
+ query: string,
6
+ ): readonly ExtensionManagementEntry[] {
7
+ const normalized = query.trim().toLowerCase();
8
+ if (!normalized) {
9
+ return entries;
10
+ }
11
+
12
+ return entries.filter((entry) =>
13
+ [entry.displayName, entry.id, entry.category, entry.description ?? '']
14
+ .join(' ')
15
+ .toLowerCase()
16
+ .includes(normalized),
17
+ );
18
+ }
19
+
20
+ export function filterBrowseEntries(
21
+ entries: readonly ExtensionCatalogBrowseEntry[],
22
+ query: string,
23
+ category?: string,
24
+ ): readonly ExtensionCatalogBrowseEntry[] {
25
+ const normalized = query.trim().toLowerCase();
26
+ return entries.filter((entry) => {
27
+ if (category && entry.category !== category) {
28
+ return false;
29
+ }
30
+ if (!normalized) {
31
+ return true;
32
+ }
33
+ return [entry.displayName, entry.id, entry.category, entry.description]
34
+ .join(' ')
35
+ .toLowerCase()
36
+ .includes(normalized);
37
+ });
38
+ }
@@ -37,6 +37,7 @@ import {
37
37
  type WorkbenchStructuredDataSchemaFieldInputProps,
38
38
  type WorkbenchStructuredDataTextArrayInputProps,
39
39
  } from './structuredDataSchema';
40
+ import { isRenderableWorkbenchStructuredDataFormError } from './structuredDataFormModel';
40
41
 
41
42
  export function WorkbenchStructuredDataForm({
42
43
  ariaLabel,
@@ -156,10 +157,6 @@ export function WorkbenchStructuredDataForm({
156
157
  );
157
158
  }
158
159
 
159
- function isRenderableWorkbenchStructuredDataFormError(error: ReactNode) {
160
- return error !== undefined && error !== null && error !== false && error !== '';
161
- }
162
-
163
160
  function StructuredDataSection({
164
161
  data,
165
162
  disabled,
@@ -135,6 +135,7 @@ function isWorkbenchStructuredDataFormEmptyValue(value: WorkbenchStructuredDataF
135
135
  return value === '' || value === false;
136
136
  }
137
137
 
138
- function isRenderableWorkbenchStructuredDataFormError(error: ReactNode) {
138
+ /** True when a form field error should render (non-empty ReactNode). */
139
+ export function isRenderableWorkbenchStructuredDataFormError(error: ReactNode) {
139
140
  return error !== undefined && error !== null && error !== false && error !== '';
140
141
  }
@@ -8,6 +8,16 @@ import { DEFAULT_PRIMARY_SIDEBAR_SIZE_PX } from './shellState';
8
8
  import { suppressNativeBrowserContextMenu } from '../commands/workbenchContextMenu';
9
9
  import { WorkbenchOverlaysProvider } from '../chrome/workbenchOverlaysContext';
10
10
 
11
+ const DEFAULT_BOTTOM_PANEL_SIZE_PERCENT = 30;
12
+
13
+ function clampBottomPanelSizePercent(value: number): number {
14
+ return Math.min(70, Math.max(10, value));
15
+ }
16
+
17
+ function clampBottomPanelPrimarySizePercent(value: number): number {
18
+ return Math.min(90, Math.max(30, value));
19
+ }
20
+
11
21
  export type WorkbenchShellActivityBarPosition = 'left' | 'top';
12
22
 
13
23
  export interface WorkbenchShellProps {
@@ -25,10 +35,18 @@ export interface WorkbenchShellProps {
25
35
  bottomPanel?: {
26
36
  isVisible: boolean;
27
37
  node: ReactNode;
38
+ /**
39
+ * Panel track size as a percent of the vertical editor+panel split.
40
+ * When set with `onSizePercentChange`, the split is controlled.
41
+ */
42
+ sizePercent?: number;
43
+ onSizePercentChange?: (sizePercent: number) => void;
28
44
  className?: string;
29
45
  style?: CSSProperties;
30
46
  };
31
47
  compactStatus?: boolean;
48
+ /** Accessible name for the status bar region (default “Status bar”). */
49
+ statusBarAriaLabel?: string;
32
50
  onStatusItemActivate?: (item: StatusBarItemModel) => void;
33
51
  primarySidebar?: {
34
52
  isVisible: boolean;
@@ -58,6 +76,7 @@ export function WorkbenchShell({
58
76
  auxiliarySidebar,
59
77
  bottomPanel,
60
78
  compactStatus = true,
79
+ statusBarAriaLabel,
61
80
  onStatusItemActivate,
62
81
  overlays,
63
82
  primarySidebar,
@@ -85,17 +104,36 @@ export function WorkbenchShell({
85
104
  const isBottomPanelCollapsed = bottomPanel !== undefined && !bottomPanel.isVisible;
86
105
  const isAuxiliarySidebarCollapsed = auxiliarySidebar !== undefined && !auxiliarySidebar.isVisible;
87
106
 
107
+ const bottomPanelSizePercent =
108
+ bottomPanel?.onSizePercentChange !== undefined
109
+ ? (bottomPanel.sizePercent ?? DEFAULT_BOTTOM_PANEL_SIZE_PERCENT)
110
+ : undefined;
111
+ const bottomPanelPrimarySizePercent =
112
+ bottomPanelSizePercent !== undefined
113
+ ? clampBottomPanelPrimarySizePercent(100 - bottomPanelSizePercent)
114
+ : undefined;
115
+
88
116
  const editorArea = bottomPanel ? (
89
117
  <SplitView
90
118
  className={cx(
91
119
  bottomPanel.className,
92
120
  isBottomPanelCollapsed && 'ui-workbench-split-view--secondary-collapsed',
93
121
  )}
94
- defaultPrimarySizePercent={70}
122
+ defaultPrimarySizePercent={100 - DEFAULT_BOTTOM_PANEL_SIZE_PERCENT}
95
123
  maxPrimarySizePercent={90}
96
124
  minPrimarySizePercent={30}
125
+ onPrimarySizePercentChange={
126
+ bottomPanel.onSizePercentChange
127
+ ? (primarySizePercent) => {
128
+ bottomPanel.onSizePercentChange?.(
129
+ clampBottomPanelSizePercent(100 - primarySizePercent),
130
+ );
131
+ }
132
+ : undefined
133
+ }
97
134
  orientation="vertical"
98
135
  primary={secondaryArea}
136
+ primarySizePercent={bottomPanelPrimarySizePercent}
99
137
  secondary={bottomPanel.node}
100
138
  />
101
139
  ) : (
@@ -178,6 +216,7 @@ export function WorkbenchShell({
178
216
  {body}
179
217
  </div>
180
218
  <StatusBar
219
+ aria-label={statusBarAriaLabel}
181
220
  compact={compactStatus}
182
221
  sections={statusSections}
183
222
  onItemActivate={onStatusItemActivate}
@@ -2,6 +2,7 @@ import '../chrome/workbench-shell-titlebar.css';
2
2
  import type { JSX } from 'react';
3
3
 
4
4
  import { IconButton } from '../../primitives/icon-button';
5
+ import { cx } from '../../utils/cx';
5
6
 
6
7
  export interface WorkbenchShellTitleBarLayoutControlsProps {
7
8
  readonly isAuxiliarySidebarVisible?: boolean;
@@ -18,10 +19,6 @@ export interface WorkbenchShellTitleBarLayoutControlsProps {
18
19
  readonly secondarySidebarShowLabel?: string;
19
20
  }
20
21
 
21
- function joinClasses(...classNames: Array<string | false | null | undefined>): string {
22
- return classNames.filter(Boolean).join(' ');
23
- }
24
-
25
22
  export function WorkbenchShellTitleBarLayoutControls({
26
23
  isAuxiliarySidebarVisible = false,
27
24
  isPanelVisible = false,
@@ -40,7 +37,7 @@ export function WorkbenchShellTitleBarLayoutControls({
40
37
  <div className="workbench-shell-titlebar__layout-controls">
41
38
  <IconButton
42
39
  aria-pressed={isPrimarySidebarVisible}
43
- className={joinClasses(
40
+ className={cx(
44
41
  'workbench-shell-titlebar__layout-control',
45
42
  isPrimarySidebarVisible && 'workbench-shell-titlebar__layout-control--active',
46
43
  )}
@@ -52,7 +49,7 @@ export function WorkbenchShellTitleBarLayoutControls({
52
49
  {onTogglePanel ? (
53
50
  <IconButton
54
51
  aria-pressed={isPanelVisible}
55
- className={joinClasses(
52
+ className={cx(
56
53
  'workbench-shell-titlebar__layout-control',
57
54
  isPanelVisible && 'workbench-shell-titlebar__layout-control--active',
58
55
  )}
@@ -65,7 +62,7 @@ export function WorkbenchShellTitleBarLayoutControls({
65
62
  {onToggleAuxiliarySidebar ? (
66
63
  <IconButton
67
64
  aria-pressed={isAuxiliarySidebarVisible}
68
- className={joinClasses(
65
+ className={cx(
69
66
  'workbench-shell-titlebar__layout-control',
70
67
  isAuxiliarySidebarVisible && 'workbench-shell-titlebar__layout-control--active',
71
68
  )}
@@ -32,6 +32,53 @@
32
32
  border-top: 1px solid var(--vscode-panel-border, var(--color-border));
33
33
  }
34
34
 
35
+ .workbench-bottom-panel__header {
36
+ display: flex;
37
+ flex-shrink: 0;
38
+ align-items: stretch;
39
+ gap: 0;
40
+ min-height: 28px;
41
+ border-bottom: 1px solid var(--vscode-panel-border, var(--color-border));
42
+ background: var(--vscode-panel-background, var(--color-surface));
43
+ }
44
+
45
+ .workbench-bottom-panel__tab {
46
+ appearance: none;
47
+ border: none;
48
+ background: transparent;
49
+ color: var(--color-text-muted);
50
+ font: inherit;
51
+ font-size: 12px;
52
+ padding: 4px 12px;
53
+ cursor: pointer;
54
+ }
55
+
56
+ .workbench-bottom-panel__tab:hover {
57
+ color: var(--color-text);
58
+ }
59
+
60
+ .workbench-bottom-panel__tab--active {
61
+ color: var(--color-text);
62
+ box-shadow: inset 0 -2px 0 var(--color-accent, var(--color-primary));
63
+ }
64
+
65
+ .workbench-bottom-panel__body {
66
+ flex: 1;
67
+ min-height: 0;
68
+ overflow: auto;
69
+ padding: 8px 12px;
70
+ }
71
+
72
+ .workbench-bottom-panel__view {
73
+ min-height: 0;
74
+ }
75
+
76
+ .workbench-bottom-panel__empty {
77
+ color: var(--color-text-muted);
78
+ font-size: 12px;
79
+ padding: 8px 12px;
80
+ }
81
+
35
82
  .ui-workbench-activity-bar__item {
36
83
  --ui-button-width: var(--workbench-activity-bar-item-size);
37
84
  --ui-button-min-width: 0;