@workbench-kit/shell-react 0.0.2-prototype.0.2.24 → 0.0.2-prototype.0.2.25

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.
package/README.md CHANGED
@@ -39,10 +39,15 @@ available from the non-React `@workbench-kit/shell-react/layout-storage` subpath
39
39
  host wants the Kit editor, or omit it for a product-owned editor region.
40
40
 
41
41
  Performance-sensitive hosts should import orchestration from the focused
42
- `provider`, `shell`, `command-host`, `command-palette`, and
42
+ `provider`, `host-shell`, `shell`, `command-host`, `command-palette`, and
43
43
  `command-descriptors` subpaths. The root barrel remains the discovery surface,
44
44
  not the default runtime import graph.
45
45
 
46
+ Use `host-shell` when the product owns sidebar, editor, panel, and overlay content.
47
+ It keeps Kit layout, Activity Bar ordering, resize persistence, and status routing
48
+ without loading the full Settings/Profile/Help assembly. Use `shell` for the
49
+ batteries-included management experience.
50
+
46
51
  Prefer this package when the host needs provider + shell orchestration. For
47
52
  layout-only chrome without host services, start from
48
53
  `@workbench-kit/react/workbench/shell` — see
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workbench-kit/shell-react",
3
- "version": "0.0.2-prototype.0.2.24",
3
+ "version": "0.0.2-prototype.0.2.25",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -13,6 +13,7 @@
13
13
  "./command-palette": "./src/workbench/command-palette.ts",
14
14
  "./field-remap": "./src/field-remap/index.ts",
15
15
  "./field-remap/view.css": "./src/field-remap/view.css",
16
+ "./host-shell": "./src/shell/host-shell.tsx",
16
17
  "./layout-storage": "./src/workbench/layout-storage.ts",
17
18
  "./provider": "./src/shell/provider.tsx",
18
19
  "./shell": "./src/shell/shell.tsx"
@@ -30,14 +31,14 @@
30
31
  "@xyflow/react": "^12.11.2",
31
32
  "jsonata": "^2.2.0",
32
33
  "react-table-mapping": "^1.0.3",
33
- "@workbench-kit/platform": "0.0.2-prototype.0.2.24",
34
- "@workbench-kit/field-remap": "0.0.2-prototype.0.2.24",
35
- "@workbench-kit/react": "0.0.2-prototype.0.2.24",
36
- "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.24",
37
- "@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.24",
38
- "@workbench-kit/workspace": "0.0.2-prototype.0.2.24",
39
- "@workbench-kit/tokens": "0.0.2-prototype.0.2.24",
40
- "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.24"
34
+ "@workbench-kit/react": "0.0.2-prototype.0.2.25",
35
+ "@workbench-kit/tokens": "0.0.2-prototype.0.2.25",
36
+ "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.25",
37
+ "@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.25",
38
+ "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.25",
39
+ "@workbench-kit/platform": "0.0.2-prototype.0.2.25",
40
+ "@workbench-kit/field-remap": "0.0.2-prototype.0.2.25",
41
+ "@workbench-kit/workspace": "0.0.2-prototype.0.2.25"
41
42
  },
