@workbench-kit/shell-react 0.0.2-prototype.0.2.24 → 0.0.2-prototype.0.2.26
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 +6 -1
- package/package.json +10 -9
- package/src/shell/host-shell.tsx +174 -0
- package/src/shell/layout-metrics.ts +27 -0
- package/src/shell/shell.tsx +10 -30
- package/src/workbench/command-host.tsx +4 -0
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.
|
|
3
|
+
"version": "0.0.2-prototype.0.2.26",
|
|
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/
|
|
34
|
-
"@workbench-kit/
|
|
35
|
-
"@workbench-kit/
|
|
36
|
-
"@workbench-kit/workbench-
|
|
37
|
-
"@workbench-kit/workbench-
|
|
38
|
-
"@workbench-kit/workspace": "0.0.2-prototype.0.2.
|
|
39
|
-
"@workbench-kit/
|
|
40
|
-
"@workbench-kit/
|
|
34
|
+
"@workbench-kit/field-remap": "0.0.2-prototype.0.2.26",
|
|
35
|
+
"@workbench-kit/tokens": "0.0.2-prototype.0.2.26",
|
|
36
|
+
"@workbench-kit/workbench-config": "0.0.2-prototype.0.2.26",
|
|
37
|
+
"@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.26",
|
|
38
|
+
"@workbench-kit/workbench-core": "0.0.2-prototype.0.2.26",
|
|
39
|
+
"@workbench-kit/workspace": "0.0.2-prototype.0.2.26",
|
|
40
|
+
"@workbench-kit/platform": "0.0.2-prototype.0.2.26",
|
|
41
|
+
"@workbench-kit/react": "0.0.2-prototype.0.2.26"
|
|
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
|
+
}
|
package/src/shell/shell.tsx
CHANGED
|
@@ -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:
|
|
631
|
-
minPrimarySizePx:
|
|
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:
|
|
628
|
+
primarySizePx: workbenchHostPrimarySidebarSizePxFromPercent(layout.sideBar.sizePercent),
|
|
649
629
|
}}
|
|
650
630
|
auxiliarySidebar={{
|
|
651
631
|
isVisible: layout.auxiliaryBar.visible,
|
|
@@ -151,12 +151,16 @@ export function WorkbenchCommandHost({
|
|
|
151
151
|
|
|
152
152
|
const shellContext = useMemo<WorkbenchShellCommandContext>(
|
|
153
153
|
() => ({
|
|
154
|
+
isFocusModeActive: layoutService.isFocusModeActive(),
|
|
154
155
|
isPrimarySidebarVisible: layout.sideBar.visible,
|
|
155
156
|
openSettings: onOpenSettings,
|
|
156
157
|
showActivity: (activityId) => {
|
|
157
158
|
layoutService.setActiveViewContainer(activityId);
|
|
158
159
|
layoutService.setSideBarVisible(true);
|
|
159
160
|
},
|
|
161
|
+
toggleFocusMode: () => {
|
|
162
|
+
layoutService.setFocusModeActive(!layoutService.isFocusModeActive());
|
|
163
|
+
},
|
|
160
164
|
togglePrimarySidebar: () => {
|
|
161
165
|
layoutService.setSideBarVisible(!layout.sideBar.visible);
|
|
162
166
|
},
|