dsh-better-sidebar 0.10.0
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/LICENSE +21 -0
- package/README.md +139 -0
- package/README_EN.md +139 -0
- package/lib/client-docx.js +7649 -0
- package/lib/client-editor.js +32677 -0
- package/lib/client-pptx.js +63438 -0
- package/lib/client-registry.js +8021 -0
- package/lib/client-terminal.js +8060 -0
- package/lib/client-xlsx.js +359755 -0
- package/lib/client.js +8021 -0
- package/lib/index.js +2604 -0
- package/lib/invariant.js +22 -0
- package/lib/types/agent-pty.d.ts +220 -0
- package/lib/types/browser-probe.d.ts +15 -0
- package/lib/types/bundle-route.d.ts +13 -0
- package/lib/types/client/BrowserView.d.ts +22 -0
- package/lib/types/client/DiffTab.d.ts +6 -0
- package/lib/types/client/DiffView.d.ts +51 -0
- package/lib/types/client/EditorHost.d.ts +10 -0
- package/lib/types/client/ExplorerView.d.ts +9 -0
- package/lib/types/client/GitView.d.ts +8 -0
- package/lib/types/client/OrphanedTab.d.ts +11 -0
- package/lib/types/client/PdfView.d.ts +6 -0
- package/lib/types/client/PptxView.d.ts +6 -0
- package/lib/types/client/SandboxStatusBar.d.ts +10 -0
- package/lib/types/client/SideCardSection.d.ts +27 -0
- package/lib/types/client/Sidebar.d.ts +6 -0
- package/lib/types/client/SubagentView.d.ts +16 -0
- package/lib/types/client/TabBar.d.ts +38 -0
- package/lib/types/client/TerminalView.d.ts +8 -0
- package/lib/types/client/TextEditor.d.ts +10 -0
- package/lib/types/client/api.d.ts +170 -0
- package/lib/types/client/binary-download.d.ts +12 -0
- package/lib/types/client/breakpoints.d.ts +12 -0
- package/lib/types/client/browser.d.ts +50 -0
- package/lib/types/client/builtins/index.d.ts +17 -0
- package/lib/types/client/builtins/tabs.d.ts +6 -0
- package/lib/types/client/builtins/viewers.d.ts +3 -0
- package/lib/types/client/chunk-loader.d.ts +74 -0
- package/lib/types/client/chunks/docx.d.ts +8 -0
- package/lib/types/client/chunks/editor.d.ts +9 -0
- package/lib/types/client/chunks/pptx.d.ts +8 -0
- package/lib/types/client/chunks/terminal.d.ts +9 -0
- package/lib/types/client/chunks/xlsx.d.ts +9 -0
- package/lib/types/client/cm-themes.d.ts +24 -0
- package/lib/types/client/conversation-draft.d.ts +14 -0
- package/lib/types/client/docx-view.d.ts +8 -0
- package/lib/types/client/editor-load.d.ts +66 -0
- package/lib/types/client/icons.d.ts +47 -0
- package/lib/types/client/image-types.d.ts +3 -0
- package/lib/types/client/ime-guard.d.ts +36 -0
- package/lib/types/client/index.d.ts +10 -0
- package/lib/types/client/intercept.d.ts +20 -0
- package/lib/types/client/lang.d.ts +12 -0
- package/lib/types/client/lazy-chunk.d.ts +25 -0
- package/lib/types/client/link-intercept.d.ts +35 -0
- package/lib/types/client/locales.d.ts +237 -0
- package/lib/types/client/office-shared.d.ts +33 -0
- package/lib/types/client/office-types.d.ts +36 -0
- package/lib/types/client/office-view.d.ts +12 -0
- package/lib/types/client/openpath-intercept.d.ts +41 -0
- package/lib/types/client/paths.d.ts +19 -0
- package/lib/types/client/pdf-types.d.ts +2 -0
- package/lib/types/client/prefs.d.ts +30 -0
- package/lib/types/client/produced-files.d.ts +27 -0
- package/lib/types/client/selection-payload.d.ts +27 -0
- package/lib/types/client/service.d.ts +214 -0
- package/lib/types/client/split-pane.d.ts +29 -0
- package/lib/types/client/state.d.ts +322 -0
- package/lib/types/client/subagent-activity.d.ts +35 -0
- package/lib/types/client/subagent-detect.d.ts +47 -0
- package/lib/types/client/subagent-jobs.d.ts +63 -0
- package/lib/types/client/theme.d.ts +28 -0
- package/lib/types/client/xlsx-to-univer.d.ts +33 -0
- package/lib/types/client/xlsx-view.d.ts +10 -0
- package/lib/types/config.d.ts +41 -0
- package/lib/types/context-types.d.ts +431 -0
- package/lib/types/fs-tree.d.ts +41 -0
- package/lib/types/git.d.ts +75 -0
- package/lib/types/html-route.d.ts +48 -0
- package/lib/types/index.d.ts +39 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/jobs-routes.d.ts +45 -0
- package/lib/types/prefs-shared.d.ts +106 -0
- package/lib/types/pty-manager.d.ts +76 -0
- package/lib/types/tools.d.ts +28 -0
- package/lib/types/trust-fence.d.ts +24 -0
- package/lib/types/wire.d.ts +38 -0
- package/package.json +168 -0
- package/src/agent-pty.ts +519 -0
- package/src/browser-probe.ts +26 -0
- package/src/bundle-route.ts +130 -0
- package/src/client/BrowserView.tsx +253 -0
- package/src/client/DiffTab.tsx +110 -0
- package/src/client/DiffView.tsx +266 -0
- package/src/client/EditorHost.tsx +101 -0
- package/src/client/ExplorerView.tsx +283 -0
- package/src/client/GitView.tsx +540 -0
- package/src/client/OrphanedTab.tsx +27 -0
- package/src/client/PdfView.tsx +110 -0
- package/src/client/PptxView.tsx +131 -0
- package/src/client/SandboxStatusBar.tsx +60 -0
- package/src/client/SideCardSection.module.css +271 -0
- package/src/client/SideCardSection.tsx +426 -0
- package/src/client/Sidebar.tsx +861 -0
- package/src/client/SubagentView.module.css +547 -0
- package/src/client/SubagentView.tsx +866 -0
- package/src/client/TabBar.tsx +188 -0
- package/src/client/TerminalView.tsx +233 -0
- package/src/client/TextEditor.tsx +382 -0
- package/src/client/api.ts +215 -0
- package/src/client/binary-download.tsx +23 -0
- package/src/client/breakpoints.ts +51 -0
- package/src/client/browser.ts +119 -0
- package/src/client/builtins/index.ts +33 -0
- package/src/client/builtins/tabs.tsx +211 -0
- package/src/client/builtins/viewers.tsx +151 -0
- package/src/client/chunk-loader.ts +196 -0
- package/src/client/chunks/docx.tsx +8 -0
- package/src/client/chunks/editor.tsx +9 -0
- package/src/client/chunks/pptx.tsx +8 -0
- package/src/client/chunks/terminal.tsx +9 -0
- package/src/client/chunks/xlsx.tsx +9 -0
- package/src/client/cm-themes.ts +129 -0
- package/src/client/conversation-draft.ts +29 -0
- package/src/client/css-modules.d.ts +5 -0
- package/src/client/docx-view.tsx +113 -0
- package/src/client/editor-load.ts +92 -0
- package/src/client/icons.tsx +137 -0
- package/src/client/image-types.ts +8 -0
- package/src/client/ime-guard.ts +47 -0
- package/src/client/index.tsx +238 -0
- package/src/client/intercept.tsx +94 -0
- package/src/client/lang.ts +95 -0
- package/src/client/layout.css +54 -0
- package/src/client/lazy-chunk.tsx +89 -0
- package/src/client/link-intercept.ts +66 -0
- package/src/client/locales.ts +476 -0
- package/src/client/office-shared.tsx +40 -0
- package/src/client/office-types.ts +48 -0
- package/src/client/office-view.tsx +12 -0
- package/src/client/openpath-intercept.ts +62 -0
- package/src/client/paths.ts +28 -0
- package/src/client/pdf-types.ts +4 -0
- package/src/client/prefs.ts +100 -0
- package/src/client/produced-files.ts +87 -0
- package/src/client/selection-payload.ts +86 -0
- package/src/client/service.ts +434 -0
- package/src/client/sidebar.module.css +1917 -0
- package/src/client/split-pane.tsx +292 -0
- package/src/client/state.ts +1048 -0
- package/src/client/subagent-activity.ts +67 -0
- package/src/client/subagent-detect.ts +125 -0
- package/src/client/subagent-jobs.ts +161 -0
- package/src/client/theme.ts +44 -0
- package/src/client/xlsx-to-univer.ts +260 -0
- package/src/client/xlsx-view.tsx +116 -0
- package/src/config.ts +95 -0
- package/src/context-types.ts +445 -0
- package/src/fs-tree.ts +115 -0
- package/src/git.ts +236 -0
- package/src/html-route.ts +70 -0
- package/src/index.ts +897 -0
- package/src/invariant.ts +32 -0
- package/src/jobs-routes.ts +257 -0
- package/src/prefs-shared.ts +127 -0
- package/src/pty-manager.ts +199 -0
- package/src/tools.ts +477 -0
- package/src/trust-fence.ts +77 -0
- package/src/wire.ts +94 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The split-pane workbench: renders the recursive split tree. A split lays
|
|
3
|
+
* children out row- or column-wise with draggable dividers (fractional
|
|
4
|
+
* sizes); a leaf renders its tab strip plus the active tab's content.
|
|
5
|
+
*
|
|
6
|
+
* Splitting is VSCode-style DRAG-TO-EDGE, not buttons: while dragging a tab
|
|
7
|
+
* over a pane, a drop overlay shows five zones — four edges (left/right/up/
|
|
8
|
+
* down) that split the pane with the tab in a fresh leaf, and the center
|
|
9
|
+
* that merges the tab into the pane. The tree and all operations live in
|
|
10
|
+
* state.ts; this file is pure presentation over them.
|
|
11
|
+
*/
|
|
12
|
+
import { Fragment, useEffect, useRef, useState } from 'react'
|
|
13
|
+
import type { ReactNode } from 'react'
|
|
14
|
+
import clsx from 'clsx'
|
|
15
|
+
import type { SidebarState, SidebarTab, SplitNode } from './state.ts'
|
|
16
|
+
import type { DropZone } from './state.ts'
|
|
17
|
+
import { TabBar, type NewTabOption, parseDrag, type TabDragPayload } from './TabBar.tsx'
|
|
18
|
+
import css from './sidebar.module.css'
|
|
19
|
+
|
|
20
|
+
/** Actions the workbench needs (bound to the store by the sidebar shell). */
|
|
21
|
+
export interface WorkbenchActions {
|
|
22
|
+
closeTab: (paneId: string, tabId: string) => void
|
|
23
|
+
activateTab: (paneId: string, tabId: string) => void
|
|
24
|
+
/** Make a pane the target of newly opened tabs (click focus). */
|
|
25
|
+
focusPane: (paneId: string) => void
|
|
26
|
+
/** VSCode drag gesture: edge → split the target pane, center → merge. */
|
|
27
|
+
moveTabToEdge: (payload: TabDragPayload, toPane: string, zone: DropZone) => void
|
|
28
|
+
/** Reorder within a pane (drop onto another tab inserts before it). */
|
|
29
|
+
moveTabBefore: (payload: TabDragPayload, toPane: string, beforeTabId: string) => void
|
|
30
|
+
resizeSplit: (splitId: string, index: number, deltaFrac: number) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One divider: pointer-capture drag translating px deltas into fractions.
|
|
34
|
+
* Deltas are incremental — each move reports the displacement since the
|
|
35
|
+
* previous move — because the store adds every reported delta to the pane
|
|
36
|
+
* sizes; a cumulative (since-pointer-down) delta would be re-added on each
|
|
37
|
+
* move and the divider would run away from the cursor. */
|
|
38
|
+
function Divider(props: { dir: 'row' | 'col'; onResize: (deltaFrac: number) => void }) {
|
|
39
|
+
const { dir, onResize } = props
|
|
40
|
+
// `last` is the previous pointer position while dragging; `size` is the
|
|
41
|
+
// container size at pointer-down (constant during the drag), used to
|
|
42
|
+
// normalize the px delta into a fraction.
|
|
43
|
+
const last = useRef({ x: 0, y: 0, size: 0 })
|
|
44
|
+
const [dragging, setDragging] = useState(false)
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
className={clsx(css.divider, dir === 'row' ? css.dividerRow : css.dividerCol, dragging && css.dividerActive)}
|
|
49
|
+
onPointerDown={(event) => {
|
|
50
|
+
event.preventDefault()
|
|
51
|
+
event.currentTarget.setPointerCapture(event.pointerId)
|
|
52
|
+
const box = event.currentTarget.parentElement?.getBoundingClientRect()
|
|
53
|
+
last.current = {
|
|
54
|
+
x: event.clientX,
|
|
55
|
+
y: event.clientY,
|
|
56
|
+
size: box === undefined ? 1 : (dir === 'row' ? box.width : box.height),
|
|
57
|
+
}
|
|
58
|
+
setDragging(true)
|
|
59
|
+
}}
|
|
60
|
+
onPointerMove={(event) => {
|
|
61
|
+
if (!event.currentTarget.hasPointerCapture(event.pointerId)) return
|
|
62
|
+
const delta = dir === 'row' ? event.clientX - last.current.x : event.clientY - last.current.y
|
|
63
|
+
onResize(delta / Math.max(1, last.current.size))
|
|
64
|
+
last.current.x = event.clientX
|
|
65
|
+
last.current.y = event.clientY
|
|
66
|
+
}}
|
|
67
|
+
onPointerUp={(event) => {
|
|
68
|
+
if (!event.currentTarget.hasPointerCapture(event.pointerId)) return
|
|
69
|
+
event.currentTarget.releasePointerCapture(event.pointerId)
|
|
70
|
+
setDragging(false)
|
|
71
|
+
}}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Map a pointer position inside a pane to the VSCode drop zone (25% edges). */
|
|
77
|
+
function zoneAt(event: React.DragEvent, pane: HTMLElement): DropZone {
|
|
78
|
+
const rect = pane.getBoundingClientRect()
|
|
79
|
+
if (rect.width === 0 || rect.height === 0) return 'center'
|
|
80
|
+
const x = (event.clientX - rect.left) / rect.width
|
|
81
|
+
const y = (event.clientY - rect.top) / rect.height
|
|
82
|
+
if (x < 0.25) return 'left'
|
|
83
|
+
if (x > 0.75) return 'right'
|
|
84
|
+
if (y < 0.25) return 'up'
|
|
85
|
+
if (y > 0.75) return 'down'
|
|
86
|
+
return 'center'
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The icon of one openable type card (mirror of the + menu options). */
|
|
90
|
+
/**
|
|
91
|
+
* An empty pane's welcome cards: the openable types as cards, clicked to
|
|
92
|
+
* open (instead of a bare "this pane is empty" message).
|
|
93
|
+
*/
|
|
94
|
+
function PaneEmptyCards(props: {
|
|
95
|
+
newTabOptions: NewTabOption[]
|
|
96
|
+
onNewTab: (optionId: string) => void
|
|
97
|
+
}) {
|
|
98
|
+
const { newTabOptions, onNewTab } = props
|
|
99
|
+
return (
|
|
100
|
+
<div className={css.paneEmptyCards}>
|
|
101
|
+
{newTabOptions.map(option => (
|
|
102
|
+
<button
|
|
103
|
+
key={option.id}
|
|
104
|
+
type="button"
|
|
105
|
+
className={css.paneCard}
|
|
106
|
+
disabled={option.disabled === true}
|
|
107
|
+
title={option.label}
|
|
108
|
+
onClick={() => { onNewTab(option.id) }}
|
|
109
|
+
>
|
|
110
|
+
{option.icon ?? null}
|
|
111
|
+
<span>{option.label}</span>
|
|
112
|
+
</button>
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** A leaf: tab strip + active content + VSCode-style drop target for tabs. */
|
|
119
|
+
function LeafView(props: {
|
|
120
|
+
leaf: { id: string; tabs: SidebarTab[]; active: string | null }
|
|
121
|
+
newTabOptions: NewTabOption[]
|
|
122
|
+
actions: WorkbenchActions
|
|
123
|
+
onNewTab: (optionId: string) => void
|
|
124
|
+
renderTab: (tab: SidebarTab, active: boolean, paneId: string) => ReactNode
|
|
125
|
+
getTabIcon?: (tab: SidebarTab) => ReactNode
|
|
126
|
+
}) {
|
|
127
|
+
const { leaf, newTabOptions, actions, onNewTab, renderTab, getTabIcon } = props
|
|
128
|
+
const [dropZone, setDropZone] = useState<DropZone | null>(null)
|
|
129
|
+
const activeTab = leaf.tabs.find(tab => tab.id === leaf.active) ?? leaf.tabs[leaf.tabs.length - 1]
|
|
130
|
+
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
const clear = (): void => { setDropZone(null) }
|
|
133
|
+
window.addEventListener('dragend', clear, true)
|
|
134
|
+
window.addEventListener('drop', clear, true)
|
|
135
|
+
window.addEventListener('blur', clear)
|
|
136
|
+
return () => {
|
|
137
|
+
window.removeEventListener('dragend', clear, true)
|
|
138
|
+
window.removeEventListener('drop', clear, true)
|
|
139
|
+
window.removeEventListener('blur', clear)
|
|
140
|
+
}
|
|
141
|
+
}, [])
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<div
|
|
145
|
+
className={clsx(css.pane, dropZone !== null && css.paneDrop)}
|
|
146
|
+
onPointerDown={() => { actions.focusPane(leaf.id) }}
|
|
147
|
+
onDragOver={(event) => {
|
|
148
|
+
event.preventDefault()
|
|
149
|
+
const zone = zoneAt(event, event.currentTarget)
|
|
150
|
+
setDropZone(zone)
|
|
151
|
+
}}
|
|
152
|
+
onDragLeave={(event) => {
|
|
153
|
+
// Only clear when the pointer left the pane entirely (not onto a child).
|
|
154
|
+
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
|
|
155
|
+
setDropZone(null)
|
|
156
|
+
}
|
|
157
|
+
}}
|
|
158
|
+
onDrop={(event) => {
|
|
159
|
+
event.preventDefault()
|
|
160
|
+
const zone = dropZone ?? zoneAt(event, event.currentTarget)
|
|
161
|
+
setDropZone(null)
|
|
162
|
+
const payload = parseDrag(event.dataTransfer.getData('application/x-dsh-tab'))
|
|
163
|
+
if (payload !== null) actions.moveTabToEdge(payload, leaf.id, zone)
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{dropZone !== null && <div className={clsx(css.dropOverlay, css[`drop${dropZone[0]!.toUpperCase()}${dropZone.slice(1)}`])} />}
|
|
167
|
+
{/*
|
|
168
|
+
The tab strip renders even for an empty pane: the + menu must stay
|
|
169
|
+
reachable when the pane has no tabs (fresh split, or the last tab was
|
|
170
|
+
dragged out), so a new tab can always be created or dragged in.
|
|
171
|
+
*/}
|
|
172
|
+
<TabBar
|
|
173
|
+
paneId={leaf.id}
|
|
174
|
+
tabs={leaf.tabs}
|
|
175
|
+
active={leaf.active}
|
|
176
|
+
onActivate={(tabId) => { actions.activateTab(leaf.id, tabId) }}
|
|
177
|
+
onClose={(tabId) => { actions.closeTab(leaf.id, tabId) }}
|
|
178
|
+
onNewTab={onNewTab}
|
|
179
|
+
newTabOptions={newTabOptions}
|
|
180
|
+
getTabIcon={getTabIcon}
|
|
181
|
+
onDropTab={(payload, before) => {
|
|
182
|
+
if (before === null) actions.moveTabToEdge(payload, leaf.id, 'center')
|
|
183
|
+
else actions.moveTabBefore(payload, leaf.id, before)
|
|
184
|
+
}}
|
|
185
|
+
/>
|
|
186
|
+
{leaf.tabs.length > 0 ? (
|
|
187
|
+
/*
|
|
188
|
+
Every tab stays MOUNTED (inactive ones hidden), so switching tabs
|
|
189
|
+
never tears down the content: a terminal keeps its pty connection
|
|
190
|
+
and scrollback, an editor keeps its CodeMirror view and unsaved
|
|
191
|
+
draft, explorer/git keep their loaded data. The unmount (and the
|
|
192
|
+
terminal's close frame) happens only when a tab is truly closed.
|
|
193
|
+
*/
|
|
194
|
+
<div className={css.paneContent}>
|
|
195
|
+
{leaf.tabs.map(tab => (
|
|
196
|
+
<div
|
|
197
|
+
key={tab.id}
|
|
198
|
+
className={clsx(css.paneTab, tab.id !== activeTab?.id && css.paneTabHidden)}
|
|
199
|
+
>
|
|
200
|
+
{renderTab(tab, tab.id === activeTab?.id, leaf.id)}
|
|
201
|
+
</div>
|
|
202
|
+
))}
|
|
203
|
+
</div>
|
|
204
|
+
) : (
|
|
205
|
+
<PaneEmptyCards newTabOptions={newTabOptions} onNewTab={onNewTab} />
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Recursive node renderer. */
|
|
212
|
+
function NodeView(props: {
|
|
213
|
+
node: SplitNode
|
|
214
|
+
state: SidebarState
|
|
215
|
+
newTabOptions: NewTabOption[]
|
|
216
|
+
actions: WorkbenchActions
|
|
217
|
+
onNewTab: (optionId: string) => void
|
|
218
|
+
renderTab: (tab: SidebarTab, active: boolean, paneId: string) => ReactNode
|
|
219
|
+
getTabIcon?: (tab: SidebarTab) => ReactNode
|
|
220
|
+
}) {
|
|
221
|
+
const { node, state, newTabOptions, actions, onNewTab, renderTab, getTabIcon } = props
|
|
222
|
+
if (node.kind === 'leaf') {
|
|
223
|
+
return (
|
|
224
|
+
<LeafView
|
|
225
|
+
leaf={node}
|
|
226
|
+
newTabOptions={newTabOptions}
|
|
227
|
+
actions={actions}
|
|
228
|
+
onNewTab={onNewTab}
|
|
229
|
+
renderTab={renderTab}
|
|
230
|
+
getTabIcon={getTabIcon}
|
|
231
|
+
/>
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
const isRow = node.dir === 'row'
|
|
235
|
+
return (
|
|
236
|
+
<div className={clsx(css.split, isRow ? css.splitRow : css.splitCol)}>
|
|
237
|
+
{node.children.map((child, index) => (
|
|
238
|
+
<Fragment key={child.id}>
|
|
239
|
+
{index > 0 && (
|
|
240
|
+
<Divider
|
|
241
|
+
dir={node.dir}
|
|
242
|
+
onResize={(deltaFrac) => { actions.resizeSplit(node.id, index - 1, deltaFrac) }}
|
|
243
|
+
/>
|
|
244
|
+
)}
|
|
245
|
+
<div
|
|
246
|
+
className={css.splitChild}
|
|
247
|
+
style={{ flexGrow: node.sizes[index], flexBasis: 0, minWidth: 0, minHeight: 0 }}
|
|
248
|
+
>
|
|
249
|
+
<NodeView
|
|
250
|
+
node={child}
|
|
251
|
+
state={state}
|
|
252
|
+
newTabOptions={newTabOptions}
|
|
253
|
+
actions={actions}
|
|
254
|
+
onNewTab={onNewTab}
|
|
255
|
+
renderTab={renderTab}
|
|
256
|
+
getTabIcon={getTabIcon}
|
|
257
|
+
/>
|
|
258
|
+
</div>
|
|
259
|
+
</Fragment>
|
|
260
|
+
))}
|
|
261
|
+
</div>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** The workbench: the split tree filling the sidebar body. `tree` selects
|
|
266
|
+
* which tree renders (the right panel's by default; the bottom panel passes
|
|
267
|
+
* `state.bottomSplits` — the actions route by pane id, so one action set
|
|
268
|
+
* serves both). */
|
|
269
|
+
export function Workbench(props: {
|
|
270
|
+
state: SidebarState
|
|
271
|
+
tree?: SplitNode
|
|
272
|
+
newTabOptions: NewTabOption[]
|
|
273
|
+
actions: WorkbenchActions
|
|
274
|
+
onNewTab: (optionId: string) => void
|
|
275
|
+
renderTab: (tab: SidebarTab, active: boolean, paneId: string) => ReactNode
|
|
276
|
+
getTabIcon?: (tab: SidebarTab) => ReactNode
|
|
277
|
+
}) {
|
|
278
|
+
const { state, tree, newTabOptions, actions, onNewTab, renderTab, getTabIcon } = props
|
|
279
|
+
return (
|
|
280
|
+
<div className={css.workbench}>
|
|
281
|
+
<NodeView
|
|
282
|
+
node={tree ?? state.splits}
|
|
283
|
+
state={state}
|
|
284
|
+
newTabOptions={newTabOptions}
|
|
285
|
+
actions={actions}
|
|
286
|
+
onNewTab={onNewTab}
|
|
287
|
+
renderTab={renderTab}
|
|
288
|
+
getTabIcon={getTabIcon}
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
)
|
|
292
|
+
}
|