@brimveyn/aimux 1.20.3 → 1.21.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.
Files changed (35) hide show
  1. package/package.json +2 -2
  2. package/src/app-runtime/side-effects.ts +3 -48
  3. package/src/app-runtime/split-drag-controller.ts +4 -8
  4. package/src/app-runtime/use-mouse-handlers.ts +27 -42
  5. package/src/app-runtime/use-terminal-resize.ts +11 -20
  6. package/src/app.tsx +9 -37
  7. package/src/config.ts +98 -8
  8. package/src/git/pr-merge.ts +64 -0
  9. package/src/git/pr-status-poller.ts +57 -0
  10. package/src/git/pr-status.ts +227 -0
  11. package/src/index.tsx +7 -2
  12. package/src/platform/clipboard.ts +140 -4
  13. package/src/platform/open-url.ts +39 -0
  14. package/src/services/ai-usage/spawn.ts +3 -1
  15. package/src/state/bars.ts +75 -0
  16. package/src/state/git-pane-sizing.ts +0 -9
  17. package/src/state/pr-status-store.ts +39 -0
  18. package/src/state/reducers/git-panel-state.ts +0 -47
  19. package/src/state/reducers/ui-state.ts +83 -13
  20. package/src/state/session-persistence.ts +5 -7
  21. package/src/state/store.ts +28 -35
  22. package/src/state/types.ts +24 -19
  23. package/src/state/workspace-save.ts +5 -7
  24. package/src/ui/components/git/diff-renderer/pierre-diff.tsx +2 -1
  25. package/src/ui/components/git/pane/git-pane-header.tsx +105 -39
  26. package/src/ui/components/git/pane/git-pane-widget.tsx +30 -16
  27. package/src/ui/components/git/pane/pr-checks-panel.tsx +197 -0
  28. package/src/ui/components/git/pane/pr-state-row.tsx +103 -0
  29. package/src/ui/components/layout/bar.tsx +191 -0
  30. package/src/ui/components/layout/top-tab-bar.tsx +3 -2
  31. package/src/ui/root.tsx +25 -109
  32. package/src/ui/widgets/registry.tsx +18 -0
  33. package/src/ui/widgets/widget-context-menu.ts +69 -0
  34. package/src/ui/components/git/pane/git-pane-context-menu.ts +0 -26
  35. package/src/ui/components/layout/sidebar/sidebar.tsx +0 -163
package/src/ui/root.tsx CHANGED
@@ -6,6 +6,7 @@ import type { MeasuredPaneRect } from '../app-runtime/use-pane-size-report'
6
6
  import type { WorktreeTemplate } from '../config'
7
7
  import type { TerminalContentOrigin } from '../input/raw-input-handler'
