@workbench-kit/react 0.0.2-prototype.0.2.2 → 0.0.2-prototype.0.2.21

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 (169) hide show
  1. package/README.md +125 -0
  2. package/package.json +27 -20
  3. package/src/index.ts +60 -29
  4. package/src/jdw/JsonCodeEditorPane.tsx +7 -6
  5. package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +1 -3
  6. package/src/jdw/createBuiltinJdwRegistry.ts +1 -4
  7. package/src/jdw/cssRenderBackend.tsx +1 -4
  8. package/src/json-config/JsonConfigWorkbench.tsx +1 -1
  9. package/src/layout/index.ts +11 -0
  10. package/src/layout/sidebar/SideBarTree.tsx +301 -0
  11. package/src/layout/sidebar/SidebarActionIconBar.tsx +3 -3
  12. package/src/layout/sidebar/WorkbenchSidebarActions.tsx +1 -1
  13. package/src/layout/sidebar/index.ts +13 -0
  14. package/src/modal/Modal.tsx +55 -25
  15. package/src/modal/ModalTitlebar.tsx +8 -8
  16. package/src/modal/modalPosition.ts +3 -6
  17. package/src/modal/useModalFocusTrap.ts +142 -0
  18. package/src/modal/useModalWindowFrame.ts +2 -2
  19. package/src/overlay/ContextMenu.tsx +112 -5
  20. package/src/overlay/context-menu.css +4 -2
  21. package/src/overlay/measureAnchoredOverlayPanel.ts +4 -6
  22. package/src/overlay/usePointerPassthroughRegion.ts +2 -0
  23. package/src/primitives/absolute-box/AbsoluteBox.tsx +4 -4
  24. package/src/primitives/button/Button.tsx +4 -4
  25. package/src/primitives/catalog-browse-card/CatalogBrowseCard.tsx +6 -6
  26. package/src/primitives/catalog-browse-pane/CatalogBrowsePane.tsx +15 -15
  27. package/src/primitives/catalog-filter-overlay/CatalogFilterOverlay.tsx +2 -2
  28. package/src/primitives/checkbox/Checkbox.tsx +1 -1
  29. package/src/primitives/chip/Chip.tsx +2 -2
  30. package/src/primitives/codicon/Codicon.tsx +1 -1
  31. package/src/primitives/file-icon/FileIcon.tsx +1 -1
  32. package/src/primitives/icon-button/IconButton.tsx +1 -1
  33. package/src/primitives/index.ts +5 -0
  34. package/src/primitives/library-detail-layout/LibraryDetailLayout.tsx +123 -9
  35. package/src/primitives/library-detail-layout/index.ts +5 -0
  36. package/src/primitives/library-detail-layout/library-detail-layout.css +57 -0
  37. package/src/primitives/library-detail-layout/resolve-hero-cover-media.ts +80 -0
  38. package/src/primitives/library-facet-filter-panel/LibraryFacetFilterPanel.tsx +12 -12
  39. package/src/primitives/library-facet-filter-strip/LibraryFacetFilterStrip.tsx +4 -4
  40. package/src/primitives/number-input/NumberInput.tsx +5 -5
  41. package/src/primitives/record-media-hero/RecordMediaHero.tsx +10 -6
  42. package/src/primitives/scroll-area/ScrollArea.tsx +5 -5
  43. package/src/primitives/scroll-area-infinite-load/useScrollAreaInfiniteLoad.ts +5 -5
  44. package/src/primitives/searchable-multi-select/SearchableMultiSelect.tsx +7 -7
  45. package/src/primitives/select/Select.tsx +1 -13
  46. package/src/primitives/text-area/TextArea.tsx +1 -1
  47. package/src/primitives/text-input/TextInput.tsx +1 -1
  48. package/src/primitives/workbench-media-slot/WorkbenchMediaPlaceholder.tsx +2 -2
  49. package/src/primitives/workbench-media-slot/WorkbenchMediaSlot.tsx +16 -10
  50. package/src/primitives/workbench-thumbnail/WorkbenchThumbnail.tsx +4 -4
  51. package/src/styles.css +1 -2
  52. package/src/test-utils/workbenchMonacoMock.tsx +20 -0
  53. package/src/utils/clamp.ts +4 -0
  54. package/src/utils/normalizeKeyToken.ts +13 -0
  55. package/src/utils/readNumber.ts +4 -0
  56. package/src/widget-tree/WidgetTreeCanvasPreview.tsx +1 -4
  57. package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +2 -2
  58. package/src/workbench/chat/ChatCommandProposalCard.tsx +4 -4
  59. package/src/workbench/chat/ChatConversationBar.tsx +3 -3
  60. package/src/workbench/chat/ChatHistoryMenu.tsx +1 -1
  61. package/src/workbench/chat/ChatMessageCollapsible.tsx +4 -0
  62. package/src/workbench/chat/ChatMessageItem.tsx +68 -10
  63. package/src/workbench/chat/ChatMessageList.tsx +1 -1
  64. package/src/workbench/chat/ChatPanel.tsx +32 -23
  65. package/src/workbench/chat/ChatPhasedRunProgress.tsx +86 -13
  66. package/src/workbench/chat/ChatRenameDialog.tsx +1 -1
  67. package/src/workbench/chat/chat-conversation.css +2 -2
  68. package/src/workbench/chat/chat-ui.css +8 -0
  69. package/src/workbench/chat/chatConversation.ts +2 -2
  70. package/src/workbench/chat/chatMessageMeta.tsx +4 -4
  71. package/src/workbench/chat/index.ts +1 -0
  72. package/src/workbench/chat/types.ts +7 -7
  73. package/src/workbench/chat/useCancelRuntimeOnUnmount.ts +4 -4
  74. package/src/workbench/chat/useChatPrependPagination.ts +10 -10
  75. package/src/workbench/chat/useSlashCommandSuggest.ts +1 -1
  76. package/src/workbench/{WorkbenchModalPortal.tsx → chrome/WorkbenchModalPortal.tsx} +1 -1
  77. package/src/workbench/chrome/workbench-chrome.css +4 -0
  78. package/src/workbench/{CommandGroupShell.tsx → commands/CommandGroupShell.tsx} +2 -2
  79. package/src/workbench/{CommandList.tsx → commands/CommandList.tsx} +5 -5
  80. package/src/workbench/{CommandPalette.tsx → commands/CommandPalette.tsx} +16 -42
  81. package/src/workbench/{ConfirmationFlow.tsx → commands/ConfirmationFlow.tsx} +5 -5
  82. package/src/workbench/{ShortcutCommandBridge.tsx → commands/ShortcutCommandBridge.tsx} +1 -13
  83. package/src/workbench/commands/WorkbenchQuickOpen.tsx +462 -0
  84. package/src/workbench/{command-model.ts → commands/command-model.ts} +5 -1
  85. package/src/workbench/commands/command-ui.ts +3 -0
  86. package/src/workbench/{commands.ts → commands/commands.ts} +1 -1
  87. package/src/workbench/commands/createWorkspaceFilesQuickOpenProvider.ts +86 -0
  88. package/src/workbench/commands/quick-open-model.ts +66 -0
  89. package/src/workbench/{WorkbenchEditorTabs.tsx → editor/WorkbenchEditorTabs.tsx} +1 -1
  90. package/src/workbench/{editor-tabs-dnd.ts → editor/editor-tabs-dnd.ts} +1 -1
  91. package/src/workbench/{editorTabContextMenu.ts → editor/editorTabContextMenu.ts} +2 -2
  92. package/src/workbench/{useEditorTabsStripDnd.ts → editor/useEditorTabsStripDnd.ts} +1 -1
  93. package/src/workbench/{useWorkbenchEditorTabContextMenu.tsx → editor/useWorkbenchEditorTabContextMenu.tsx} +1 -1
  94. package/src/workbench/editor-tabs/index.ts +9 -9
  95. package/src/workbench/index.ts +110 -74
  96. package/src/workbench/management/ExtensionManagementPanel.tsx +32 -36
  97. package/src/workbench/management/ExtensionManagementSidebar.tsx +19 -35
  98. package/src/workbench/management/IntegrationSettingsSurface.tsx +2 -5
  99. package/src/workbench/management/IntegrationsShell.tsx +1 -1
  100. package/src/workbench/management/KeybindingCaptureField.tsx +1 -9
  101. package/src/workbench/management/WorkbenchDialogFrame.tsx +2 -2
  102. package/src/workbench/management/WorkbenchNotice.tsx +2 -5
  103. package/src/workbench/management/createWorkbenchNotify.tsx +88 -0
  104. package/src/workbench/management/extension-install-approval.ts +72 -0
  105. package/src/workbench/management/extension-management-filters.ts +38 -0
  106. package/src/workbench/management/index.ts +14 -0
  107. package/src/workbench/management/management.css +28 -0
  108. package/src/workbench/management/types.ts +14 -1
  109. package/src/workbench/management/useWorkbenchNotify.ts +10 -0
  110. package/src/workbench/{MarkdownPreview.tsx → markdown/MarkdownPreview.tsx} +22 -7
  111. package/src/workbench/markdown/markdownRemarkPlugins.ts +12 -0
  112. package/src/workbench/markdown/sanitizeMarkdownHref.ts +29 -0
  113. package/src/workbench/settings/StructuredDataForm.tsx +1 -4
  114. package/src/workbench/settings/structuredDataFormModel.ts +2 -1
  115. package/src/workbench/{ActivityBar.tsx → shell/ActivityBar.tsx} +8 -8
  116. package/src/workbench/{ArtifactShell.tsx → shell/ArtifactShell.tsx} +25 -25
  117. package/src/workbench/{SplitView.tsx → shell/SplitView.tsx} +3 -3
  118. package/src/workbench/{StatusBar.tsx → shell/StatusBar.tsx} +8 -8
  119. package/src/workbench/{StructuredArtifactEditor.tsx → shell/StructuredArtifactEditor.tsx} +9 -9
  120. package/src/workbench/{Timeline.tsx → shell/Timeline.tsx} +77 -20
  121. package/src/workbench/{WorkbenchCanvasShell.defaults.ts → shell/WorkbenchCanvasShell.defaults.ts} +1 -1
  122. package/src/workbench/{WorkbenchCanvasShell.model.ts → shell/WorkbenchCanvasShell.model.ts} +5 -5
  123. package/src/workbench/{WorkbenchCanvasShell.tsx → shell/WorkbenchCanvasShell.tsx} +6 -6
  124. package/src/workbench/{WorkbenchDesktopTitleBar.tsx → shell/WorkbenchDesktopTitleBar.tsx} +15 -15
  125. package/src/workbench/{WorkbenchShell.tsx → shell/WorkbenchShell.tsx} +45 -6
  126. package/src/workbench/{WorkbenchShellTitleBarLayoutControls.tsx → shell/WorkbenchShellTitleBarLayoutControls.tsx} +16 -19
  127. package/src/workbench/{WorkbenchStandaloneShell.tsx → shell/WorkbenchStandaloneShell.tsx} +4 -4
  128. package/src/workbench/{WorkbenchViewEditor.tsx → shell/WorkbenchViewEditor.tsx} +7 -7
  129. package/src/workbench/{WorkbenchViewSidebar.tsx → shell/WorkbenchViewSidebar.tsx} +7 -7
  130. package/src/workbench/{WorkbenchWindowChromeControls.tsx → shell/WorkbenchWindowChromeControls.tsx} +18 -15
  131. package/src/workbench/shell/activity-bar.css +47 -0
  132. package/src/workbench/{shell.ts → shell/shell.ts} +18 -12
  133. package/src/workbench/{shellViewModel.ts → shell/shellViewModel.ts} +3 -3
  134. package/src/workbench/{standalone.ts → shell/standalone.ts} +14 -2
  135. package/src/workbench/{useDeferredWorkbenchMount.ts → shell/useDeferredWorkbenchMount.ts} +3 -3
  136. package/src/workbench/{useWorkbenchAppearanceDocumentSync.tsx → shell/useWorkbenchAppearanceDocumentSync.tsx} +1 -1
  137. package/src/workbench/{useWorkbenchSidebarActionBarDnd.ts → shell/useWorkbenchSidebarActionBarDnd.ts} +1 -1
  138. package/src/workbench/{useWorkbenchSidebarViewPlacementDropZone.ts → shell/useWorkbenchSidebarViewPlacementDropZone.ts} +2 -2
  139. package/src/workbench/{workbenchViewRouteState.ts → shell/workbenchViewRouteState.ts} +4 -4
  140. package/src/workbench/{WorkbenchThemeProvider.tsx → theme/WorkbenchThemeProvider.tsx} +4 -4
  141. package/src/workbench/{theme.ts → theme/theme.ts} +1 -1
  142. package/src/workbench/{themePresets.ts → theme/themePresets.ts} +3 -2
  143. package/src/workbench/views/view-hosts.css +3 -3
  144. package/src/workbench/workspace/WorkspaceEditorPanel.tsx +1 -1
  145. package/src/workbench/workspace/WorkspaceExplorer.tsx +104 -3
  146. package/src/workbench/workspace/workspaceExplorerKeyboard.ts +109 -0
  147. package/src/workbench/workspace/workspaceJsonDiagnostics.ts +6 -5
  148. package/src/workbench/jdw/jdw-editors.css +0 -4
  149. package/src/workbench/jdw/jdw.css +0 -2
  150. package/src/workbench/markdownRemarkPlugins.ts +0 -4
  151. package/src/workbench/workbench-chrome.css +0 -4
  152. /package/src/workbench/{WorkbenchPlatformContext.tsx → chrome/WorkbenchPlatformContext.tsx} +0 -0
  153. /package/src/workbench/{workbenchModalViewState.ts → chrome/workbenchModalViewState.ts} +0 -0
  154. /package/src/workbench/{workbenchOverlaysContext.tsx → chrome/workbenchOverlaysContext.tsx} +0 -0
  155. /package/src/workbench/{workbenchPlatformChrome.ts → chrome/workbenchPlatformChrome.ts} +0 -0
  156. /package/src/workbench/{command-execution-policy.ts → commands/command-execution-policy.ts} +0 -0
  157. /package/src/workbench/{keyboard.ts → commands/keyboard.ts} +0 -0
  158. /package/src/workbench/{workbenchContextMenu.ts → commands/workbenchContextMenu.ts} +0 -0
  159. /package/src/workbench/{activityBarOrder.ts → shell/activityBarOrder.ts} +0 -0
  160. /package/src/workbench/{appearanceLabels.ts → shell/appearanceLabels.ts} +0 -0
  161. /package/src/workbench/{hardReset.ts → shell/hardReset.ts} +0 -0
  162. /package/src/workbench/{shellPresets.ts → shell/shellPresets.ts} +0 -0
  163. /package/src/workbench/{shellState.ts → shell/shellState.ts} +0 -0
  164. /package/src/workbench/{sidebarViewPlacementDnd.ts → shell/sidebarViewPlacementDnd.ts} +0 -0
  165. /package/src/workbench/{status.ts → shell/status.ts} +0 -0
  166. /package/src/workbench/{useWorkbenchSidebarLayout.tsx → shell/useWorkbenchSidebarLayout.tsx} +0 -0
  167. /package/src/workbench/{useWorkbenchStandaloneShellStateSync.tsx → shell/useWorkbenchStandaloneShellStateSync.tsx} +0 -0
  168. /package/src/workbench/{workbenchSidebarLayoutContext.ts → shell/workbenchSidebarLayoutContext.ts} +0 -0
  169. /package/src/workbench/{workbenchStandaloneShellReactContext.tsx → shell/workbenchStandaloneShellReactContext.tsx} +0 -0