42
43
  "peerDependencies": {
43
44
  "react": "^19.0.0",
@@ -0,0 +1,174 @@
1
+ import { useCallback, useEffect, useMemo, useReducer, type ReactNode } from 'react';
2
+ import {
3
+ WorkbenchShell as ReactWorkbenchShell,
4
+ type StatusBarItemModel,
5
+ type StatusBarSectionModel,
6
+ type WorkbenchShellProps as ReactWorkbenchShellProps,
7
+ } from '@workbench-kit/react/workbench/shell';
8
+ import {
9
+ filterActivityBarItems,
10
+ sortActivityBarItems,
11
+ } from '@workbench-kit/react/workbench/activityBarOrder';
12
+ import { filterActivitiesByWhenClause } from '@workbench-kit/workbench-core';
13
+
14
+ import { useContextKeyRevision } from '../commands/use-context-key-revision.js';
15
+ import { createWorkbenchShellActivityItems } from './model.js';
16
+ import { useWorkbench } from './provider.js';
17
+ import {
18
+ WORKBENCH_HOST_PRIMARY_SIDEBAR_MAX_PX,
19
+ WORKBENCH_HOST_PRIMARY_SIDEBAR_MIN_PX,
20
+ workbenchHostPrimarySidebarSizePercentFromPx,
21
+ workbenchHostPrimarySidebarSizePxFromPercent,
22
+ } from './layout-metrics.js';
23
+
24
+ export interface WorkbenchHostShellProps {
25
+ activityBarAriaLabel?: string | undefined;
26
+ activityBarPosition?: ReactWorkbenchShellProps['activityBarPosition'];
27
+ auxiliarySidebar?: ReactNode;
28
+ bottomPanel?: ReactNode;
29
+ compactStatus?: boolean | undefined;
30
+ editorArea: ReactNode;
31
+ onStatusItemActivate?: ((item: StatusBarItemModel) => void) | undefined;
32
+ overlays?: ReactNode;
33
+ primarySidebar?: ReactNode;
34
+ rootClassName?: string | undefined;
35
+ shellPreset?: string | undefined;
36
+ statusBarAriaLabel?: string | undefined;
37
+ statusSections?: StatusBarSectionModel[] | undefined;
38
+ theme?: string | undefined;
39
+ themePreset?: string | undefined;
40
+ titleBar?: ReactNode;
41
+ }
42
+
43
+ /** Lean product shell: Kit owns layout/chrome while the host owns every content surface. */
44
+ export function WorkbenchHostShell({
45
+ activityBarAriaLabel = 'Primary activities',
46
+ activityBarPosition,
47
+ auxiliarySidebar,
48
+ bottomPanel,
49
+ compactStatus = true,
50
+ editorArea,
51
+ onStatusItemActivate,
52
+ overlays,
53
+ primarySidebar,
54
+ rootClassName,
55
+ shellPreset,
56
+ statusBarAriaLabel = 'Status bar',
57
+ statusSections = [],
58
+ theme,
59
+ themePreset,
60
+ titleBar,
61
+ }: WorkbenchHostShellProps) {
62
+ const { contextKeyService, executeCommand, extensionRegistry, layoutService } = useWorkbench();
63
+ const contextKeyRevision = useContextKeyRevision(contextKeyService);
64
+ const [, forceRender] = useReducer((count: number) => count + 1, 0);
65
+ const rerender = useCallback(() => forceRender(), []);
66
+ const contextKeySnapshot = useMemo(
67
+ () => contextKeyService.createSnapshot(),
68
+ [contextKeyRevision, contextKeyService],
69
+ );
70
+ const layout = layoutService.getState();
71
+ const activities = useMemo(
72
+ () =>
73
+ filterActivitiesByWhenClause(
74
+ extensionRegistry.activities.getActivities(),
75
+ contextKeySnapshot,
76
+ ),
77
+ [contextKeySnapshot, extensionRegistry],
78
+ );
79
+ const activityItems = filterActivityBarItems(
80
+ sortActivityBarItems(
81
+ createWorkbenchShellActivityItems({
82
+ activeViewContainerId: layout.sideBar.activeViewContainer,
83
+ activities,
84
+ viewContainers: extensionRegistry.views.getViewContainers('activitybar'),
85
+ views: extensionRegistry.views.getViews(),
86
+ }),
87
+ layout.activityBar.itemOrder,
88
+ ),
89
+ layout.activityBar.hiddenItemIds,
90
+ );
91
+
92
+ useEffect(() => {
93
+ const disposable = layoutService.onDidChangeLayout(rerender);
94
+ return () => disposable.dispose();
95
+ }, [layoutService, rerender]);
96
+
97
+ useEffect(() => {
98
+ if (activityItems.length === 0) {
99
+ return;
100
+ }
101
+ const activeId = layout.sideBar.activeViewContainer;
102
+ if (activeId !== undefined && !activityItems.some((item) => item.id === activeId)) {
103
+ layoutService.setActiveViewContainer(activityItems[0]?.id);
104
+ }
105
+ }, [activityItems, layout.sideBar.activeViewContainer, layoutService]);
106
+
107
+ return (
108
+ <ReactWorkbenchShell
109
+ activityBar={{
110
+ 'aria-label': activityBarAriaLabel,
111
+ items: activityItems,
112
+ reorderable: true,
113
+ visible: layout.activityBar.visible,
114
+ onItemActivate: (item) => layoutService.focusSideBarViewContainer(item.id),
115
+ onItemsReorder: (itemIds) => {
116
+ const preserved =
117
+ layout.activityBar.itemOrder?.filter((itemId) => !itemIds.includes(itemId)) ?? [];
118
+ layoutService.setActivityBarItemOrder([...itemIds, ...preserved]);
119
+ },
120
+ }}
121
+ activityBarPosition={activityBarPosition}
122
+ auxiliarySidebar={
123
+ auxiliarySidebar === undefined
124
+ ? undefined
125
+ : { isVisible: layout.auxiliaryBar.visible, node: auxiliarySidebar }
126
+ }
127
+ bottomPanel={
128
+ bottomPanel === undefined
129
+ ? undefined
130
+ : {
131
+ isVisible: layout.panel.visible,
132
+ node: bottomPanel,
133
+ sizePercent: layout.panel.sizePercent,
134
+ onSizePercentChange: (sizePercent) => layoutService.setPanelSizePercent(sizePercent),
135
+ }
136
+ }
137
+ compactStatus={compactStatus}
138
+ onStatusItemActivate={(item) => {
139
+ const contributed = extensionRegistry.statusBar.getStatusBarItem(item.id);
140
+ if (contributed?.command) {
141
+ void executeCommand(contributed.command).catch(() => undefined);
142
+ return;
143
+ }
144
+ onStatusItemActivate?.(item);
145
+ }}
146
+ overlays={overlays}
147
+ primarySidebar={
148
+ primarySidebar === undefined
149
+ ? undefined
150
+ : {
151
+ isVisible: layout.sideBar.visible,
152
+ maxPrimarySizePx: WORKBENCH_HOST_PRIMARY_SIDEBAR_MAX_PX,
153
+ minPrimarySizePx: WORKBENCH_HOST_PRIMARY_SIDEBAR_MIN_PX,
154
+ node: primarySidebar,
155
+ primarySizePx: workbenchHostPrimarySidebarSizePxFromPercent(
156
+ layout.sideBar.sizePercent,
157
+ ),
158
+ onSizePxChange: (sizePx) =>
159
+ layoutService.setSideBarSizePercent(
160
+ workbenchHostPrimarySidebarSizePercentFromPx(sizePx),
161
+ ),
162
+ }
163
+ }
164
+ rootClassName={rootClassName}
165
+ secondaryArea={editorArea}
166
+ shellPreset={shellPreset}
167
+ statusBarAriaLabel={statusBarAriaLabel}
168
+ statusSections={statusSections}
169
+ theme={theme}
170
+ themePreset={themePreset}
171
+ titleBar={titleBar}
172
+ />
173
+ );
174
+ }
@@ -0,0 +1,27 @@
1
+ const SIDEBAR_LAYOUT_REFERENCE_WIDTH_PX = 1200;
2
+
3
+ export const WORKBENCH_HOST_PRIMARY_SIDEBAR_MIN_PX = 200;
4
+ export const WORKBENCH_HOST_PRIMARY_SIDEBAR_MAX_PX = 480;
5
+
6
+ export function clampWorkbenchHostPrimarySidebarSizePx(sizePx: number): number {
7
+ if (!Number.isFinite(sizePx)) {
8
+ return WORKBENCH_HOST_PRIMARY_SIDEBAR_MIN_PX;
9
+ }
10
+ return Math.min(
11
+ WORKBENCH_HOST_PRIMARY_SIDEBAR_MAX_PX,
12
+ Math.max(WORKBENCH_HOST_PRIMARY_SIDEBAR_MIN_PX, Math.round(sizePx)),
13
+ );
14
+ }
15
+
16
+ export function workbenchHostPrimarySidebarSizePxFromPercent(
17
+ sizePercent: number | undefined,
18
+ ): number {
19
+ const percent = Number.isFinite(sizePercent) ? (sizePercent as number) : 20;
20
+ return clampWorkbenchHostPrimarySidebarSizePx(
21
+ (percent / 100) * SIDEBAR_LAYOUT_REFERENCE_WIDTH_PX,
22
+ );
23
+ }
24
+
25
+ export function workbenchHostPrimarySidebarSizePercentFromPx(sizePx: number): number {
26
+ return (clampWorkbenchHostPrimarySidebarSizePx(sizePx) / SIDEBAR_LAYOUT_REFERENCE_WIDTH_PX) * 100;
27
+ }
@@ -78,30 +78,14 @@ import {
78
78
  import { WorkbenchShellTitleBarLayoutControls } from './titlebar-layout-controls.js';
79
79
  import { WorkbenchProfileModal, type WorkbenchProfileInput } from '../workbench/profile-modal.js';
80
80
  import { useContextKeyRevision } from '../commands/use-context-key-revision.js';
81
+ import {
82
+ WORKBENCH_HOST_PRIMARY_SIDEBAR_MAX_PX,
83
+ WORKBENCH_HOST_PRIMARY_SIDEBAR_MIN_PX,
84
+ workbenchHostPrimarySidebarSizePercentFromPx,
85
+ workbenchHostPrimarySidebarSizePxFromPercent,
86
+ } from './layout-metrics.js';
81
87
  export type { WorkbenchLocaleOption, WorkbenchThemeOption } from './settings.js';
82
88
 
83
- /** Virtual width used to bridge layout `sizePercent` into pixel SplitView units. */
84
- const SHELL_REACT_SIDEBAR_LAYOUT_REFERENCE_WIDTH_PX = 1200;
85
- const SHELL_REACT_PRIMARY_SIDEBAR_MIN_PX = 200;
86
- const SHELL_REACT_PRIMARY_SIDEBAR_MAX_PX = 480;
87
-
88
- function clampShellReactPrimarySidebarSizePx(sizePx: number): number {
89
- if (!Number.isFinite(sizePx)) {
90
- return SHELL_REACT_PRIMARY_SIDEBAR_MIN_PX;
91
- }
92
- return Math.min(
93
- SHELL_REACT_PRIMARY_SIDEBAR_MAX_PX,
94
- Math.max(SHELL_REACT_PRIMARY_SIDEBAR_MIN_PX, Math.round(sizePx)),
95
- );
96
- }
97
-
98
- function shellReactPrimarySidebarSizePxFromPercent(sizePercent: number | undefined): number {
99
- const percent = Number.isFinite(sizePercent) ? (sizePercent as number) : 20;
100
- return clampShellReactPrimarySidebarSizePx(
101
- (percent / 100) * SHELL_REACT_SIDEBAR_LAYOUT_REFERENCE_WIDTH_PX,
102
- );
103
- }
104
-
105
89
  export interface WorkbenchShellProps {
106
90
  accountManagement?: WorkbenchAccountManagementInput | undefined;
107
91
  additionalSettingsCategories?: readonly WorkbenchSettingsCategory[] | undefined;
@@ -627,8 +611,8 @@ export function WorkbenchShell({
627
611
  isVisible: layout.sideBar.visible,
628
612
  // Provider layout still stores sideBar.sizePercent; convert at the shell boundary
629
613
  // until workbench-core persists pixel widths directly.
630
- maxPrimarySizePx: SHELL_REACT_PRIMARY_SIDEBAR_MAX_PX,
631
- minPrimarySizePx: SHELL_REACT_PRIMARY_SIDEBAR_MIN_PX,
614
+ maxPrimarySizePx: WORKBENCH_HOST_PRIMARY_SIDEBAR_MAX_PX,
615
+ minPrimarySizePx: WORKBENCH_HOST_PRIMARY_SIDEBAR_MIN_PX,
632
616
  node:
633
617
  primarySidebar ??
634
618
  renderDefaultPrimarySidebar(
@@ -639,13 +623,9 @@ export function WorkbenchShell({
639
623
  contextKeySnapshot,
640
624
  ),
641
625
  onSizePxChange: (sizePx) => {
642
- layoutService.setSideBarSizePercent(
643
- (clampShellReactPrimarySidebarSizePx(sizePx) /
644
- SHELL_REACT_SIDEBAR_LAYOUT_REFERENCE_WIDTH_PX) *
645
- 100,
646
- );
626
+ layoutService.setSideBarSizePercent(workbenchHostPrimarySidebarSizePercentFromPx(sizePx));
647
627
  },
648
- primarySizePx: shellReactPrimarySidebarSizePxFromPercent(layout.sideBar.sizePercent),
628
+ primarySizePx: workbenchHostPrimarySidebarSizePxFromPercent(layout.sideBar.sizePercent),
649
629
  }}
650
630
  auxiliarySidebar={{
651
631
  isVisible: layout.auxiliaryBar.visible,