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

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 (534) hide show
  1. package/package.json +31 -23
  2. package/src/brand/TilepaperAppIcon.tsx +181 -0
  3. package/src/brand/index.ts +2 -0
  4. package/src/brand/tilepaper-app-icon-geometry.ts +201 -0
  5. package/src/icons/WorkbenchIcon.tsx +57 -0
  6. package/src/icons/WorkbenchIconContext.tsx +25 -0
  7. package/src/icons/file-icon.ts +154 -0
  8. package/src/icons/index.ts +17 -0
  9. package/src/icons/resolveWorkbenchIcon.ts +48 -0
  10. package/src/icons/types.ts +28 -0
  11. package/src/index.ts +261 -71
  12. package/src/jdw/JdwPreview.tsx +165 -8
  13. package/src/jdw/JdwPreviewViewport.tsx +190 -0
  14. package/src/jdw/JsonCodeEditorPane.tsx +165 -15
  15. package/src/jdw/builtins/renderBuiltinWidgetLeaf.tsx +101 -0
  16. package/src/jdw/createBuiltinJdwRegistry.ts +330 -5
  17. package/src/jdw/cssRenderBackend.tsx +96 -7
  18. package/src/jdw/document.ts +0 -3
  19. package/src/jdw/fixtures/jdw-sample-screens.ts +10 -8
  20. package/src/jdw/index.ts +9 -4
  21. package/src/jdw/jdw-preview-viewport.css +41 -0
  22. package/src/jdw/renderJdw.tsx +49 -8
  23. package/src/json-config/JsonConfigWorkbench.tsx +11 -14
  24. package/src/layout/ViewEmptyState.tsx +14 -0
  25. package/src/layout/WorkbenchCanvas.tsx +113 -6
  26. package/src/layout/WorkbenchLayout.tsx +3 -0
  27. package/src/layout/WorkbenchLayoutBase.tsx +43 -2
  28. package/src/layout/WorkbenchMediaPreviewViewport.tsx +122 -0
  29. package/src/layout/WorkbenchPropertyOverrideLabel.tsx +90 -0
  30. package/src/layout/WorkbenchPropertyPanel.tsx +153 -17
  31. package/src/layout/WorkbenchPropertySearch.tsx +51 -0
  32. package/src/layout/WorkbenchTree.tsx +9 -10
  33. package/src/layout/canvas/canvas-drag-ghost.css +21 -0
  34. package/src/layout/canvas/canvas-drag-preview-frame.css +15 -0
  35. package/src/layout/canvas/canvas-drop-indicator.css +16 -0
  36. package/src/layout/canvas/canvas-frame-handle.css +37 -0
  37. package/src/layout/canvas/canvas-frame-surface.css +24 -0
  38. package/src/layout/canvas/canvas-guide-block.css +17 -0
  39. package/src/layout/canvas/canvas-guide-layer.css +6 -0
  40. package/src/layout/canvas/canvas-guide-line.css +22 -0
  41. package/src/layout/canvas/canvas-item-badge.css +23 -0
  42. package/src/layout/canvas/canvas-item-frame.css +54 -0
  43. package/src/layout/canvas/canvas-pane-surface.css +9 -0
  44. package/src/layout/canvas/canvas-placeholder.css +21 -0
  45. package/src/layout/canvas/canvas-resize-frame.css +9 -0
  46. package/src/layout/canvas/canvas-resize-handle.css +74 -0
  47. package/src/layout/canvas/canvas-resize-preview.css +11 -0
  48. package/src/layout/canvas/canvas-selection-marquee.css +14 -0
  49. package/src/layout/canvas/canvas-viewport.css +18 -0
  50. package/src/layout/canvas/index.css +19 -0
  51. package/src/layout/canvas/preview-canvas.css +144 -0
  52. package/src/layout/editor/editor-body.css +9 -0
  53. package/src/layout/editor/editor-bottom-panel.css +44 -0
  54. package/src/layout/editor/editor-frame.css +14 -0
  55. package/src/layout/editor/editor-viewport.css +7 -0
  56. package/src/layout/editor/floating-menu.css +91 -0
  57. package/src/layout/editor/index.css +10 -0
  58. package/src/layout/editor/parse-error.css +9 -0
  59. package/src/layout/editor/problem-item.css +52 -0
  60. package/src/layout/editor/problem-list.css +7 -0
  61. package/src/layout/editor/render-surface.css +13 -0
  62. package/src/layout/fillScrollContract.ts +49 -0
  63. package/src/layout/fullscreen/fullscreen-backdrop.css +31 -0
  64. package/src/layout/fullscreen/fullscreen-button.css +44 -0
  65. package/src/layout/fullscreen/fullscreen-carousel.css +20 -0
  66. package/src/layout/fullscreen/fullscreen-content.css +8 -0
  67. package/src/layout/fullscreen/fullscreen-empty.css +16 -0
  68. package/src/layout/fullscreen/fullscreen-header.css +77 -0
  69. package/src/layout/fullscreen/fullscreen-hero.css +41 -0
  70. package/src/layout/fullscreen/fullscreen-nav-button.css +29 -0
  71. package/src/layout/fullscreen/fullscreen-option.css +83 -0
  72. package/src/layout/fullscreen/fullscreen-pill-row.css +6 -0
  73. package/src/layout/fullscreen/fullscreen-pill.css +10 -0
  74. package/src/layout/fullscreen/fullscreen-root.css +11 -0
  75. package/src/layout/fullscreen/index.css +13 -0
  76. package/src/layout/index.ts +156 -0
  77. package/src/layout/layout-base/banner.css +32 -0
  78. package/src/layout/layout-base/center.css +7 -0
  79. package/src/layout/layout-base/column.css +7 -0
  80. package/src/layout/layout-base/divider.css +5 -0
  81. package/src/layout/layout-base/empty.css +12 -0
  82. package/src/layout/layout-base/fill-scroll.css +36 -0
  83. package/src/layout/layout-base/fill.css +18 -0
  84. package/src/layout/layout-base/index.css +13 -0
  85. package/src/layout/layout-base/pane.css +6 -0
  86. package/src/layout/layout-base/panel-scroll.css +5 -0
  87. package/src/layout/layout-base/panel-surface.css +14 -0
  88. package/src/layout/layout-base/root.css +9 -0
  89. package/src/layout/layout-base/section-title.css +8 -0
  90. package/src/layout/layout.css +10 -0
  91. package/src/layout/layoutHelpers.ts +4 -2
  92. package/src/layout/media/index.css +2 -0
  93. package/src/layout/media/media-preview-viewport.css +69 -0
  94. package/src/layout/{Panel.tsx → panel/Panel.tsx} +29 -40
  95. package/src/layout/panel/TemplateGallery.tsx +90 -0
  96. package/src/layout/panel/WorkbenchAuthoringShell.tsx +57 -0
  97. package/src/layout/panel/WorkbenchLabeledPane.tsx +81 -0
  98. package/src/layout/panel/WorkbenchSidebarStack.tsx +15 -0
  99. package/src/layout/panel/filter-bar.css +59 -0
  100. package/src/layout/panel/index.css +7 -0
  101. package/src/layout/panel/index.ts +70 -0
  102. package/src/layout/panel/preview-pane.css +63 -0
  103. package/src/layout/panel/template-gallery.css +131 -0
  104. package/src/layout/panel/workbench-authoring-shell.css +48 -0
  105. package/src/layout/panel/workbench-labeled-pane.css +69 -0
  106. package/src/layout/panel/workbench-sidebar-stack.css +18 -0
  107. package/src/layout/property/color-input.css +8 -0
  108. package/src/layout/property/color-row.css +6 -0
  109. package/src/layout/property/index.css +15 -0
  110. package/src/layout/property/property-card.css +8 -0
  111. package/src/layout/property/property-grid.css +24 -0
  112. package/src/layout/property/property-hint.css +4 -0
  113. package/src/layout/property/property-inline.css +10 -0
  114. package/src/layout/property/property-key-value.css +29 -0
  115. package/src/layout/property/property-override-label.css +34 -0
  116. package/src/layout/property/property-panel.css +20 -0
  117. package/src/layout/property/property-row.css +17 -0
  118. package/src/layout/property/property-search.css +43 -0
  119. package/src/layout/property/property-section.css +97 -0
  120. package/src/layout/property/property-stack.css +11 -0
  121. package/src/layout/property/property-toggle.css +14 -0
  122. package/src/layout/propertyFieldFilter.ts +84 -0
  123. package/src/layout/sidebar/SideBarViewFrame.tsx +235 -0
  124. package/src/layout/sidebar/SideBarViewTabStrip.tsx +60 -0
  125. package/src/layout/sidebar/SideBarViewTitleMenu.tsx +75 -0
  126. package/src/layout/sidebar/SidebarActionIconBar.tsx +242 -0
  127. package/src/layout/sidebar/SidebarSectionNestingContext.tsx +21 -0
  128. package/src/layout/sidebar/SidebarSlotChrome.tsx +32 -0
  129. package/src/layout/sidebar/SidebarToolbar.tsx +10 -0
  130. package/src/layout/sidebar/WorkbenchActionSidebar.tsx +73 -0
  131. package/src/layout/{WorkbenchSidebarActions.tsx → sidebar/WorkbenchSidebarActions.tsx} +105 -51
  132. package/src/layout/sidebar/WorkbenchSidebarSectionStack.tsx +165 -0
  133. package/src/layout/sidebar/index.css +5 -0
  134. package/src/layout/sidebar/index.ts +55 -0
  135. package/src/layout/sidebar/sidebar-chrome.css +95 -0
  136. package/src/layout/sidebar/sidebar-section-stack.css +25 -0
  137. package/src/layout/sidebar/sidebar-section.css +124 -0
  138. package/src/layout/sidebar/sidebar-view.css +290 -0
  139. package/src/layout/tree/drag-preview.css +25 -0
  140. package/src/layout/tree/index.css +10 -0
  141. package/src/layout/tree/template-glyph.css +96 -0
  142. package/src/layout/tree/tree-action.css +20 -0
  143. package/src/layout/tree/tree-drag-overlay.css +13 -0
  144. package/src/layout/tree/tree-drop-line.css +17 -0
  145. package/src/layout/tree/tree-drop-zone.css +13 -0
  146. package/src/layout/tree/tree-expander.css +23 -0
  147. package/src/layout/tree/tree-item.css +93 -0
  148. package/src/layout/tree/tree.css +49 -0
  149. package/src/layout/usePreviewViewport.ts +428 -0
  150. package/src/modal/ConfirmDialog.tsx +1 -1
  151. package/src/modal/Modal.tsx +148 -305
  152. package/src/modal/ModalResizeHandles.tsx +40 -0
  153. package/src/modal/ModalTitlebar.tsx +86 -0
  154. package/src/modal/modal.css +291 -0
  155. package/src/modal/modalContainer.ts +36 -0
  156. package/src/modal/modalPosition.ts +36 -0
  157. package/src/modal/modalSize.ts +84 -0
  158. package/src/modal/modalTypes.ts +13 -0
  159. package/src/modal/useModalContainerViewport.ts +41 -0
  160. package/src/modal/useModalDrag.ts +118 -0
  161. package/src/modal/useModalResize.ts +122 -0
  162. package/src/modal/useModalWindowFrame.ts +122 -0
  163. package/src/overlay/ContextMenu.tsx +28 -50
  164. package/src/overlay/clampFixedOverlayPosition.ts +22 -0
  165. package/src/overlay/context-menu.css +127 -0
  166. package/src/overlay/index.ts +31 -0
  167. package/src/overlay/measureAnchoredOverlayPanel.ts +107 -0
  168. package/src/overlay/overlay.css +2 -0
  169. package/src/overlay/pointerPassthroughRegion.ts +83 -0
  170. package/src/overlay/useAnchoredOverlayPanel.ts +222 -0
  171. package/src/overlay/useClampedFixedOverlayPosition.ts +27 -0
  172. package/src/overlay/useContextMenuState.ts +59 -0
  173. package/src/overlay/useFixedOverlayDismiss.ts +49 -0
  174. package/src/overlay/usePointerPassthroughRegion.ts +85 -0
  175. package/src/primitives/{AbsoluteBox.tsx → absolute-box/AbsoluteBox.tsx} +1 -1
  176. package/src/primitives/absolute-box/index.ts +1 -0
  177. package/src/primitives/{Badge.tsx → badge/Badge.tsx} +4 -2
  178. package/src/primitives/badge/badge.css +20 -0
  179. package/src/primitives/badge/index.ts +1 -0
  180. package/src/primitives/{Button.tsx → button/Button.tsx} +3 -2
  181. package/src/primitives/button/button.css +76 -0
  182. package/src/primitives/button/index.ts +1 -0
  183. package/src/primitives/catalog-browse-card/CatalogBrowseCard.tsx +121 -0
  184. package/src/primitives/catalog-browse-card/catalog-browse-card.css +175 -0
  185. package/src/primitives/catalog-browse-card/index.ts +1 -0
  186. package/src/primitives/catalog-browse-pane/CatalogBrowseFacetChips.tsx +59 -0
  187. package/src/primitives/catalog-browse-pane/CatalogBrowsePane.tsx +360 -0
  188. package/src/primitives/catalog-browse-pane/catalog-browse-facet-chips.css +8 -0
  189. package/src/primitives/catalog-browse-pane/catalog-browse-pane.css +84 -0
  190. package/src/primitives/catalog-browse-pane/filterCatalogBrowseItems.ts +27 -0
  191. package/src/primitives/catalog-browse-pane/index.ts +3 -0
  192. package/src/primitives/catalog-filter-overlay/CatalogFilterOverlay.tsx +73 -0
  193. package/src/primitives/catalog-filter-overlay/catalog-filter-overlay.css +65 -0
  194. package/src/primitives/catalog-filter-overlay/index.ts +1 -0
  195. package/src/primitives/checkbox/Checkbox.tsx +40 -0
  196. package/src/primitives/checkbox/checkbox.css +81 -0
  197. package/src/primitives/checkbox/index.ts +1 -0
  198. package/src/primitives/chip/Chip.tsx +43 -0
  199. package/src/primitives/chip/chip.css +110 -0
  200. package/src/primitives/chip/index.ts +2 -0
  201. package/src/primitives/clearable-text-input/ClearableTextInput.tsx +78 -0
  202. package/src/primitives/clearable-text-input/clearable-text-input.css +15 -0
  203. package/src/primitives/clearable-text-input/index.ts +1 -0
  204. package/src/primitives/codicon/Codicon.tsx +20 -0
  205. package/src/primitives/codicon/index.ts +1 -0
  206. package/src/primitives/{EmptyState.tsx → empty-state/EmptyState.tsx} +9 -3
  207. package/src/primitives/empty-state/empty-state.css +20 -0
  208. package/src/primitives/empty-state/index.ts +1 -0
  209. package/src/primitives/external-link-button/ExternalLinkButton.tsx +56 -0
  210. package/src/primitives/external-link-button/index.ts +1 -0
  211. package/src/primitives/{Field.tsx → field/Field.tsx} +2 -1
  212. package/src/primitives/field/field.css +51 -0
  213. package/src/primitives/field/index.ts +1 -0
  214. package/src/primitives/file-icon/FileIcon.tsx +21 -0
  215. package/src/primitives/file-icon/index.ts +1 -0
  216. package/src/primitives/{IconButton.tsx → icon-button/IconButton.tsx} +6 -4
  217. package/src/primitives/icon-button/icon-button.css +46 -0
  218. package/src/primitives/icon-button/index.ts +1 -0
  219. package/src/primitives/index.ts +210 -50
  220. package/src/primitives/library-detail-layout/LibraryDetailLayout.tsx +135 -0
  221. package/src/primitives/library-detail-layout/index.ts +1 -0
  222. package/src/primitives/library-detail-layout/library-detail-layout.css +169 -0
  223. package/src/primitives/library-facet-filter-panel/LibraryFacetFilterPanel.tsx +454 -0
  224. package/src/primitives/library-facet-filter-panel/index.ts +1 -0
  225. package/src/primitives/library-facet-filter-panel/library-facet-filter-panel.css +205 -0
  226. package/src/primitives/library-facet-filter-strip/LibraryFacetFilterStrip.tsx +527 -0
  227. package/src/primitives/library-facet-filter-strip/index.ts +1 -0
  228. package/src/primitives/library-facet-filter-strip/library-facet-filter-strip.css +31 -0
  229. package/src/primitives/{List.tsx → list/List.tsx} +15 -6
  230. package/src/primitives/list/index.ts +1 -0
  231. package/src/primitives/list/list-empty-state.css +10 -0
  232. package/src/primitives/list/list-item.css +93 -0
  233. package/src/primitives/list/list.css +6 -0
  234. package/src/primitives/number-input/NumberInput.tsx +235 -0
  235. package/src/primitives/number-input/index.ts +1 -0
  236. package/src/primitives/number-input/number-input.css +105 -0
  237. package/src/primitives/panel-loading/PanelLoading.tsx +29 -0
  238. package/src/primitives/panel-loading/index.ts +1 -0
  239. package/src/primitives/panel-loading/panel-loading.css +16 -0
  240. package/src/primitives/panel-surface/panel-surface.css +77 -0
  241. package/src/primitives/primitives.css +36 -0
  242. package/src/primitives/record-media-hero/RecordMediaHero.tsx +81 -0
  243. package/src/primitives/record-media-hero/index.ts +1 -0
  244. package/src/primitives/record-media-hero/record-media-hero.css +84 -0
  245. package/src/primitives/scroll-area/ScrollArea.tsx +200 -0
  246. package/src/primitives/scroll-area/index.ts +1 -0
  247. package/src/primitives/scroll-area/scroll-area-overlay.css +69 -0
  248. package/src/primitives/scroll-area-infinite-load/ScrollAreaInfiniteSentinel.tsx +22 -0
  249. package/src/primitives/scroll-area-infinite-load/index.ts +2 -0
  250. package/src/primitives/scroll-area-infinite-load/scroll-area-infinite-sentinel.css +6 -0
  251. package/src/primitives/scroll-area-infinite-load/useScrollAreaInfiniteLoad.ts +84 -0
  252. package/src/primitives/searchable-multi-select/SearchableMultiSelect.tsx +378 -0
  253. package/src/primitives/searchable-multi-select/index.ts +9 -0
  254. package/src/primitives/searchable-multi-select/overlay.ts +125 -0
  255. package/src/primitives/searchable-multi-select/searchable-multi-select.css +129 -0
  256. package/src/primitives/select/Select.tsx +21 -3
  257. package/src/primitives/select/overlay.ts +23 -4
  258. package/src/primitives/select/select.app.css +5 -2
  259. package/src/primitives/select/select.css +49 -12
  260. package/src/primitives/styles.css +3 -952
  261. package/src/primitives/tabbed-panels/TabbedPanels.tsx +68 -0
  262. package/src/primitives/tabbed-panels/index.ts +2 -0
  263. package/src/primitives/tabbed-panels/tabbed-panels.css +58 -0
  264. package/src/primitives/{TextArea.tsx → text-area/TextArea.tsx} +2 -1
  265. package/src/primitives/text-area/index.ts +1 -0
  266. package/src/primitives/{TextInput.tsx → text-input/TextInput.tsx} +2 -1
  267. package/src/primitives/text-input/index.ts +1 -0
  268. package/src/primitives/text-input/input.css +54 -0
  269. package/src/primitives/{Toolbar.tsx → toolbar/Toolbar.tsx} +1 -1
  270. package/src/primitives/toolbar/index.ts +1 -0
  271. package/src/primitives/visually-hidden/visually-hidden.css +11 -0
  272. package/src/primitives/{WorkbenchEditor.tsx → workbench-editor/WorkbenchEditor.tsx} +81 -43
  273. package/src/primitives/workbench-editor/button-group.css +10 -0
  274. package/src/primitives/workbench-editor/editor-tabs.css +201 -0
  275. package/src/primitives/workbench-editor/index.ts +1 -0
  276. package/src/primitives/workbench-editor/resizable-panels.css +53 -0
  277. package/src/primitives/workbench-editor/segmented-control.css +98 -0
  278. package/src/primitives/workbench-media-slot/WorkbenchMediaPlaceholder.tsx +25 -0
  279. package/src/primitives/workbench-media-slot/WorkbenchMediaSlot.tsx +60 -0
  280. package/src/primitives/workbench-media-slot/index.ts +2 -0
  281. package/src/primitives/workbench-media-slot/media-slot.css +72 -0
  282. package/src/primitives/workbench-thumbnail/WorkbenchThumbnail.tsx +38 -0
  283. package/src/primitives/workbench-thumbnail/index.ts +1 -0
  284. package/src/primitives/workbench-thumbnail/thumbnail.css +69 -0
  285. package/src/scrollbars.css +77 -2
  286. package/src/styles.css +14 -7735
  287. package/src/test-utils/workbenchMonacoMock.tsx +37 -0
  288. package/src/utils/dragMime.ts +89 -0
  289. package/src/utils/useWorkbenchMediaImage.ts +37 -0
  290. package/src/widget-asset/WidgetAssetEditor.tsx +9 -7
  291. package/src/widget-asset/WidgetAssetModeControls.tsx +1 -1
  292. package/src/widget-asset/WidgetAssetWorkbench.tsx +5 -6
  293. package/src/widget-studio/create-widget-studio-workspace-editor.tsx +1 -2
  294. package/src/widget-studio/index.ts +4 -1
  295. package/src/widget-studio/resolve-widget-studio-asset-catalog.ts +27 -10
  296. package/src/widget-tree/WidgetAssetPalette.tsx +35 -7
  297. package/src/widget-tree/WidgetInspectorPanel.tsx +53 -3
  298. package/src/widget-tree/WidgetSourceEditor.tsx +90 -3
  299. package/src/widget-tree/WidgetTreeCanvasPreview.tsx +875 -0
  300. package/src/widget-tree/WidgetTreeLab.tsx +411 -83
  301. package/src/widget-tree/WidgetTreeModeControls.tsx +30 -1
  302. package/src/widget-tree/WidgetTreeSidePanel.tsx +24 -34
  303. package/src/widget-tree/WidgetTreeView.tsx +711 -11
  304. package/src/widget-tree/WidgetTreeWorkbench.tsx +59 -15
  305. package/src/widget-tree/WidgetTreeWorkspaceShell.tsx +26 -8
  306. package/src/widget-tree/index.ts +22 -4
  307. package/src/widget-tree/widget-placement-asset-dnd.ts +35 -0
  308. package/src/widget-tree/widget-tree-document.ts +3 -9
  309. package/src/widget-tree/widget-tree-editor-state.ts +40 -0
  310. package/src/widget-tree/widget-tree-layout.ts +48 -3
  311. package/src/widget-tree/widget-tree-play-helpers.ts +69 -58
  312. package/src/workbench/ActivityBar.tsx +128 -21
  313. package/src/workbench/ArtifactShell.tsx +8 -8
  314. package/src/workbench/CommandGroupShell.tsx +131 -0
  315. package/src/workbench/CommandList.tsx +167 -0
  316. package/src/workbench/CommandPalette.tsx +194 -596
  317. package/src/workbench/ConfirmationFlow.tsx +3 -5
  318. package/src/workbench/MarkdownPreview.tsx +8 -12
  319. package/src/workbench/ShortcutCommandBridge.tsx +1 -4
  320. package/src/workbench/SplitView.tsx +385 -36
  321. package/src/workbench/StatusBar.tsx +4 -2
  322. package/src/workbench/StructuredArtifactEditor.tsx +3 -3
  323. package/src/workbench/Timeline.tsx +2 -7
  324. package/src/workbench/WorkbenchCanvasShell.defaults.ts +106 -0
  325. package/src/workbench/WorkbenchCanvasShell.model.ts +46 -0
  326. package/src/workbench/WorkbenchCanvasShell.tsx +32 -146
  327. package/src/workbench/WorkbenchDesktopTitleBar.tsx +126 -0
  328. package/src/workbench/WorkbenchEditorTabs.tsx +52 -0
  329. package/src/workbench/WorkbenchModalPortal.tsx +52 -0
  330. package/src/workbench/WorkbenchPlatformContext.tsx +31 -0
  331. package/src/workbench/WorkbenchShell.tsx +144 -26
  332. package/src/workbench/WorkbenchShellTitleBarLayoutControls.tsx +80 -0
  333. package/src/workbench/WorkbenchStandaloneShell.tsx +327 -41
  334. package/src/workbench/WorkbenchThemeProvider.tsx +142 -0
  335. package/src/workbench/WorkbenchViewEditor.tsx +75 -0
  336. package/src/workbench/WorkbenchViewSidebar.tsx +85 -0
  337. package/src/workbench/WorkbenchWindowChromeControls.tsx +290 -0
  338. package/src/workbench/activityBarOrder.ts +98 -0
  339. package/src/workbench/appearanceLabels.ts +28 -0
  340. package/src/workbench/auth/WorkbenchLoginView.tsx +4 -6
  341. package/src/workbench/auth/auth-flows.css +440 -0
  342. package/src/workbench/auth/auth.css +3 -0
  343. package/src/workbench/auth/profile-modals.css +115 -0
  344. package/src/workbench/bootstrap/WorkbenchBootstrapGate.tsx +45 -0
  345. package/src/workbench/bootstrap/WorkbenchBootstrapView.tsx +100 -0
  346. package/src/workbench/bootstrap/index.ts +12 -0
  347. package/src/workbench/bootstrap/useWorkbenchBootstrap.ts +130 -0
  348. package/src/workbench/chat/ChatCommandProposalCard.tsx +115 -0
  349. package/src/workbench/chat/ChatComposer.tsx +32 -34
  350. package/src/workbench/chat/ChatConversationBar.tsx +134 -0
  351. package/src/workbench/chat/ChatHistoryMenu.tsx +171 -0
  352. package/src/workbench/chat/ChatHistoryMenuRow.tsx +74 -0
  353. package/src/workbench/chat/ChatMessageCollapsible.tsx +115 -0
  354. package/src/workbench/chat/ChatMessageItem.tsx +293 -19
  355. package/src/workbench/chat/ChatMessageList.tsx +109 -24
  356. package/src/workbench/chat/ChatPanel.tsx +132 -19
  357. package/src/workbench/chat/ChatPhasedRunProgress.tsx +202 -0
  358. package/src/workbench/chat/ChatRenameDialog.tsx +77 -0
  359. package/src/workbench/chat/chat-conversation.css +273 -0
  360. package/src/workbench/chat/chat-ui.css +1124 -0
  361. package/src/workbench/chat/chat.css +3 -0
  362. package/src/workbench/chat/chatConversation.ts +46 -0
  363. package/src/workbench/chat/chatHistoryRowMenuItems.ts +29 -0
  364. package/src/workbench/chat/chatMessageMeta.tsx +252 -0
  365. package/src/workbench/chat/chatScroll.ts +22 -0
  366. package/src/workbench/chat/index.ts +47 -1
  367. package/src/workbench/chat/slashCommand.ts +5 -7
  368. package/src/workbench/chat/types.ts +35 -0
  369. package/src/workbench/chat/useCancelRuntimeOnUnmount.ts +60 -0
  370. package/src/workbench/chat/useChatPrependPagination.ts +233 -0
  371. package/src/workbench/chat/useChatRuntimeState.ts +3 -15
  372. package/src/workbench/chat/useSlashCommandSuggest.ts +76 -23
  373. package/src/workbench/chrome/index.css +6 -0
  374. package/src/workbench/chrome/workbench-desktop-titlebar.css +60 -0
  375. package/src/workbench/chrome/workbench-host.css +85 -0
  376. package/src/workbench/chrome/workbench-layout-regions.css +42 -0
  377. package/src/workbench/chrome/workbench-platform-chrome.css +142 -0
  378. package/src/workbench/chrome/workbench-shell-titlebar.css +124 -0
  379. package/src/workbench/command-execution-policy.ts +61 -0
  380. package/src/workbench/command-model.ts +275 -0
  381. package/src/workbench/command-palette/command-palette.css +156 -0
  382. package/src/workbench/commands.ts +62 -6
  383. package/src/workbench/editor-tabs/index.ts +22 -0
  384. package/src/workbench/editor-tabs-dnd.ts +162 -0
  385. package/src/workbench/editorTabContextMenu.ts +111 -0
  386. package/src/workbench/hardReset.ts +64 -0
  387. package/src/workbench/index.ts +234 -2
  388. package/src/workbench/jdw/jdw-editors.css +4 -0
  389. package/src/workbench/jdw/jdw.css +2 -0
  390. package/src/workbench/management/AccountManagementPanel.tsx +82 -0
  391. package/src/workbench/management/CommandInspectorPanel.tsx +127 -0
  392. package/src/workbench/management/CommandManagementPanel.tsx +129 -0
  393. package/src/workbench/management/CommandManagementSidebar.tsx +318 -0
  394. package/src/workbench/management/ExtensionManagementPanel.tsx +498 -0
  395. package/src/workbench/management/ExtensionManagementSidebar.tsx +492 -0
  396. package/src/workbench/management/IntegrationCommandActions.tsx +78 -0
  397. package/src/workbench/management/IntegrationSettingsSurface.tsx +290 -0
  398. package/src/workbench/management/IntegrationsShell.tsx +53 -0
  399. package/src/workbench/management/KeybindingCaptureField.tsx +142 -0
  400. package/src/workbench/management/KeybindingManagementPanel.tsx +120 -0
  401. package/src/workbench/management/LibraryCatalogPickerDialog.tsx +208 -0
  402. package/src/workbench/management/LibraryFacetFilterDialog.tsx +62 -0
  403. package/src/workbench/management/ManagementCard.tsx +89 -0
  404. package/src/workbench/management/ManagementFilterChips.tsx +41 -0
  405. package/src/workbench/management/ManagementGroup.tsx +56 -0
  406. package/src/workbench/management/ManagementPanelControls.tsx +43 -0
  407. package/src/workbench/management/ManagementPanelFrame.tsx +109 -0
  408. package/src/workbench/management/WorkbenchDialogFrame.tsx +148 -0
  409. package/src/workbench/management/WorkbenchNotice.tsx +211 -0
  410. package/src/workbench/management/build-command-management-groups.ts +157 -0
  411. package/src/workbench/management/command-inspector-uri.ts +26 -0
  412. package/src/workbench/management/extension-category-display.ts +42 -0
  413. package/src/workbench/management/format-command-run-state.ts +19 -0
  414. package/src/workbench/management/index.ts +130 -0
  415. package/src/workbench/management/integration-command-action.ts +25 -0
  416. package/src/workbench/management/library-catalog-picker-dialog.css +56 -0
  417. package/src/workbench/management/library-facet-filter-dialog.css +8 -0
  418. package/src/workbench/management/management-panel.css +444 -0
  419. package/src/workbench/management/management.css +4 -0
  420. package/src/workbench/management/types.ts +126 -0
  421. package/src/workbench/markdownRemarkPlugins.ts +4 -0
  422. package/src/workbench/schema/index.ts +0 -1
  423. package/src/workbench/settings/NavigationPanel.tsx +41 -2
  424. package/src/workbench/settings/SchemaForm.tsx +20 -7
  425. package/src/workbench/settings/SectionTabPanel.tsx +172 -0
  426. package/src/workbench/settings/SectionedPanel.tsx +40 -286
  427. package/src/workbench/settings/StructuredDataForm.tsx +106 -26
  428. package/src/workbench/settings/StructuredDataSchemaPanel.tsx +345 -93
  429. package/src/workbench/settings/StructuredDataTableView.tsx +2 -2
  430. package/src/workbench/settings/WorkbenchPanelRegion.tsx +1 -1
  431. package/src/workbench/settings/WorkbenchSettingsModal.tsx +116 -62
  432. package/src/workbench/settings/WorkbenchSettingsNav.tsx +3 -3
  433. package/src/workbench/settings/extensionSettingsForm.ts +34 -0
  434. package/src/workbench/settings/index.ts +36 -1
  435. package/src/workbench/settings/navigation-panel.css +59 -0
  436. package/src/workbench/settings/panel-region.css +3 -2
  437. package/src/workbench/settings/schema-form.css +36 -0
  438. package/src/workbench/settings/schemaFormSettingSpec.ts +177 -0
  439. package/src/workbench/settings/schemaFormSettingsCategory.tsx +21 -0
  440. package/src/workbench/settings/section-tab-panel.css +157 -0
  441. package/src/workbench/settings/sectionScrollSpyDom.ts +139 -0
  442. package/src/workbench/settings/sectioned-panel.css +162 -0
  443. package/src/workbench/settings/sectionedPanelScrollSpy.ts +158 -28
  444. package/src/workbench/settings/settings-responsive.css +58 -0
  445. package/src/workbench/settings/settings.css +11 -0
  446. package/src/workbench/settings/settingsCommit.tsx +55 -0
  447. package/src/workbench/settings/structured-data-form.css +114 -0
  448. package/src/workbench/settings/structured-data-schema-panel.css +33 -19
  449. package/src/workbench/settings/structuredDataFormModel.ts +140 -0
  450. package/src/workbench/settings/structuredDataSchema.ts +111 -658
  451. package/src/workbench/settings/structuredDataSchemaDate.ts +197 -0
  452. package/src/workbench/settings/structuredDataSchemaField.ts +105 -0
  453. package/src/workbench/settings/structuredDataSchemaSection.ts +74 -0
  454. package/src/workbench/settings/structuredDataSchemaTable.ts +120 -0
  455. package/src/workbench/settings/structuredDataSchemaTypes.ts +308 -0
  456. package/src/workbench/settings/structuredDataSchemaValidation.ts +166 -0
  457. package/src/workbench/settings/useSectionScrollSpy.ts +420 -0
  458. package/src/workbench/settings/workbench-settings-modal.css +273 -0
  459. package/src/workbench/shell/activity-bar.css +245 -0
  460. package/src/workbench/shell/confirmation-flow.css +87 -0
  461. package/src/workbench/shell/index.css +5 -0
  462. package/src/workbench/shell/split-view.css +188 -0
  463. package/src/workbench/shell/status-bar.css +141 -0
  464. package/src/workbench/shell.ts +133 -2
  465. package/src/workbench/shellPresets.ts +36 -0
  466. package/src/workbench/shellState.ts +16 -23
  467. package/src/workbench/shellViewModel.ts +160 -0
  468. package/src/workbench/sidebarViewPlacementDnd.ts +61 -0
  469. package/src/workbench/standalone.ts +9 -9
  470. package/src/workbench/status.ts +10 -6
  471. package/src/workbench/theme.ts +8 -2
  472. package/src/workbench/themePresets.ts +191 -0
  473. package/src/workbench/useDeferredWorkbenchMount.ts +71 -0
  474. package/src/workbench/useEditorTabsStripDnd.ts +262 -0
  475. package/src/workbench/useWorkbenchAppearanceDocumentSync.tsx +17 -0
  476. package/src/workbench/useWorkbenchEditorTabContextMenu.tsx +66 -0
  477. package/src/workbench/useWorkbenchSidebarActionBarDnd.ts +227 -0
  478. package/src/workbench/useWorkbenchSidebarLayout.tsx +232 -0
  479. package/src/workbench/useWorkbenchSidebarViewPlacementDropZone.ts +194 -0
  480. package/src/workbench/useWorkbenchStandaloneShellStateSync.tsx +112 -0
  481. package/src/workbench/views/action-list.css +144 -0
  482. package/src/workbench/views/artifact-preview.css +184 -0
  483. package/src/workbench/views/command-inspector.css +98 -0
  484. package/src/workbench/views/command-list.css +356 -0
  485. package/src/workbench/views/commands-sidebar.css +51 -0
  486. package/src/workbench/views/extensions-sidebar.css +175 -0
  487. package/src/workbench/views/multi-provider-explorer.css +182 -0
  488. package/src/workbench/views/search.css +10 -0
  489. package/src/workbench/views/timeline.css +239 -0
  490. package/src/workbench/views/view-hosts.css +37 -0
  491. package/src/workbench/views/view-sidebar.css +21 -0
  492. package/src/workbench/views/views.css +12 -0
  493. package/src/workbench/widgets/widget-tree.css +598 -0
  494. package/src/workbench/widgets/widgets.css +2 -0
  495. package/src/workbench/workbench-chrome.css +4 -0
  496. package/src/workbench/workbenchContextMenu.ts +18 -0
  497. package/src/workbench/workbenchModalViewState.ts +52 -0
  498. package/src/workbench/workbenchOverlaysContext.tsx +21 -0
  499. package/src/workbench/workbenchPlatformChrome.ts +40 -0
  500. package/src/workbench/workbenchSidebarLayoutContext.ts +46 -0
  501. package/src/workbench/workbenchStandaloneShellReactContext.tsx +80 -0
  502. package/src/workbench/workbenchViewRouteState.ts +151 -0
  503. package/src/workbench/workspace/WorkspaceDraftsContext.tsx +59 -71
  504. package/src/workbench/workspace/WorkspaceEditor.tsx +27 -123
  505. package/src/workbench/workspace/WorkspaceEditorPanel.tsx +67 -8
  506. package/src/workbench/workspace/WorkspaceExplorer.tsx +149 -51
  507. package/src/workbench/workspace/WorkspaceExplorerPanel.tsx +73 -0
  508. package/src/workbench/workspace/WorkspaceFileIcon.tsx +4 -160
  509. package/src/workbench/workspace/WorkspacePathLabel.tsx +42 -0
  510. package/src/workbench/workspace/WorkspaceSearchPanel.tsx +18 -15
  511. package/src/workbench/workspace/WorkspaceSearchResults.tsx +6 -2
  512. package/src/workbench/workspace/build-workspace-explorer-nodes.ts +44 -0
  513. package/src/workbench/workspace/createVirtualWorkspaceExplorerPort.ts +141 -0
  514. package/src/workbench/workspace/explorer-tree-style.ts +18 -0
  515. package/src/workbench/workspace/index.ts +45 -32
  516. package/src/workbench/workspace/json-config-workbench.css +57 -0
  517. package/src/workbench/workspace/monaco-panel.css +71 -0
  518. package/src/workbench/workspace/path.ts +2 -0
  519. package/src/workbench/workspace/useWorkspaceExplorerController.ts +490 -0
  520. package/src/workbench/workspace/workspace-editor.css +235 -0
  521. package/src/workbench/workspace/workspace-explorer-panel.css +33 -0
  522. package/src/workbench/workspace/workspace.css +5 -0
  523. package/src/workbench/workspace/workspaceExplorerController.ts +193 -0
  524. package/src/workbench/workspace/workspaceJsonDiagnostics.ts +2 -2
  525. package/src/workbench-menu-surfaces.css +57 -0
  526. package/src/jdw/builtins/renderBuiltinWidgetNode.tsx +0 -126
  527. package/src/layout/SideBarViewFrame.tsx +0 -367
  528. package/src/primitives/Checkbox.tsx +0 -21
  529. package/src/primitives/NumberInput.tsx +0 -38
  530. package/src/primitives/ScrollArea.tsx +0 -42
  531. package/src/primitives/Select.tsx +0 -1
  532. package/src/primitives/StatusBar.tsx +0 -46
  533. package/src/primitives/WorkbenchChrome.tsx +0 -424
  534. package/src/workbench/workspace/MultiProviderExplorer.tsx +0 -625