8
8
  import type {
9
+ BarSide,
9
10
  FocusMode,
10
11
  ModalState,
11
12
  SessionRecord,
@@ -16,13 +17,10 @@ import type { ThemeId } from './themes'
16
17
 
17
18
  import { useWorktreeBranchPolling } from '../git/worktree-branch-poller'
18
19
  import { useAppStore } from '../state/app-store'
19
- import { dispatchGlobal } from '../state/dispatch-ref'
20
- import { getGitPaneWidthFromRatio } from '../state/git-pane-sizing'
20
+ import { getBarWidth } from '../state/bars'
21
21
  import { getTreeForTab, PANE_BORDER, type SplitDirection } from '../state/layout-tree'
22
22
  import { GitView } from './components/git/git-view'
23
- import { buildGitPaneContextMenu } from './components/git/pane/git-pane-context-menu'
24
- import { GitPaneWidget } from './components/git/pane/git-pane-widget'
25
- import { Sidebar } from './components/layout/sidebar/sidebar'
23
+ import { Bar, type BarBoundaryResizeInfo } from './components/layout/bar'
26
24
  import { SplitLayout } from './components/layout/split-layout'
27
25
  import { StatusBar } from './components/layout/status-bar'
28
26
  import { TerminalPane } from './components/layout/terminal-pane'
@@ -41,7 +39,6 @@ import { NewTabModal } from './components/modals/tabs/new-tab-modal'
41
39
  import { ThemePickerModal } from './components/modals/themes/theme-picker-modal'
42
40
  import { WorktreeMoveConfirmModal } from './components/modals/worktree/worktree-move-confirm-modal'
43
41
  import { WorktreeMoveModal } from './components/modals/worktree/worktree-move-modal'
44
- import { ContextMenuBox } from './components/overlays/context-menu/context-menu-box'
45
42
  import { ContextMenuOverlay } from './components/overlays/context-menu/context-menu-overlay'
46
43
  import { PendingChordOverlay } from './components/overlays/pending-chord-overlay'
47
44
  import { ToastViewport } from './components/overlays/toast/toast-viewport'
@@ -289,17 +286,8 @@ interface RootViewProps {
289
286
  onTerminalMouseUp?: (event: MouseEvent) => boolean
290
287
  onPaneActivate?: (tabId: string) => void
291
288
  onSplitResize?: (tabId: string, ratio: number, axis: SplitDirection) => void
292
- onSidebarResizeStart?: (info: { initialWidth: number; screenStart: number }) => void
293
- onGitPaneResizeStart?: (info: {
294
- initialWidth: number
295
- screenStart: number
296
- side: 'left' | 'right'
297
- }) => void
298
- onEmbeddedGitResizeStart?: (info: {
299
- containerStart: number
300
- position: 'top' | 'bottom'
301
- totalSize: number
302
- }) => void
289
+ onBarResizeStart?: (info: { initialWidth: number; screenStart: number; side: BarSide }) => void
290
+ onBarBoundaryResizeStart?: (info: BarBoundaryResizeInfo) => void
303
291
  onSeparatorDragStart?: (info: {
304
292
  tabId: string
305
293
  direction: SplitDirection
@@ -317,14 +305,13 @@ export function RootView({
317
305
  contentOrigin,
318
306
  localScrollbackEnabled,
319
307
  mouseForwardingEnabled,
320
- onEmbeddedGitResizeStart,
321
- onGitPaneResizeStart,
308
+ onBarBoundaryResizeStart,
309
+ onBarResizeStart,
322
310
  onMeasure,
323
311
  onPaneActivate,
324
312
  onSeparatorDrag,
325
313
  onSeparatorDragEnd,
326
314
  onSeparatorDragStart,
327
- onSidebarResizeStart,
328
315
  onSplitResize,
329
316
  onTerminalClick,
330
317
  onTerminalDrag,
@@ -349,31 +336,25 @@ export function RootView({
349
336
  const currentSessionId = useAppStore((s) => s.currentSessionId)
350
337
  const worktreeDivergence = useAppStore((s) => s.worktreeDivergence)
351
338
  const worktreeTemplates = useAppStore((s) => s.worktreeTemplates)
352
- const gitPaneMode = useAppStore((s) => s.gitPane.mode)
353
- const gitPaneVisible = useAppStore((s) => s.gitPane.visible)
354
- const gitPanePosition = useAppStore((s) => s.gitPane.position)
355
- const gitPaneRatio = useAppStore((s) => s.gitPane.paneRatio)
356
- const sidebarWidth = useAppStore((s) => s.sidebar.width)
357
- const sidebarVisible = useAppStore((s) => s.sidebar.visible)
339
+ const leftBarWidth = useAppStore((s) => getBarWidth(s.bars.left))
358
340
 
359
- const gitPaneInPaneOnLeft = gitPaneMode === 'pane' && gitPaneVisible && gitPanePosition === 'left'
360
341
  const splitChrome = PANE_BORDER * 2
361
342
 
362
343
  // Keep every worktree's `branch` in state synchronized with the on-disk
363
344
  // HEAD so the sidebar (and divergence calc) never reads a stale value.
364
345
  useWorktreeBranchPolling(true)
365
346
 
366
- const handleSidebarEdgeResize = useCallback(
347
+ // The terminal's own left edge stays a drag target for the left bar, so the
348
+ // resize gesture works from either side of the seam.
349
+ const handleLeftBarEdgeResize = useCallback(
367
350
  (event: MouseEvent): boolean => {
368
- onSidebarResizeStart?.({ initialWidth: sidebarWidth, screenStart: event.x })
351
+ onBarResizeStart?.({ initialWidth: leftBarWidth, screenStart: event.x, side: 'left' })
369
352
  return true
370
353
  },
371
- [onSidebarResizeStart, sidebarWidth]
354
+ [leftBarWidth, onBarResizeStart]
372
355
  )
373
356
  const handleTerminalLeftEdgeMouseDown =
374
- sidebarVisible && !gitPaneInPaneOnLeft && onSidebarResizeStart
375
- ? handleSidebarEdgeResize
376
- : undefined
357
+ leftBarWidth > 0 && onBarResizeStart ? handleLeftBarEdgeResize : undefined
377
358
 
378
359
  const handleRootMouseDrag = useCallback(
379
360
  (event: MouseEvent) => {
@@ -460,21 +441,16 @@ export function RootView({
460
441
  onMouseUp={handleRootMouseUp}
461
442
  >
462
443
  <box flexDirection="row" gap={0} padding={0} flexGrow={1}>
463
- <Sidebar
464
- onEmbeddedGitResizeStart={onEmbeddedGitResizeStart}
444
+ <Bar
445
+ side="left"
446
+ onBoundaryResizeStart={onBarBoundaryResizeStart}
447
+ onEdgeResizeStart={onBarResizeStart}
465
448
  onResizeDrag={onSeparatorDrag}
466
449
  onResizeDragEnd={onSeparatorDragEnd}
467
450
  />
468
451
  <box flexDirection="column" flexGrow={1}>
469
452
  <TopTabBar />
470
453
  <box flexDirection="row" gap={0} padding={0} flexGrow={1}>
471
- {gitPaneMode === 'pane' && gitPaneVisible && gitPanePosition === 'left' ? (
472
- <GitPaneInPaneMode
473
- position="left"
474
- ratio={gitPaneRatio}
475
- onGitPaneResizeStart={onGitPaneResizeStart}
476
- />
477
- ) : null}
478
454
  {activeTree && activeTree.type === 'split' ? (
479
455
  <SplitLayout
480
456
  node={activeTree}
@@ -517,15 +493,15 @@ export function RootView({
517
493
  onMeasure={onMeasure}
518
494
  />
519
495
  )}
520
- {gitPaneMode === 'pane' && gitPaneVisible && gitPanePosition === 'right' ? (
521
- <GitPaneInPaneMode
522
- position="right"
523
- ratio={gitPaneRatio}
524
- onGitPaneResizeStart={onGitPaneResizeStart}
525
- />
526
- ) : null}
527
496
  </box>
528
497
  </box>
498
+ <Bar
499
+ side="right"
500
+ onBoundaryResizeStart={onBarBoundaryResizeStart}
501
+ onEdgeResizeStart={onBarResizeStart}
502
+ onResizeDrag={onSeparatorDrag}
503
+ onResizeDragEnd={onSeparatorDragEnd}
504
+ />
529
505
  </box>
530
506
  <StatusBar />
531
507
  <PendingChordOverlay />
@@ -547,63 +523,3 @@ export function RootView({
547
523
  </box>
548
524
  )
549
525
  }
550
-
551
- function GitPaneInPaneMode({
552
- onGitPaneResizeStart,
553
- position,
554
- ratio,
555
- }: {
556
- ratio: number
557
- position: 'left' | 'right'
558
- onGitPaneResizeStart?: (info: {
559
- initialWidth: number
560
- screenStart: number
561
- side: 'left' | 'right'
562
- }) => void
563
- }) {
564
- const tokens = useTheme()
565
- const bg = tokens.backgroundPanel
566
- const gitPane = useAppStore((s) => s.gitPane)
567
- const width = getGitPaneWidthFromRatio(ratio)
568
- const contentWidth = Math.max(1, width - 1)
569
- const gitPaneMenu = buildGitPaneContextMenu(
570
- gitPane,
571
- () => dispatchGlobal({ type: 'toggle-git-pane' }),
572
- (mode, nextPosition) => {
573
- dispatchGlobal({ mode, type: 'set-git-pane-mode' })
574
- dispatchGlobal({ position: nextPosition, type: 'set-git-pane-position' })
575
- }
576
- )
577
- const handleResizeMouseDown = useCallback(
578
- (event: MouseEvent) => {
579
- event.preventDefault()
580
- event.stopPropagation()
581
- onGitPaneResizeStart?.({ initialWidth: width, screenStart: event.x, side: position })
582
- },
583
- [onGitPaneResizeStart, position, width]
584
- )
585
- const handle = (
586
- <box
587
- width={1}
588
- flexShrink={0}
589
- backgroundColor={tokens.border}
590
- onMouseDown={handleResizeMouseDown}
591
- />
592
- )
593
- return (
594
- <box flexDirection="column" width={width} flexShrink={0} backgroundColor={bg} overflow="hidden">
595
- <box flexDirection="row" flexGrow={1} overflow="hidden">
596
- {position === 'right' ? handle : null}
597
- <ContextMenuBox
598
- width={contentWidth}
599
- flexGrow={1}
600
- overflow="hidden"
601
- rightClickMenu={gitPaneMenu}
602
- >
603
- <GitPaneWidget pollingEnabled />
604
- </ContextMenuBox>
605
- {position === 'left' ? handle : null}
606
- </box>
607
- </box>
608
- )
609
- }
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import { GitPaneWidget } from '../components/git/pane/git-pane-widget'
4
+ import { WorkspaceList } from '../components/layout/sidebar/workspace-list'
5
+
6
+ /**
7
+ * Everything a bar can host. Adding a widget = one entry here plus its id in
8
+ * `KNOWN_WIDGET_IDS` (src/state/bars.ts) and in the default layout.
9
+ */
10
+ export const WIDGET_RENDERERS: Record<string, (contentWidth: number) => ReactNode> = {
11
+ git: (contentWidth) => <GitPaneWidget contentWidth={contentWidth} pollingEnabled />,
12
+ workspaces: (contentWidth) => <WorkspaceList contentWidth={contentWidth} />,
13
+ }
14
+
15
+ export const WIDGET_LABELS: Record<string, string> = {
16
+ git: 'Git',
17
+ workspaces: 'Workspaces',
18
+ }
@@ -0,0 +1,69 @@
1
+ import type { BarSide, BarsState } from '../../state/types'
2
+ import type { ContextMenuItem } from '../context-menu/controller'
3
+
4
+ import { visibleWidgets } from '../../state/bars'
5
+ import { dispatchGlobal } from '../../state/dispatch-ref'
6
+ import { WIDGET_LABELS } from './registry'
7
+
8
+ /**
9
+ * Right-click menu for a widget hosted in a bar: move it across, reorder it
10
+ * within its bar, or hide it. Generic — a new widget gets this for free.
11
+ */
12
+ export function buildWidgetContextMenu(
13
+ bars: BarsState,
14
+ side: BarSide,
15
+ widgetId: string
16
+ ): ContextMenuItem[] {
17
+ const widgets = bars[side].widgets
18
+ const index = widgets.findIndex((widget) => widget.id === widgetId)
19
+ if (index === -1) return []
20
+
21
+ const other: BarSide = side === 'left' ? 'right' : 'left'
22
+ const items: ContextMenuItem[] = [
23
+ [
24
+ `Move to ${other} bar`,
25
+ () =>
26
+ dispatchGlobal({
27
+ index: bars[other].widgets.length,
28
+ side: other,
29
+ type: 'move-widget',
30
+ widgetId,
31
+ }),
32
+ ],
33
+ ]
34
+
35
+ if (index > 0) {
36
+ items.push([
37
+ 'Move up',
38
+ () => dispatchGlobal({ index: index - 1, side, type: 'move-widget', widgetId }),
39
+ ])
40
+ }
41
+ if (index < widgets.length - 1) {
42
+ items.push([
43
+ 'Move down',
44
+ () => dispatchGlobal({ index: index + 1, side, type: 'move-widget', widgetId }),
45
+ ])
46
+ }
47
+
48
+ // Hiding the last visible widget would collapse the bar to zero width and
49
+ // take its own context menu with it — leaving no way back.
50
+ if (visibleWidgets(bars[side]).length > 1) {
51
+ items.push(['Hide', () => dispatchGlobal({ type: 'toggle-widget', widgetId })])
52
+ }
53
+ return items
54
+ }
55
+
56
+ /** Menu for the bar itself. Offers a way back for every hidden widget. */
57
+ export function buildBarContextMenu(bars: BarsState, side: BarSide): ContextMenuItem[] {
58
+ const items: ContextMenuItem[] = [
59
+ [`Hide ${side} bar`, () => dispatchGlobal({ side, type: 'toggle-bar' })],
60
+ ]
61
+ for (const widget of bars[side].widgets) {
62
+ if (widget.visible) continue
63
+ items.push([
64
+ `Show ${WIDGET_LABELS[widget.id] ?? widget.id}`,
65
+ () => dispatchGlobal({ type: 'toggle-widget', widgetId: widget.id }),
66
+ ])
67
+ }
68
+ return items
69
+ }
@@ -1,26 +0,0 @@
1
- import type { GitPaneMode, GitPanePosition, GitPaneState } from '../../../../state/types'
2
- import type { ContextMenuItem } from '../../../context-menu/controller'
3
-
4
- const GIT_PANE_MENU_OPTIONS: {
5
- label: string
6
- mode: GitPaneMode
7
- position: GitPanePosition
8
- }[] = [
9
- { label: 'Move to top', mode: 'embedded', position: 'top' },
10
- { label: 'Move to bottom', mode: 'embedded', position: 'bottom' },
11
- { label: 'Move to left', mode: 'pane', position: 'left' },
12
- { label: 'Move to right', mode: 'pane', position: 'right' },
13
- ]
14
-
15
- export function buildGitPaneContextMenu(
16
- gitPane: Pick<GitPaneState, 'mode' | 'position'>,
17
- onToggle: () => void,
18
- onMove: (mode: GitPaneMode, position: GitPanePosition) => void
19
- ): ContextMenuItem[] {
20
- return [
21
- ['Toggle', onToggle],
22
- ...GIT_PANE_MENU_OPTIONS.filter(
23
- ({ mode, position }) => !(gitPane.mode === mode && gitPane.position === position)
24
- ).map(({ label, mode, position }) => [label, () => onMove(mode, position)] as ContextMenuItem),
25
- ]
26
- }
@@ -1,163 +0,0 @@
1
- import type { BoxRenderable, MouseEvent as OtuiMouseEvent } from '@opentui/core'
2
-
3
- import { useCallback, useMemo, useRef } from 'react'
4
-
5
- import { useAppStore } from '../../../../state/app-store'
6
- import { dispatchGlobal } from '../../../../state/dispatch-ref'
7
- import { useTheme } from '../../../theme'
8
- import { buildGitPaneContextMenu } from '../../git/pane/git-pane-context-menu'
9
- import { GitPaneWidget } from '../../git/pane/git-pane-widget'
10
- import { ContextMenuBox } from '../../overlays/context-menu/context-menu-box'
11
- import { WorkspaceList } from './workspace-list'
12
-
13
- interface SidebarProps {
14
- onResizeDrag?: (event: OtuiMouseEvent) => boolean
15
- onResizeDragEnd?: () => void
16
- onEmbeddedGitResizeStart?: (info: {
17
- containerStart: number
18
- position: 'top' | 'bottom'
19
- totalSize: number
20
- }) => void
21
- }
22
-
23
- const RESIZE_HANDLE = '─'
24
-
25
- export function Sidebar({ onEmbeddedGitResizeStart, onResizeDrag, onResizeDragEnd }: SidebarProps) {
26
- const t = useTheme()
27
- const sidebarBg = t.background
28
- const sidebarVisible = useAppStore((s) => s.sidebar.visible)
29
- const sidebarWidth = useAppStore((s) => s.sidebar.width)
30
- const gitPane = useAppStore((s) => s.gitPane)
31
- const focusMode = useAppStore((s) => s.focusMode)
32
- const bodyRef = useRef<BoxRenderable | null>(null)
33
-
34
- const handleSidebarMouseDown = useCallback(() => {
35
- if (focusMode === 'terminal-input') {
36
- dispatchGlobal({ focusMode: 'navigation', type: 'set-focus-mode' })
37
- }
38
- }, [focusMode])
39
- const handleSidebarMouseDrag = useCallback(
40
- (event: OtuiMouseEvent) => {
41
- if (onResizeDrag?.(event) === true) {
42
- event.preventDefault()
43
- event.stopPropagation()
44
- }
45
- },
46
- [onResizeDrag]
47
- )
48
- const handleSidebarMouseUp = useCallback(() => {
49
- onResizeDragEnd?.()
50
- }, [onResizeDragEnd])
51
- const handleEmbeddedHandleMouseDown = useCallback(
52
- (event: OtuiMouseEvent) => {
53
- const body = bodyRef.current
54
- if (!body) return
55
- event.preventDefault()
56
- event.stopPropagation()
57
- onEmbeddedGitResizeStart?.({
58
- containerStart: body.y,
59
- position: gitPane.position === 'top' ? 'top' : 'bottom',
60
- totalSize: Math.max(1, body.height),
61
- })
62
- },
63
- [gitPane.position, onEmbeddedGitResizeStart]
64
- )
65
- const sidebarMenu = useMemo<[string, () => void][]>(
66
- () => [
67
- ['Hide sidebar', () => dispatchGlobal({ type: 'toggle-sidebar' })],
68
- ['Toggle git pane', () => dispatchGlobal({ type: 'toggle-git-pane' })],
69
- [
70
- 'Toggle diff mode',
71
- () => {
72
- dispatchGlobal({ type: 'enter-git-mode' })
73
- dispatchGlobal({ type: 'git-mode-toggle-diff-view' })
74
- },
75
- ],
76
- ],
77
- []
78
- )
79
-
80
- if (!sidebarVisible) {
81
- return null
82
- }
83
-
84
- const gitEmbedded = gitPane.mode === 'embedded' && gitPane.visible
85
- const gitOnTop = gitEmbedded && gitPane.position === 'top'
86
- const gitOnBottom = gitEmbedded && gitPane.position === 'bottom'
87
- const contentWidth = Math.max(1, sidebarWidth - 1)
88
- const gitPaneMenu = buildGitPaneContextMenu(
89
- gitPane,
90
- () => dispatchGlobal({ type: 'toggle-git-pane' }),
91
- (mode, position) => {
92
- dispatchGlobal({ mode, type: 'set-git-pane-mode' })
93
- dispatchGlobal({ position, type: 'set-git-pane-position' })
94
- }
95
- )
96
-
97
- // flex-grow scaled by 100 (integer preferred); workspace list gets (1-ratio), git gets ratio.
98
- const listGrow = gitEmbedded ? Math.max(1, Math.round((1 - gitPane.embeddedRatio) * 100)) : 1
99
- const gitGrow = gitEmbedded ? Math.max(1, Math.round(gitPane.embeddedRatio * 100)) : 0
100
-
101
- const gitBody = gitEmbedded ? (
102
- <ContextMenuBox
103
- flexDirection="column"
104
- flexGrow={gitGrow}
105
- flexShrink={1}
106
- flexBasis={0}
107
- overflow="hidden"
108
- rightClickMenu={gitPaneMenu}
109
- >
110
- <GitPaneWidget pollingEnabled={gitPane.visible} />
111
- </ContextMenuBox>
112
- ) : null
113
- const embeddedHandle = gitEmbedded ? (
114
- <box minHeight={1} flexShrink={0} onMouseDown={handleEmbeddedHandleMouseDown}>
115
- <text fg={t.border} selectable={false}>
116
- {RESIZE_HANDLE.repeat(Math.max(1, contentWidth))}
117
- </text>
118
- </box>
119
- ) : null
120
-
121
- return (
122
- <ContextMenuBox
123
- width={sidebarWidth}
124
- padding={0}
125
- flexDirection="column"
126
- backgroundColor={sidebarBg}
127
- gap={0}
128
- overflow="hidden"
129
- rightClickMenu={sidebarMenu}
130
- onMouseDown={handleSidebarMouseDown}
131
- onMouseDrag={handleSidebarMouseDrag}
132
- onMouseUp={handleSidebarMouseUp}
133
- >
134
- <box flexDirection="row" width={sidebarWidth} flexGrow={1} overflow="hidden">
135
- <box width={contentWidth} flexGrow={1} flexDirection="column" overflow="hidden">
136
- <box ref={bodyRef} flexDirection="column" flexGrow={1} overflow="hidden">
137
- {gitOnTop ? (
138
- <>
139
- {gitBody}
140
- {embeddedHandle}
141
- </>
142
- ) : null}
143
- <box
144
- flexDirection="column"
145
- flexGrow={listGrow}
146
- flexShrink={1}
147
- flexBasis={0}
148
- overflow="hidden"
149
- >
150
- <WorkspaceList contentWidth={contentWidth} />
151
- </box>
152
- {gitOnBottom ? (
153
- <>
154
- {embeddedHandle}
155
- {gitBody}
156
- </>
157
- ) : null}
158
- </box>
159
- </box>
160
- </box>
161
- </ContextMenuBox>
162
- )
163
- }