@@ -379,6 +379,14 @@
379
379
  margin-top: 2px;
380
380
  }
381
381
 
382
+ .message__attachments {
383
+ display: flex;
384
+ flex-wrap: wrap;
385
+ gap: 6px;
386
+ margin-top: 6px;
387
+ max-width: 100%;
388
+ }
389
+
382
390
  .message__after {
383
391
  display: flex;
384
392
  flex-wrap: wrap;
@@ -4,9 +4,9 @@ export interface WorkbenchChatConversation {
4
4
  id: string;
5
5
  title: string;
6
6
  /** Short secondary line shown in history menu rows. */
7
- metaSummary?: string | undefined;
7
+ metaSummary?: string;
8
8
  /** Tooltip content for the active conversation pill. */
9
- metaTooltip?: ReactNode | undefined;
9
+ metaTooltip?: ReactNode;
10
10
  }
11
11
 
12
12
  export interface WorkbenchChatConversationLabels {
@@ -118,8 +118,8 @@ export function toChatMessageTimeDateTime(value: ChatMessageTimestampInput): str
118
118
  export function getPeerChatSenderKey(
119
119
  message: ChatMessage,
120
120
  options: {
121
- assistantLabel?: string | undefined;
122
- userLabel?: string | undefined;
121
+ assistantLabel?: string;
122
+ userLabel?: string;
123
123
  } = {},
124
124
  ): string {
125
125
  if (message.source === 'user') {
@@ -133,8 +133,8 @@ export function shouldShowPeerChatSenderLabel(
133
133
  messages: readonly ChatMessage[],
134
134
  index: number,
135
135
  options: {
136
- assistantLabel?: string | undefined;
137
- userLabel?: string | undefined;
136
+ assistantLabel?: string;
137
+ userLabel?: string;
138
138
  } = {},
139
139
  ): boolean {
140
140
  const message = messages[index];
@@ -5,6 +5,7 @@ export type { ChatCommandProposalCardProps } from './ChatCommandProposalCard';
5
5
  export { ChatPhasedRunProgress } from './ChatPhasedRunProgress';
6
6
  export type {
7
7
  ChatPhasedRunProgressAction,
8
+ ChatPhasedRunProgressLabels,
8
9
  ChatPhasedRunProgressProps,
9
10
  ChatRunPhase,
10
11
  ChatRunPhaseStatus,
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
 
3
- import type { WorkbenchCommandExecutionPolicy } from '../command-model';
3
+ import type { WorkbenchCommandExecutionPolicy } from '../commands/command-model';
4
4
 
5
5
  export type ChatMessageSource = 'assistant' | 'user';
6
6
 
@@ -15,11 +15,11 @@ export type ChatCommandProposalStatus =
15
15
  'pending' | 'running' | 'allowed' | 'denied' | 'blocked' | 'executed' | 'failed';
16
16
 
17
17
  export interface ChatCommandProposal {
18
- args?: readonly unknown[] | undefined;
18
+ args?: readonly unknown[];
19
19
  commandId: string;
20
- description?: string | undefined;
20
+ description?: string;
21
21
  id: string;
22
- label?: string | undefined;
22
+ label?: string;
23
23
  policy: WorkbenchCommandExecutionPolicy;
24
24
  status: ChatCommandProposalStatus;
25
25
  }
@@ -27,13 +27,13 @@ export interface ChatCommandProposal {
27
27
  export type ChatMessageTimestamp = number | Date | string;
28
28
 
29
29
  export interface ChatMessage {
30
- commandProposals?: readonly ChatCommandProposal[] | undefined;
30
+ commandProposals?: readonly ChatCommandProposal[];
31
31
  content: string;
32
32
  /**
33
33
  * How `content` is rendered. Defaults to markdown for assistant-layout
34
34
  * assistant messages; otherwise plain text.
35
35
  */
36
- contentMode?: ChatMessageContentMode | undefined;
36
+ contentMode?: ChatMessageContentMode;
37
37
  createdAt?: string;
38
38
  id: string;
39
39
  label?: ReactNode;
@@ -41,5 +41,5 @@ export interface ChatMessage {
41
41
  /** Preferred timestamp for display; falls back to `createdAt` when omitted. */
42
42
  timestamp?: ChatMessageTimestamp;
43
43
  /** Optional visual tone for host-driven status / error messages. */
44
- tone?: ChatMessageTone | undefined;
44
+ tone?: ChatMessageTone;
45
45
  }
@@ -5,10 +5,10 @@ export const DEFAULT_RUNTIME_UNMOUNT_CANCELLABLE_STATUSES = ['running'] as const
5
5
 
6
6
  export interface UseCancelRuntimeOnUnmountOptions<TStatus extends string = RuntimeStatus> {
7
7
  cancel: () => void;
8
- cancellableStatuses?: readonly TStatus[] | undefined;
9
- enabled?: boolean | undefined;
10
- getStatus?: (() => TStatus) | undefined;
11
- status?: TStatus | undefined;
8
+ cancellableStatuses?: readonly TStatus[];
9
+ enabled?: boolean;
10
+ getStatus?: () => TStatus;
11
+ status?: TStatus;
12
12
  }
13
13
 
14
14
  export function shouldCancelRuntimeOnUnmount(
@@ -8,23 +8,23 @@ import {
8
8
  } from './chatScroll';
9
9
 
10
10
  export interface UseChatPrependPaginationOptions {
11
- autoScrollBottomThreshold?: number | undefined;
12
- getScrollContainer?: ((listElement: HTMLDivElement | null) => HTMLElement | null) | undefined;
13
- initialVisibleItemCount?: number | undefined;
14
- isStreaming?: boolean | undefined;
11
+ autoScrollBottomThreshold?: number;
12
+ getScrollContainer?: (listElement: HTMLDivElement | null) => HTMLElement | null;
13
+ initialVisibleItemCount?: number;
14
+ isStreaming?: boolean;
15
15
  itemCount: number;
16
- lastItemId?: string | undefined;
16
+ lastItemId?: string;
17
17
  /** Content revision for the trailing item (e.g. streaming chunk updates). */
18
- lastItemRevision?: string | undefined;
19
- paginationKey?: string | undefined;
20
- rootMargin?: string | undefined;
18
+ lastItemRevision?: string;
19
+ paginationKey?: string;
20
+ rootMargin?: string;
21
21
  /**
22
22
  * - `true` (default): always scroll to the latest message
23
23
  * - `'auto'`: scroll only when the container is already near the bottom
24
24
  * - `false`: never auto-scroll
25
25
  */
26
- stickToBottom?: boolean | 'auto' | undefined;
27
- visibleItemPageSize?: number | undefined;
26
+ stickToBottom?: boolean | 'auto';
27
+ visibleItemPageSize?: number;
28
28
  }
29
29
 
30
30
  export interface UseChatPrependPaginationResult {
@@ -5,7 +5,7 @@ import {
5
5
  getNextWorkbenchCommandIndex,
6
6
  isWorkbenchCommandRunnable,
7
7
  type WorkbenchCommandDescriptor,
8
- } from '../CommandPalette';
8
+ } from '../commands/CommandPalette';
9
9
  import { getSlashCommandQuery, isSlashCommandInput } from './slashCommand';
10
10
 
11
11
  type SlashCommandSuggestKeyboardEvent = Pick<
@@ -1,7 +1,7 @@
1
1
  import { useLayoutEffect, useRef, useState, type ReactNode } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
3
 
4
- import { WORKBENCH_OVERLAYS_CLASS } from '../modal/modalContainer';
4
+ import { WORKBENCH_OVERLAYS_CLASS } from '../../modal/modalContainer';
5
5
  import { useWorkbenchOverlaysContainer } from './workbenchOverlaysContext';
6
6
 
7
7
  export interface WorkbenchModalPortalProps {
@@ -0,0 +1,4 @@
1
+ /* Workbench shell and platform chrome */
2
+ @import '../shell/index.css';
3
+ @import './index.css';
4
+ @import '../command-palette/command-palette.css';
@@ -1,6 +1,6 @@
1
1
  import { useId, useMemo, type ComponentPropsWithRef, type ReactNode } from 'react';
2
- import { EmptyState } from '../primitives/empty-state';
3
- import { cx } from '../utils/cx';
2
+ import { EmptyState } from '../../primitives/empty-state';
3
+ import { cx } from '../../utils/cx';
4
4
  import { WorkbenchCommandList } from './CommandList';
5
5
  import {
6
6
  filterWorkbenchCommands,
@@ -1,8 +1,8 @@
1
1
  import { useEffect, useId, useRef, type ComponentPropsWithRef, type ReactNode } from 'react';
2
- import { Button } from '../primitives/button';
3
- import { EmptyState } from '../primitives/empty-state';
4
- import { cxCodicon } from '../utils/codicon';
5
- import { cx } from '../utils/cx';
2
+ import { Button } from '../../primitives/button';
3
+ import { EmptyState } from '../../primitives/empty-state';
4
+ import { cxCodicon } from '../../utils/codicon';
5
+ import { cx } from '../../utils/cx';
6
6
  import {
7
7
  getWorkbenchCommandExecutionLabel,
8
8
  getWorkbenchCommandStatusLabel,
@@ -11,7 +11,7 @@ import {
11
11
  type WorkbenchCommandRunContext,
12
12
  type WorkbenchCommandRunSource,
13
13
  } from './command-model';
14
- import { isWorkbenchStatusBusy } from './status';
14
+ import { isWorkbenchStatusBusy } from '../shell/status';
15
15
 
16
16
  function commandIcon(command: WorkbenchCommandDescriptor) {
17
17
  if (!command.icon) return null;
@@ -9,9 +9,10 @@ import {
9
9
  type KeyboardEvent,
10
10
  type ReactNode,
11
11
  } from 'react';
12
- import { IconButton } from '../primitives/icon-button';
13
- import { TextInput } from '../primitives/text-input';
14
- import { cx } from '../utils/cx';
12
+ import { useModalFocusTrap } from '../../modal/useModalFocusTrap';
13
+ import { IconButton } from '../../primitives/icon-button';
14
+ import { TextInput } from '../../primitives/text-input';
15
+ import { cx } from '../../utils/cx';
15
16
  import {
16
17
  filterWorkbenchCommands,
17
18
  getNextWorkbenchCommandIndex,
@@ -130,8 +131,8 @@ export function WorkbenchCommandPalette({
130
131
  }: WorkbenchCommandPaletteProps) {
131
132
  const titleId = useId();
132
133
  const listId = useId();
134
+ const dialogRef = useRef<HTMLDivElement>(null);
133
135
  const inputRef = useRef<HTMLInputElement>(null);
134
- const previousFocusRef = useRef<HTMLElement | null>(null);
135
136
  const [uncontrolledActiveCommandId, setUncontrolledActiveCommandId] = useState<string>();
136
137
  const [resolvedQuery, setResolvedQuery] = useControllableCommandQuery({
137
138
  defaultQuery,
@@ -164,15 +165,13 @@ export function WorkbenchCommandPalette({
164
165
  [activeCommandId, onActiveCommandChange],
165
166
  );
166
167
 
167
- const restoreFocus = useCallback(() => {
168
- if (!restoreFocusOnClose) return;
169
- previousFocusRef.current?.focus();
170
- }, [restoreFocusOnClose]);
171
-
172
- const closePalette = useCallback(() => {
173
- restoreFocus();
174
- onClose();
175
- }, [onClose, restoreFocus]);
168
+ useModalFocusTrap({
169
+ enabled: open,
170
+ containerRef: dialogRef,
171
+ initialFocusRef: inputRef,
172
+ onClose,
173
+ restoreFocusOnClose,
174
+ });
176
175
 
177
176
  const runCommand = useCallback(
178
177
  (command: WorkbenchCommandDescriptor, context: WorkbenchCommandRunContext) => {
@@ -182,18 +181,6 @@ export function WorkbenchCommandPalette({
182
181
  [onRunCommand],
183
182
  );
184
183
 
185
- useEffect(() => {
186
- if (!open) return;
187
-
188
- previousFocusRef.current = document.activeElement as HTMLElement | null;
189
- const frame = window.requestAnimationFrame(() => inputRef.current?.focus());
190
-
191
- return () => {
192
- window.cancelAnimationFrame(frame);
193
- restoreFocus();
194
- };
195
- }, [open, restoreFocus]);
196
-
197
184
  useEffect(() => {
198
185
  if (!open) return;
199
186
  if (activeCommandId !== undefined) return;
@@ -207,12 +194,6 @@ export function WorkbenchCommandPalette({
207
194
 
208
195
  const handlePaletteKeyDown = useCallback(
209
196
  (event: WorkbenchCommandPaletteKeyEvent) => {
210
- if (event.key === 'Escape') {
211
- event.preventDefault();
212
- closePalette();
213
- return;
214
- }
215
-
216
197
  if (
217
198
  event.key !== 'ArrowDown' &&
218
199
  event.key !== 'ArrowUp' &&
@@ -275,15 +256,7 @@ export function WorkbenchCommandPalette({
275
256
  updateActiveCommand(filteredCommands[nextIndex]?.id);
276
257
  }
277
258
  },
278
- [
279
- activeCommand,
280
- activeIndex,
281
- closePalette,
282
- commandQuery,
283
- filteredCommands,
284
- runCommand,
285
- updateActiveCommand,
286
- ],
259
+ [activeCommand, activeIndex, commandQuery, filteredCommands, runCommand, updateActiveCommand],
287
260
  );
288
261
 
289
262
  useEffect(() => {
@@ -308,13 +281,14 @@ export function WorkbenchCommandPalette({
308
281
  if (!open) return null;
309
282
 
310
283
  return (
311
- <div className="ui-workbench-command-palette-overlay" onClick={closePalette}>
284
+ <div className="ui-workbench-command-palette-overlay" onClick={onClose}>
312
285
  <div
313
286
  aria-labelledby={titleId}
314
287
  aria-modal="true"
315
288
  className={cx('ui-workbench-command-palette', className)}
316
289
  role="dialog"
317
290
  {...props}
291
+ ref={dialogRef}
318
292
  onClick={(event) => event.stopPropagation()}
319
293
  >
320
294
  <div className="ui-workbench-command-palette__header">
@@ -325,7 +299,7 @@ export function WorkbenchCommandPalette({
325
299
  className="ui-workbench-command-palette__close"
326
300
  icon="codicon-close"
327
301
  label={closeLabel}
328
- onClick={closePalette}
302
+ onClick={onClose}
329
303
  />
330
304
  </div>
331
305
  <div className="ui-workbench-command-palette__search">
@@ -1,14 +1,14 @@
1
1
  import { useState, type FormEvent, type ReactNode } from 'react';
2
- import './shell/confirmation-flow.css';
3
- import { Modal } from '../modal/Modal';
4
- import { Button } from '../primitives/button';
5
- import { cx } from '../utils/cx';
2
+ import '../shell/confirmation-flow.css';
3
+ import { Modal } from '../../modal/Modal';
4
+ import { Button } from '../../primitives/button';
5
+ import { cx } from '../../utils/cx';
6
6
  import {
7
7
  getWorkbenchStatusLabel,
8
8
  isWorkbenchStatusBusy,
9
9
  isWorkbenchStatusDisabled,
10
10
  type WorkbenchStatus,
11
- } from './status';
11
+ } from '../shell/status';
12
12
 
13
13
  export type WorkbenchConfirmationVariant = 'default' | 'danger';
14
14
 
@@ -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);