@@ -8,9 +8,10 @@ import { isSaveSuccess, type SaveResult } from '@workbench-kit/contracts';
8
8
  import { useWorkspaceDrafts } from './WorkspaceDraftsContext';
9
9
  import { ConfirmDialog } from '../../modal/ConfirmDialog';
10
10
  import { ContextMenu, type ContextMenuItem } from '../../overlay/ContextMenu';
11
- import { EmptyState } from '../../primitives/EmptyState';
12
- import { IconButton } from '../../primitives/IconButton';
13
- import { Panel, PanelBody } from '../../layout/Panel';
11
+ import { Button } from '../../primitives/button';
12
+ import { EmptyState } from '../../primitives/empty-state';
13
+ import { IconButton } from '../../primitives/icon-button';
14
+ import { Panel, PanelBody } from '../../layout/panel';
14
15
  import {
15
16
  WORKBENCH_COMMAND_SURFACE_EDITOR,
16
17
  commandMenuItemsToContextMenuItems,
@@ -56,7 +57,29 @@ export type WorkspaceEditorPanelRenderTabActions = (
56
57
  context: WorkspaceEditorPanelRenderTabActionsContext,
57
58
  ) => ReactNode;
58
59
 
60
+ export type WorkspaceEditorPanelCanSaveFile = (
61
+ path: string,
62
+ content: string,
63
+ file: WorkspaceFile,
64
+ ) => boolean;
65
+
66
+ /**
67
+ * Panel-level Ctrl/Cmd+S behavior.
68
+ * - `panel` (default): panel handles the shortcut
69
+ * - `editor`: when `renderEditor` is set, panel does not bind Ctrl/Cmd+S (host owns save)
70
+ * - `off`: never bind panel Ctrl/Cmd+S
71
+ */
72
+ export type WorkspaceEditorPanelSaveShortcutMode = 'panel' | 'editor' | 'off';
73
+
74
+ export interface WorkspaceEditorPanelSaveShortcutContext {
75
+ content: string;
76
+ path: string;
77
+ /** Persist via the panel draft save path (respects `canSaveFile`). */
78
+ save: () => void;
79
+ }
80
+
59
81
  export interface WorkspaceEditorPanelProps {
82
+ canSaveFile?: WorkspaceEditorPanelCanSaveFile | undefined;
60
83
  emptyLabel?: string;
61
84
  files: WorkspaceFile[];
62
85
  onCloseAll?: () => void;
@@ -69,15 +92,22 @@ export interface WorkspaceEditorPanelProps {
69
92
  content: string,
70
93
  previousUpdatedAt?: string,
71
94
  ) => SaveResult | Promise<SaveResult | undefined> | undefined;
95
+ /**
96
+ * Called instead of the default panel save when Ctrl/Cmd+S fires and the panel
97
+ * still owns the binding. Call `save()` to persist, or skip to take ownership.
98
+ */
99
+ onSaveShortcut?: ((context: WorkspaceEditorPanelSaveShortcutContext) => void) | undefined;
72
100
  onSelectedPathChange: (path: string) => void;
73
101
  openPaths: string[];
74
102
  renderEditor?: WorkspaceEditorPanelRenderEditor | undefined;
75
103
  renderTabActions?: WorkspaceEditorPanelRenderTabActions | undefined;
104
+ saveShortcutMode?: WorkspaceEditorPanelSaveShortcutMode | undefined;
76
105
  selectedPath?: string;
77
106
  theme?: WorkspaceEditorTheme;
78
107
  }
79
108
 
80
109
  export function WorkspaceEditorPanel({
110
+ canSaveFile,
81
111
  emptyLabel = 'Open a file from Explorer or Search.',
82
112
  files,
83
113
  onCloseAll,
@@ -86,10 +116,12 @@ export function WorkspaceEditorPanel({
86
116
  onCopyPath,
87
117
  onDeletePath,
88
118
  onSaveFile,
119
+ onSaveShortcut,
89
120
  onSelectedPathChange,
90
121
  openPaths,
91
122
  renderEditor,
92
123
  renderTabActions,
124
+ saveShortcutMode = 'panel',
93
125
  selectedPath,
94
126
  theme,
95
127
  }: WorkspaceEditorPanelProps) {
@@ -118,7 +150,10 @@ export function WorkspaceEditorPanel({
118
150
  };
119
151
 
120
152
  const saveFile = (path: string, content: string) => {
121
- Promise.resolve(onSaveFile?.(path, content, filesByPath.get(path)?.updatedAt))
153
+ const file = filesByPath.get(path);
154
+ if (!file || canSaveFile?.(path, content, file) === false) return;
155
+
156
+ Promise.resolve(onSaveFile?.(path, content, file.updatedAt))
122
157
  .then((result) => {
123
158
  if (!result || isSaveSuccess(result)) {
124
159
  saveDraftContext(path, content);
@@ -144,6 +179,7 @@ export function WorkspaceEditorPanel({
144
179
  ): WorkbenchEditorCommandContext => {
145
180
  const filePath = file?.path;
146
181
  const content = file ? getDraft(file.path, file.content) : '';
182
+ const saveAllowed = filePath && file ? canSaveFile?.(filePath, content, file) !== false : false;
147
183
 
148
184
  return {
149
185
  canCloseAll: Boolean(onCloseAll),
@@ -152,7 +188,10 @@ export function WorkspaceEditorPanel({
152
188
  canCopyPath: Boolean(onCopyPath),
153
189
  canDeletePath: Boolean(onDeletePath),
154
190
  canDiscardFile: Boolean(filePath),
155
- canSaveFile: Boolean(filePath),
191
+ canSaveFile: saveAllowed,
192
+ canSplitDown: false,
193
+ canSplitRight: false,
194
+ canTogglePinned: false,
156
195
  closeAll: () => onCloseAll?.(),
157
196
  closeOthers: () => {
158
197
  if (filePath) onCloseOthers?.(filePath);
@@ -173,9 +212,13 @@ export function WorkspaceEditorPanel({
173
212
  hasMultipleOpenFiles: openFiles.length > 1,
174
213
  hasOpenFiles: openFiles.length > 0,
175
214
  hasUnsavedChanges: Boolean(file && isDirty(file.path, file.content)),
215
+ isPinned: false,
176
216
  saveFile: () => {
177
217
  if (filePath) saveFile(filePath, content);
178
218
  },
219
+ splitDown: () => undefined,
220
+ splitRight: () => undefined,
221
+ togglePinned: () => undefined,
179
222
  };
180
223
  };
181
224
 
@@ -212,7 +255,24 @@ export function WorkspaceEditorPanel({
212
255
  if (!selectedFile) return;
213
256
  if (event.key.toLowerCase() !== 's' || (!event.ctrlKey && !event.metaKey)) return;
214
257
 
258
+ const effectiveMode =
259
+ saveShortcutMode === 'editor' && renderEditor ? 'off' : saveShortcutMode;
260
+ if (effectiveMode === 'off') {
261
+ return;
262
+ }
263
+
215
264
  event.preventDefault();
265
+ if (onSaveShortcut) {
266
+ onSaveShortcut({
267
+ content: selectedContent,
268
+ path: selectedFile.path,
269
+ save: () => {
270
+ saveFile(selectedFile.path, selectedContent);
271
+ },
272
+ });
273
+ return;
274
+ }
275
+
216
276
  saveFile(selectedFile.path, selectedContent);
217
277
  }}
218
278
  >
@@ -234,11 +294,10 @@ export function WorkspaceEditorPanel({
234
294
  title={file.path}
235
295
  onContextMenu={(event) => handleTabContextMenu(event, file.path)}
236
296
  >
237
- <button
297
+ <Button
238
298
  aria-selected={isActive}
239
299
  className="workspace-editor__tab-button"
240
300
  role="tab"
241
- type="button"
242
301
  onClick={() => onSelectedPathChange(file.path)}
243
302
  >
244
303
  <WorkspaceFileIcon mimeType={file.mimeType} path={file.path} />
@@ -246,7 +305,7 @@ export function WorkspaceEditorPanel({
246
305
  {isDirtyValue ? (
247
306
  <span className="workspace-editor__dirty" aria-label="Unsaved changes" />
248
307
  ) : null}
249
- </button>
308
+ </Button>
250
309
  <IconButton
251
310
  className="workspace-editor__tab-close"
252
311
  icon="codicon-close"
@@ -10,6 +10,7 @@ import {
10
10
  type ReactNode,
11
11
  } from 'react';
12
12
  import {
13
+ createEmptyWorkspaceSelection,
13
14
  getWorkspaceSelectionActionPaths,
14
15
  normalizeWorkspaceSelectionPaths,
15
16
  parentPathOf,
@@ -17,8 +18,10 @@ import {
17
18
  type WorkspaceSelectionMode,
18
19
  type WorkspaceSelectionState,
19
20
  } from '@workbench-kit/workspace';
20
- import { SideBarList, SideBarListItem, sideBarTreeDepthStyle } from '../../layout/SideBarViewFrame';
21
+ import { SideBarList, SideBarListItem, useSidebarSectionBaseDepth } from '../../layout/sidebar';
22
+ import { TextInput } from '../../primitives/text-input';
21
23
  import { cxCodicon } from '../../utils/codicon';
24
+ import { explorerTreeDepthStyle } from './explorer-tree-style';
22
25
  import { flattenWorkspaceTree } from './tree';
23
26
  import { WorkspaceFileIcon } from './WorkspaceFileIcon';
24
27
  import type { WorkspaceTreeNode } from './types';
@@ -26,11 +29,14 @@ import type { WorkspaceTreeNode } from './types';
26
29
  export const WORKSPACE_EXPLORER_DRAG_DATA_TYPE = 'application/x-newchobo-ui-workspace-paths';
27
30
  export const WORKSPACE_EXPLORER_DRAG_METADATA_DATA_TYPE = `${WORKSPACE_EXPLORER_DRAG_DATA_TYPE}.metadata`;
28
31
 
32
+ export type WorkspaceExplorerSelectionChangeReason =
33
+ 'clear' | 'click' | 'context-menu' | 'drag-start' | 'folder-click';
34
+
29
35
  export interface WorkspaceExplorerSelectionChangeMeta {
30
- event: DragEvent<HTMLButtonElement> | MouseEvent<HTMLButtonElement>;
31
- mode: WorkspaceSelectionMode;
32
- node: WorkspaceTreeNode;
33
- reason: 'click' | 'context-menu' | 'drag-start';
36
+ event?: DragEvent<HTMLButtonElement> | MouseEvent<HTMLButtonElement>;
37
+ mode?: WorkspaceSelectionMode;
38
+ node?: WorkspaceTreeNode;
39
+ reason: WorkspaceExplorerSelectionChangeReason;
34
40
  }
35
41
 
36
42
  export interface WorkspaceExplorerItemActionMeta {
@@ -67,10 +73,7 @@ export interface WorkspaceExplorerMoveRequestMeta {
67
73
  }
68
74
 
69
75
  export type WorkspaceExplorerInlineEditKind =
70
- | 'create-file'
71
- | 'create-folder'
72
- | 'rename-file'
73
- | 'rename-folder';
76
+ 'create-file' | 'create-folder' | 'rename-file' | 'rename-folder';
74
77
 
75
78
  export interface WorkspaceExplorerInlineEditState {
76
79
  error?: ReactNode;
@@ -99,6 +102,7 @@ export interface WorkspaceExplorerProps {
99
102
  onInlineEditCancel?: (edit: WorkspaceExplorerInlineEditState) => void;
100
103
  onInlineEditCommit?: (meta: WorkspaceExplorerInlineEditCommitMeta) => void;
101
104
  onInlineEditValueChange?: (value: string, edit: WorkspaceExplorerInlineEditState) => void;
105
+ onBackgroundContextMenu?: (event: MouseEvent<HTMLUListElement>) => void;
102
106
  onItemContextMenu?: (
103
107
  event: MouseEvent<HTMLButtonElement>,
104
108
  node: WorkspaceTreeNode,
@@ -112,6 +116,8 @@ export interface WorkspaceExplorerProps {
112
116
  meta: WorkspaceExplorerSelectionChangeMeta,
113
117
  ) => void;
114
118
  onToggleFolder: (path: string) => void;
119
+ focusedPath?: string | undefined;
120
+ renderItemActions?: (node: WorkspaceTreeNode, meta: WorkspaceExplorerItemActionMeta) => ReactNode;
115
121
  selectedPaths?: Iterable<string>;
116
122
  selectionAnchorPath?: string;
117
123
  }
@@ -123,9 +129,11 @@ export function WorkspaceExplorer({
123
129
  dragMetadataFactory,
124
130
  expandedPaths,
125
131
  filterQuery = '',
132
+ focusedPath,
126
133
  inlineEdit,
127
134
  nodes,
128
135
  onActivateFile,
136
+ onBackgroundContextMenu,
129
137
  onInlineEditCancel,
130
138
  onInlineEditCommit,
131
139
  onInlineEditValueChange,
@@ -135,11 +143,14 @@ export function WorkspaceExplorer({
135
143
  onRequestRename,
136
144
  onSelectionChange,
137
145
  onToggleFolder,
146
+ renderItemActions,
138
147
  selectedPaths = [],
139
148
  selectionAnchorPath,
140
149
  }: WorkspaceExplorerProps) {
150
+ const sectionBaseDepth = useSidebarSectionBaseDepth();
141
151
  const draggedPathsRef = useRef<string[]>([]);
142
152
  const inlineEditInputRef = useRef<HTMLInputElement>(null);
153
+ const inlineEditCommitStartedRef = useRef(false);
143
154
  const [dropTargetPath, setDropTargetPath] = useState<string | null>(null);
144
155
  const visibleNodes = useMemo(
145
156
  () => flattenWorkspaceTree({ expandedPaths, filterQuery, nodes }),
@@ -170,6 +181,7 @@ export function WorkspaceExplorer({
170
181
  : undefined;
171
182
 
172
183
  useEffect(() => {
184
+ inlineEditCommitStartedRef.current = false;
173
185
  const input = inlineEditInputRef.current;
174
186
  if (!input) return;
175
187
 
@@ -177,19 +189,29 @@ export function WorkspaceExplorer({
177
189
  input.select();
178
190
  }, [inlineEditKey]);
179
191
 
192
+ useEffect(() => {
193
+ // Validation failures keep the same draft id; allow Enter/blur retry after an error.
194
+ if (inlineEdit?.error) {
195
+ inlineEditCommitStartedRef.current = false;
196
+ }
197
+ }, [inlineEdit?.error]);
198
+
180
199
  const selectFile = (event: MouseEvent<HTMLButtonElement>, node: WorkspaceTreeNode) => {
181
200
  const mode = resolveSelectionMode(event);
201
+ const nextSelection = onSelectionChange
202
+ ? {
203
+ ...updateWorkspaceSelection({
204
+ mode,
205
+ orderedPaths: visibleFilePaths,
206
+ selection: currentSelection,
207
+ targetPath: node.path,
208
+ }),
209
+ focusedPath: node.path,
210
+ }
211
+ : currentSelection;
182
212
 
183
213
  if (onSelectionChange) {
184
- onSelectionChange(
185
- updateWorkspaceSelection({
186
- mode,
187
- orderedPaths: visibleFilePaths,
188
- selection: currentSelection,
189
- targetPath: node.path,
190
- }),
191
- { event, mode, node, reason: 'click' },
192
- );
214
+ onSelectionChange(nextSelection, { event, mode, node, reason: 'click' });
193
215
  }
194
216
 
195
217
  if (mode === 'single' || !onSelectionChange) {
@@ -197,15 +219,48 @@ export function WorkspaceExplorer({
197
219
  }
198
220
  };
199
221
 
222
+ const selectFolder = (event: MouseEvent<HTMLButtonElement>, node: WorkspaceTreeNode) => {
223
+ onSelectionChange?.(
224
+ {
225
+ anchorPath: undefined,
226
+ focusedPath: node.path,
227
+ paths: [],
228
+ },
229
+ { event, mode: 'single', node, reason: 'folder-click' },
230
+ );
231
+ onToggleFolder(node.path);
232
+ };
233
+
234
+ const clearSelection = () => {
235
+ onSelectionChange?.(createEmptyWorkspaceSelection(), { reason: 'clear' });
236
+ };
237
+
200
238
  const getItemActionMeta = (node: WorkspaceTreeNode): WorkspaceExplorerItemActionMeta => {
201
- const selected = node.type === 'file' && selectedPathSet.has(node.path);
202
- const selection =
203
- node.type === 'file' && !selected
204
- ? {
205
- anchorPath: node.path,
206
- paths: [node.path],
207
- }
208
- : currentSelection;
239
+ if (node.type === 'folder') {
240
+ const selection = {
241
+ anchorPath: undefined,
242
+ focusedPath: node.path,
243
+ paths: [] as string[],
244
+ };
245
+
246
+ return {
247
+ actionPaths: [node.path],
248
+ selected: focusedPath === node.path,
249
+ selection,
250
+ };
251
+ }
252
+
253
+ const selected = selectedPathSet.has(node.path);
254
+ const selection = !selected
255
+ ? {
256
+ anchorPath: node.path,
257
+ focusedPath: node.path,
258
+ paths: [node.path],
259
+ }
260
+ : {
261
+ ...currentSelection,
262
+ focusedPath: node.path,
263
+ };
209
264
 
210
265
  return {
211
266
  actionPaths: getWorkspaceSelectionActionPaths({
@@ -218,15 +273,39 @@ export function WorkspaceExplorer({
218
273
  };
219
274
 
220
275
  const handleItemContextMenu = (event: MouseEvent<HTMLButtonElement>, node: WorkspaceTreeNode) => {
276
+ // Keep item menus from bubbling to the list background handler.
277
+ event.stopPropagation();
221
278
  const meta = getItemActionMeta(node);
222
279
 
223
280
  if (node.type === 'file' && !meta.selected) {
224
- onSelectionChange?.(meta.selection, {
225
- event,
226
- mode: 'single',
227
- node,
228
- reason: 'context-menu',
229
- });
281
+ onSelectionChange?.(
282
+ {
283
+ ...meta.selection,
284
+ focusedPath: node.path,
285
+ },
286
+ {
287
+ event,
288
+ mode: 'single',
289
+ node,
290
+ reason: 'context-menu',
291
+ },
292
+ );
293
+ }
294
+
295
+ if (node.type === 'folder') {
296
+ onSelectionChange?.(
297
+ {
298
+ anchorPath: undefined,
299
+ focusedPath: node.path,
300
+ paths: [],
301
+ },
302
+ {
303
+ event,
304
+ mode: 'single',
305
+ node,
306
+ reason: 'context-menu',
307
+ },
308
+ );
230
309
  }
231
310
 
232
311
  onItemContextMenu?.(event, node, meta);
@@ -238,7 +317,7 @@ export function WorkspaceExplorer({
238
317
  const meta = getItemActionMeta(node);
239
318
 
240
319
  if (event.key === 'Delete') {
241
- if (node.type === 'file' && meta.actionPaths.length === 0) return;
320
+ if (meta.actionPaths.length === 0) return;
242
321
 
243
322
  event.preventDefault();
244
323
  onRequestDelete?.({ ...meta, event, node });
@@ -355,8 +434,10 @@ export function WorkspaceExplorer({
355
434
  };
356
435
 
357
436
  const commitInlineEdit = () => {
358
- if (!inlineEdit) return;
437
+ if (!inlineEdit || inlineEditCommitStartedRef.current) return;
359
438
 
439
+ // Enter commits then blurs the same input; commit only once per draft.
440
+ inlineEditCommitStartedRef.current = true;
360
441
  onInlineEditCommit?.({
361
442
  edit: inlineEdit,
362
443
  value: inlineEdit.value.trim(),
@@ -393,27 +474,28 @@ export function WorkspaceExplorer({
393
474
  }) => {
394
475
  if (!inlineEdit) return null;
395
476
 
396
- const depthStyle = sideBarTreeDepthStyle(depth);
477
+ const depthStyle = explorerTreeDepthStyle(sectionBaseDepth + depth);
397
478
 
398
479
  return (
399
- <li key={key} className="ui-side-bar-list-entry">
400
- <div className="ui-side-bar-inline-edit" style={depthStyle}>
480
+ <li key={key} className="ui-sidebar-list-entry">
481
+ <div className="ui-sidebar-inline-edit" style={depthStyle}>
401
482
  <span className="workbench-tree-prefix">
402
483
  <span className="workbench-tree-spacer" />
403
484
  <WorkspaceFileIcon directory={directory} mimeType={mimeType} path={path} />
404
485
  </span>
405
- <input
486
+ <TextInput
406
487
  ref={inlineEditInputRef}
407
488
  aria-label="Workspace item name"
408
- className="ui-side-bar-inline-edit__input"
489
+ className="ui-sidebar-inline-edit__input"
490
+ controlWidth="full"
409
491
  value={inlineEdit.value}
410
492
  onBlur={commitInlineEdit}
411
- onChange={(event) => onInlineEditValueChange?.(event.currentTarget.value, inlineEdit)}
493
+ onValueChange={(value) => onInlineEditValueChange?.(value, inlineEdit)}
412
494
  onKeyDown={handleInlineEditKeyDown}
413
495
  />
414
496
  </div>
415
497
  {inlineEdit.error ? (
416
- <div className="ui-side-bar-inline-edit__error" style={depthStyle}>
498
+ <div className="ui-sidebar-inline-edit__error" style={depthStyle}>
417
499
  {inlineEdit.error}
418
500
  </div>
419
501
  ) : null}
@@ -426,22 +508,23 @@ export function WorkspaceExplorer({
426
508
  fill
427
509
  aria-label="Workspace files"
428
510
  dropTarget={dropTargetPath === ''}
511
+ onContextMenu={(event) => {
512
+ onBackgroundContextMenu?.(event);
513
+ }}
429
514
  onDragLeave={(event) => handleDropTargetDragLeave(event, '')}
430
515
  onDragOver={(event) => handleDropTargetDragOver(event, '')}
431
516
  onDrop={(event) => handleDrop(event, '')}
517
+ onMouseDown={(event) => {
518
+ if (event.target === event.currentTarget) {
519
+ clearSelection();
520
+ }
521
+ }}
432
522
  >
433
- {inlineEdit?.kind.startsWith('create') && !inlineEdit.parentPath
434
- ? renderInlineEdit({
435
- depth: 0,
436
- directory: inlineEdit.kind === 'create-folder',
437
- key: `inline-edit:${inlineEditKey}`,
438
- path: inlineEdit.value,
439
- })
440
- : null}
441
523
  {visibleNodes.map(({ depth, node }) => {
442
524
  const isFolder = node.type === 'folder';
443
525
  const expanded = expandedPaths.has(node.path) || Boolean(filterQuery.trim());
444
- const selected = node.type === 'file' && selectedPathSet.has(node.path);
526
+ const isFocused = focusedPath === node.path;
527
+ const selected = isFocused || (node.type === 'file' && selectedPathSet.has(node.path));
445
528
  const dropTarget = isFolder && dropTargetPath === node.path;
446
529
  const editingRename =
447
530
  inlineEdit?.kind.startsWith('rename') && inlineEdit.path === node.path;
@@ -458,6 +541,13 @@ export function WorkspaceExplorer({
458
541
  <Fragment key={node.path}>
459
542
  <SideBarListItem
460
543
  active={activePath === node.path}
544
+ after={
545
+ renderItemActions ? (
546
+ <span className="ui-workspace-explorer-item-actions">
547
+ {renderItemActions(node, getItemActionMeta(node))}
548
+ </span>
549
+ ) : undefined
550
+ }
461
551
  data-workspace-path={node.path}
462
552
  depth={depth}
463
553
  draggable={Boolean(onRequestMove)}
@@ -465,7 +555,7 @@ export function WorkspaceExplorer({
465
555
  selected={selected}
466
556
  onClick={(event) => {
467
557
  if (isFolder) {
468
- onToggleFolder(node.path);
558
+ selectFolder(event, node);
469
559
  return;
470
560
  }
471
561
  selectFile(event, node);
@@ -516,6 +606,14 @@ export function WorkspaceExplorer({
516
606
  </Fragment>
517
607
  );
518
608
  })}
609
+ {inlineEdit?.kind.startsWith('create') && !inlineEdit.parentPath
610
+ ? renderInlineEdit({
611
+ depth: 0,
612
+ directory: inlineEdit.kind === 'create-folder',
613
+ key: `inline-edit:${inlineEditKey}`,
614
+ path: inlineEdit.value,
615
+ })
616
+ : null}
519
617
  {visibleNodes.length === 0 ? <SideBarListItem disabled>No files</SideBarListItem> : null}
520
618
  </SideBarList>
521
619
  );
@@ -0,0 +1,73 @@
1
+ import type { ReactNode } from 'react';
2
+ import { SideBarViewFrame, SidebarToolbar, WorkbenchSidebarSection } from '../../layout/sidebar';
3
+ import { IconButton } from '../../primitives/icon-button';
4
+ import { cx } from '../../utils/cx';
5
+ import { WorkspaceExplorer, type WorkspaceExplorerProps } from './WorkspaceExplorer';
6
+
7
+ export interface WorkspaceExplorerPanelProps extends WorkspaceExplorerProps {
8
+ 'aria-label'?: string | undefined;
9
+ className?: string | undefined;
10
+ onNewFile?: (() => void) | undefined;
11
+ onNewFolder?: (() => void) | undefined;
12
+ onRefresh?: (() => void) | undefined;
13
+ refreshLabel?: string;
14
+ sectionTitle?: string;
15
+ title?: string;
16
+ toolbarLeading?: ReactNode;
17
+ toolbarStatus?: ReactNode;
18
+ toolbarTrailing?: ReactNode;
19
+ }
20
+
21
+ export function WorkspaceExplorerPanel({
22
+ 'aria-label': ariaLabel,
23
+ className,
24
+ onNewFile,
25
+ onNewFolder,
26
+ onRefresh,
27
+ refreshLabel = 'Refresh Explorer',
28
+ sectionTitle = 'Workspace',
29
+ title = 'Explorer',
30
+ toolbarLeading,
31
+ toolbarStatus,
32
+ toolbarTrailing,
33
+ ...explorerProps
34
+ }: WorkspaceExplorerPanelProps) {
35
+ const hasDefaultActions = Boolean(onNewFile || onNewFolder || onRefresh);
36
+ const hasToolbarSlots = Boolean(toolbarLeading || toolbarTrailing || toolbarStatus);
37
+ const headerActions =
38
+ hasDefaultActions || hasToolbarSlots ? (
39
+ <SidebarToolbar
40
+ aria-label="Explorer actions"
41
+ className="ui-explorer-action-bar"
42
+ role="toolbar"
43
+ >
44
+ {toolbarLeading}
45
+ {onNewFile ? (
46
+ <IconButton compact icon="codicon-new-file" label="New file" onClick={onNewFile} />
47
+ ) : null}
48
+ {onNewFolder ? (
49
+ <IconButton compact icon="codicon-new-folder" label="New folder" onClick={onNewFolder} />
50
+ ) : null}
51
+ {onRefresh ? (
52
+ <IconButton compact icon="codicon-refresh" label={refreshLabel} onClick={onRefresh} />
53
+ ) : null}
54
+ {toolbarTrailing}
55
+ {toolbarStatus ? (
56
+ <span className="ui-explorer-action-bar__status">{toolbarStatus}</span>
57
+ ) : null}
58
+ </SidebarToolbar>
59
+ ) : undefined;
60
+
61
+ return (
62
+ <SideBarViewFrame
63
+ actions={headerActions}
64
+ aria-label={ariaLabel}
65
+ className={cx('workbench-explorer-view', 'ui-workspace-explorer-panel', className)}
66
+ title={title}
67
+ >
68
+ <WorkbenchSidebarSection id="workspace-explorer-section" title={sectionTitle}>
69
+ <WorkspaceExplorer {...explorerProps} />
70
+ </WorkbenchSidebarSection>
71
+ </SideBarViewFrame>
72
+ );
73
+ }