@brimveyn/aimux 1.14.14 → 1.14.16
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/package.json +2 -2
- package/src/app-runtime/side-effects.ts +242 -38
- package/src/app.tsx +30 -1
- package/src/config.ts +101 -0
- package/src/git/worktree.ts +45 -0
- package/src/input/modes/bridge.ts +4 -0
- package/src/input/modes/transitions.ts +8 -1
- package/src/input/modes/types.ts +15 -1
- package/src/state/reducers/modal-state.ts +232 -33
- package/src/state/reducers/session-state.ts +11 -19
- package/src/state/reducers/tab-state.ts +17 -0
- package/src/state/selectors.ts +55 -1
- package/src/state/session-persistence.ts +23 -1
- package/src/state/store.ts +4 -0
- package/src/state/types.ts +45 -8
- package/src/ui/components/layout/sidebar/worktree-row.tsx +12 -2
- package/src/ui/components/layout/top-tab-bar.tsx +146 -12
- package/src/ui/components/modals/shared/worktree-delete-confirm.tsx +39 -0
- package/src/ui/components/modals/tabs/new-tab-modal.tsx +181 -44
- package/src/ui/root.tsx +21 -2
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react'
|
|
2
2
|
|
|
3
|
+
import type { WorktreeTemplate } from '../../../../config'
|
|
3
4
|
import type { AssistantId, WorktreeRecord } from '../../../../state/types'
|
|
4
5
|
|
|
5
6
|
import { getAllAssistantOptions, getAssistantOption } from '../../../../pty/command-registry'
|
|
6
7
|
import { dispatchGlobal, runSideEffectGlobal } from '../../../../state/dispatch-ref'
|
|
7
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
buildBaseRefOptions,
|
|
10
|
+
filterAssistants,
|
|
11
|
+
getTemplateNoneOffset,
|
|
12
|
+
} from '../../../../state/selectors'
|
|
8
13
|
import { useTheme } from '../../../theme'
|
|
9
14
|
import { uiTokens } from '../../../ui-tokens'
|
|
10
|
-
import { Form, TextField } from '../shared/form'
|
|
15
|
+
import { AutoComplete, Form, type FormOptionItem, TextField } from '../shared/form'
|
|
11
16
|
import { Picker, type PickerItem } from '../shared/picker'
|
|
17
|
+
import { WorktreeDeleteConfirm } from '../shared/worktree-delete-confirm'
|
|
12
18
|
|
|
13
19
|
interface NewTabModalProps {
|
|
14
20
|
selectedIndex: number
|
|
@@ -18,18 +24,29 @@ interface NewTabModalProps {
|
|
|
18
24
|
currentSessionId: string | null
|
|
19
25
|
editingCommand: AssistantId | null
|
|
20
26
|
editBuffer: string
|
|
21
|
-
activeField: 'assistant' | 'branch-name' | 'target-worktree' | 'worktree-name'
|
|
27
|
+
activeField: 'assistant' | 'branch-name' | 'target-worktree' | 'worktree-name' | 'base'
|
|
22
28
|
branchError: string | null
|
|
23
29
|
branchName: string
|
|
24
30
|
createWorktree: boolean
|
|
25
31
|
selectedAssistantId: AssistantId | null
|
|
26
|
-
step: 'assistant' | 'worktree' | 'worktree-create'
|
|
27
|
-
|
|
28
|
-
worktreeDeleteMessage: string | null
|
|
32
|
+
step: 'assistant' | 'worktree' | 'worktree-create' | 'template'
|
|
33
|
+
worktreeDeletePrompt: { worktreeId: string; reason: string } | null
|
|
29
34
|
worktrees: WorktreeRecord[]
|
|
30
35
|
worktreeName: string
|
|
36
|
+
baseQuery: string
|
|
37
|
+
baseRef: string
|
|
38
|
+
baseBranches: string[]
|
|
39
|
+
worktreeTemplates: WorktreeTemplate[]
|
|
40
|
+
/**
|
|
41
|
+
* True when this modal is the worktree-aware new-tab flow. False when the
|
|
42
|
+
* component is reused for `split-picker`, which can only choose an
|
|
43
|
+
* assistant — the template shortcut entry must be hidden there.
|
|
44
|
+
*/
|
|
45
|
+
allowTemplateShortcut: boolean
|
|
31
46
|
}
|
|
32
47
|
|
|
48
|
+
const TEMPLATE_SHORTCUT_KEY = '__template-shortcut__'
|
|
49
|
+
|
|
33
50
|
function getDeleteBlockedReason({
|
|
34
51
|
currentSessionId,
|
|
35
52
|
worktree,
|
|
@@ -52,6 +69,10 @@ function getDeleteBlockedReason({
|
|
|
52
69
|
|
|
53
70
|
export function NewTabModal({
|
|
54
71
|
activeField,
|
|
72
|
+
allowTemplateShortcut,
|
|
73
|
+
baseBranches,
|
|
74
|
+
baseQuery,
|
|
75
|
+
baseRef,
|
|
55
76
|
branchError,
|
|
56
77
|
branchName,
|
|
57
78
|
createWorktree,
|
|
@@ -64,10 +85,10 @@ export function NewTabModal({
|
|
|
64
85
|
selectedAssistantId,
|
|
65
86
|
selectedIndex,
|
|
66
87
|
step,
|
|
67
|
-
|
|
68
|
-
worktreeDeleteMessage,
|
|
88
|
+
worktreeDeletePrompt,
|
|
69
89
|
worktreeName,
|
|
70
90
|
worktrees,
|
|
91
|
+
worktreeTemplates,
|
|
71
92
|
}: NewTabModalProps) {
|
|
72
93
|
const t = useTheme()
|
|
73
94
|
const options = useMemo(() => getAllAssistantOptions(customCommands), [customCommands])
|
|
@@ -95,9 +116,8 @@ export function NewTabModal({
|
|
|
95
116
|
active && canDelete && currentSessionId != null && currentSessionId !== ''
|
|
96
117
|
? () => {
|
|
97
118
|
dispatchGlobal({ index, type: 'set-modal-selection-index' })
|
|
98
|
-
dispatchGlobal({ message: null, type: 'set-new-tab-worktree-delete-state' })
|
|
99
119
|
runSideEffectGlobal({
|
|
100
|
-
force:
|
|
120
|
+
force: false,
|
|
101
121
|
sessionId: currentSessionId,
|
|
102
122
|
type: 'delete-worktree',
|
|
103
123
|
worktreeId: worktree.id,
|
|
@@ -115,37 +135,114 @@ export function NewTabModal({
|
|
|
115
135
|
}),
|
|
116
136
|
{
|
|
117
137
|
key: '__create-worktree__',
|
|
118
|
-
onClick: () =>
|
|
138
|
+
onClick: () => {
|
|
139
|
+
dispatchGlobal({ type: 'enter-new-tab-worktree-create' })
|
|
140
|
+
runSideEffectGlobal({ type: 'load-new-tab-base-branches' })
|
|
141
|
+
},
|
|
119
142
|
subtitle: <text fg={t.textMuted}>Create an Aimux temp worktree</text>,
|
|
120
143
|
title: <text fg={createWorktree ? t.text : t.textMuted}>Create new worktree</text>,
|
|
121
144
|
},
|
|
122
145
|
],
|
|
123
|
-
[createWorktree, currentSessionId, selectedIndex, t,
|
|
146
|
+
[createWorktree, currentSessionId, selectedIndex, t, worktrees]
|
|
124
147
|
)
|
|
125
148
|
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
149
|
+
const noneOffset = getTemplateNoneOffset(selectedAssistantId)
|
|
150
|
+
const showNoneOption = noneOffset === 1
|
|
151
|
+
const templateItems = useMemo<PickerItem[]>(() => {
|
|
152
|
+
const noneItem: PickerItem[] = showNoneOption
|
|
153
|
+
? [
|
|
154
|
+
{
|
|
155
|
+
key: '__none__',
|
|
156
|
+
onClick: () => {
|
|
157
|
+
dispatchGlobal({ index: 0, type: 'set-modal-selection-index' })
|
|
158
|
+
runSideEffectGlobal({ type: 'launch-selected-assistant' })
|
|
159
|
+
},
|
|
160
|
+
subtitle: <text fg={t.textMuted}>Single pane — current assistant only</text>,
|
|
161
|
+
title: <text fg={selectedIndex === 0 ? t.text : t.textMuted}>None</text>,
|
|
162
|
+
},
|
|
163
|
+
]
|
|
164
|
+
: []
|
|
165
|
+
return [
|
|
166
|
+
...noneItem,
|
|
167
|
+
...worktreeTemplates.map((template, idx) => {
|
|
168
|
+
const itemIndex = idx + noneOffset
|
|
169
|
+
const active = itemIndex === selectedIndex
|
|
170
|
+
const tabCount = template.tabs.length
|
|
171
|
+
const paneCount = template.tabs.reduce((sum, tab) => sum + tab.panes.length, 0)
|
|
131
172
|
return {
|
|
132
|
-
key:
|
|
133
|
-
onClick: () =>
|
|
134
|
-
dispatchGlobal({
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
subtitle:
|
|
138
|
-
|
|
139
|
-
<text fg={t.textMuted}>{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
173
|
+
key: template.id,
|
|
174
|
+
onClick: () => {
|
|
175
|
+
dispatchGlobal({ index: itemIndex, type: 'set-modal-selection-index' })
|
|
176
|
+
runSideEffectGlobal({ type: 'launch-selected-assistant' })
|
|
177
|
+
},
|
|
178
|
+
subtitle:
|
|
179
|
+
template.description != null && template.description !== '' ? (
|
|
180
|
+
<text fg={t.textMuted}>{template.description}</text>
|
|
181
|
+
) : (
|
|
182
|
+
<text fg={t.textMuted}>
|
|
183
|
+
{tabCount} tab{tabCount === 1 ? '' : 's'}, {paneCount} pane
|
|
184
|
+
{paneCount === 1 ? '' : 's'}
|
|
185
|
+
</text>
|
|
186
|
+
),
|
|
187
|
+
title: <text fg={active ? t.text : t.textMuted}>{template.name}</text>,
|
|
146
188
|
}
|
|
147
189
|
}),
|
|
148
|
-
|
|
190
|
+
]
|
|
191
|
+
}, [noneOffset, selectedIndex, showNoneOption, t, worktreeTemplates])
|
|
192
|
+
|
|
193
|
+
const showShortcutEntry = allowTemplateShortcut && worktreeTemplates.length > 0
|
|
194
|
+
const items = useMemo<PickerItem[]>(() => {
|
|
195
|
+
const assistantItems: PickerItem[] = filtered.map((option, index) => {
|
|
196
|
+
const active = index === selectedIndex
|
|
197
|
+
const customCmd = customCommands[option.id]
|
|
198
|
+
return {
|
|
199
|
+
key: option.id,
|
|
200
|
+
onClick: () => dispatchGlobal({ assistantId: option.id, type: 'select-new-tab-assistant' }),
|
|
201
|
+
onEdit: () => dispatchGlobal({ assistantId: option.id, type: 'open-edit-custom-command' }),
|
|
202
|
+
subtitle: (
|
|
203
|
+
<box flexDirection="column">
|
|
204
|
+
<text fg={t.textMuted}>{option.description}</text>
|
|
205
|
+
{customCmd != null && customCmd !== '' ? <text fg={t.primary}>{customCmd}</text> : null}
|
|
206
|
+
</box>
|
|
207
|
+
),
|
|
208
|
+
title: <text fg={active ? t.text : t.textMuted}>{option.label}</text>,
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
if (!showShortcutEntry) return assistantItems
|
|
212
|
+
const shortcutIndex = filtered.length
|
|
213
|
+
const shortcutActive = shortcutIndex === selectedIndex
|
|
214
|
+
return [
|
|
215
|
+
...assistantItems,
|
|
216
|
+
{
|
|
217
|
+
key: TEMPLATE_SHORTCUT_KEY,
|
|
218
|
+
onClick: () => {
|
|
219
|
+
dispatchGlobal({ index: shortcutIndex, type: 'set-modal-selection-index' })
|
|
220
|
+
dispatchGlobal({ type: 'enter-new-tab-template-shortcut' })
|
|
221
|
+
},
|
|
222
|
+
subtitle: (
|
|
223
|
+
<text fg={t.textMuted}>Create a worktree and pick a template — no assistant step</text>
|
|
224
|
+
),
|
|
225
|
+
title: <text fg={shortcutActive ? t.text : t.textMuted}>Worktree from template…</text>,
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
}, [customCommands, filtered, showShortcutEntry, selectedIndex, t])
|
|
229
|
+
|
|
230
|
+
const baseItems = useMemo<FormOptionItem[]>(
|
|
231
|
+
() =>
|
|
232
|
+
buildBaseRefOptions(worktrees, baseBranches, baseQuery).map((option) => ({
|
|
233
|
+
key: option.ref,
|
|
234
|
+
leading: (
|
|
235
|
+
<text fg={option.kind === 'worktree' ? t.warning : t.textMuted}>
|
|
236
|
+
{option.kind === 'worktree' ? '\u{e728}' : '\u{e702}'}
|
|
237
|
+
</text>
|
|
238
|
+
),
|
|
239
|
+
subtitle:
|
|
240
|
+
option.kind === 'worktree' ? (
|
|
241
|
+
<text fg={t.textMuted}>worktree: {option.detail}</text>
|
|
242
|
+
) : null,
|
|
243
|
+
title: (active) => <text fg={active ? t.text : t.textMuted}>{option.label}</text>,
|
|
244
|
+
})),
|
|
245
|
+
[baseBranches, baseQuery, t, worktrees]
|
|
149
246
|
)
|
|
150
247
|
|
|
151
248
|
if (editingCommand !== null) {
|
|
@@ -167,9 +264,32 @@ export function NewTabModal({
|
|
|
167
264
|
)
|
|
168
265
|
}
|
|
169
266
|
|
|
267
|
+
if (step === 'template') {
|
|
268
|
+
return (
|
|
269
|
+
<Picker
|
|
270
|
+
title="Pick worktree template"
|
|
271
|
+
keybindsModeId="modal.new-tab.command-edit"
|
|
272
|
+
width={uiTokens.modalWidth.md}
|
|
273
|
+
gap={1}
|
|
274
|
+
filter={null}
|
|
275
|
+
items={templateItems}
|
|
276
|
+
selectedIndex={selectedIndex}
|
|
277
|
+
emptyState={<text fg={t.textMuted}>No templates configured.</text>}
|
|
278
|
+
onHover={handleHover}
|
|
279
|
+
footer={
|
|
280
|
+
<box flexDirection="column">
|
|
281
|
+
<text fg={t.textMuted}>Step 4/4: choose template</text>
|
|
282
|
+
<text fg={t.textMuted}>Enter launches, Esc returns to worktree settings</text>
|
|
283
|
+
</box>
|
|
284
|
+
}
|
|
285
|
+
/>
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
|
|
170
289
|
if (step === 'worktree-create') {
|
|
171
290
|
const selectedAssistant =
|
|
172
291
|
options.find((option) => option.id === selectedAssistantId) ?? options[0]
|
|
292
|
+
const baseActive = activeField === 'base'
|
|
173
293
|
return (
|
|
174
294
|
<Form
|
|
175
295
|
title={`New worktree: ${selectedAssistant?.label ?? 'assistant'}`}
|
|
@@ -196,7 +316,20 @@ export function NewTabModal({
|
|
|
196
316
|
<text fg={t.error}>{branchError}</text>
|
|
197
317
|
) : null}
|
|
198
318
|
</box>
|
|
199
|
-
<
|
|
319
|
+
<AutoComplete
|
|
320
|
+
active={baseActive}
|
|
321
|
+
label="Base (fork from)"
|
|
322
|
+
placeholder="branch or worktree to fork from..."
|
|
323
|
+
value={baseQuery}
|
|
324
|
+
displayValue={baseRef !== '' ? baseRef : 'current branch'}
|
|
325
|
+
items={baseItems}
|
|
326
|
+
selectedIndex={selectedIndex}
|
|
327
|
+
cursorPos={baseActive ? cursorPos : undefined}
|
|
328
|
+
maxVisibleRows={6}
|
|
329
|
+
onHover={handleHover}
|
|
330
|
+
emptyState={<text fg={t.textMuted}>No branches found</text>}
|
|
331
|
+
/>
|
|
332
|
+
<text fg={t.textMuted}>Step 3/3: configure new worktree · Tab switches fields</text>
|
|
200
333
|
</box>
|
|
201
334
|
</Form>
|
|
202
335
|
)
|
|
@@ -207,6 +340,19 @@ export function NewTabModal({
|
|
|
207
340
|
options.find((option) => option.id === selectedAssistantId) ??
|
|
208
341
|
filtered[selectedIndex] ??
|
|
209
342
|
options[0]
|
|
343
|
+
|
|
344
|
+
if (worktreeDeletePrompt != null) {
|
|
345
|
+
const target = worktrees.find((worktree) => worktree.id === worktreeDeletePrompt.worktreeId)
|
|
346
|
+
const label = target?.branch ?? target?.name ?? 'this worktree'
|
|
347
|
+
return (
|
|
348
|
+
<WorktreeDeleteConfirm
|
|
349
|
+
keybindsModeId="modal.new-tab.worktree-delete-confirm"
|
|
350
|
+
reason={worktreeDeletePrompt.reason}
|
|
351
|
+
worktreeLabel={label}
|
|
352
|
+
/>
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
|
|
210
356
|
const selectedWorktree = worktrees[selectedIndex]
|
|
211
357
|
const deleteBlockedReason = getDeleteBlockedReason({
|
|
212
358
|
currentSessionId,
|
|
@@ -226,17 +372,8 @@ export function NewTabModal({
|
|
|
226
372
|
onHover={handleHover}
|
|
227
373
|
footer={
|
|
228
374
|
<box flexDirection="column">
|
|
229
|
-
{
|
|
230
|
-
|
|
231
|
-
<text
|
|
232
|
-
fg={
|
|
233
|
-
worktreeDeleteMessage != null && worktreeDeleteMessage !== ''
|
|
234
|
-
? t.error
|
|
235
|
-
: t.textMuted
|
|
236
|
-
}
|
|
237
|
-
>
|
|
238
|
-
{worktreeDeleteMessage ?? deleteBlockedReason}
|
|
239
|
-
</text>
|
|
375
|
+
{deleteBlockedReason != null && deleteBlockedReason !== '' ? (
|
|
376
|
+
<text fg={t.textMuted}>{deleteBlockedReason}</text>
|
|
240
377
|
) : null}
|
|
241
378
|
<text fg={t.textMuted}>Step 2/2: choose worktree</text>
|
|
242
379
|
<text fg={t.textMuted}>Enter launches, Ctrl+d deletes selected worktree</text>
|
package/src/ui/root.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import type { MouseEvent } from '@opentui/core'
|
|
|
3
3
|
import { useCallback, useMemo } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { MeasuredPaneRect } from '../app-runtime/use-pane-size-report'
|
|
6
|
+
import type { WorktreeTemplate } from '../config'
|
|
6
7
|
import type { TerminalContentOrigin } from '../input/raw-input-handler'
|
|
7
8
|
import type {
|
|
8
9
|
FocusMode,
|
|
@@ -33,6 +34,7 @@ import { GitCommitModal } from './components/modals/git/git-commit-modal'
|
|
|
33
34
|
import { CreateSessionModal } from './components/modals/sessions/create-session-modal'
|
|
34
35
|
import { SessionNameModal } from './components/modals/sessions/session-name-modal'
|
|
35
36
|
import { SessionPickerModal } from './components/modals/sessions/session-picker-modal'
|
|
37
|
+
import { WorktreeDeleteConfirm } from './components/modals/shared/worktree-delete-confirm'
|
|
36
38
|
import { SnippetEditorModal } from './components/modals/snippets/snippet-editor-modal'
|
|
37
39
|
import { SnippetPickerModal } from './components/modals/snippets/snippet-picker-modal'
|
|
38
40
|
import { NewTabModal } from './components/modals/tabs/new-tab-modal'
|
|
@@ -45,6 +47,7 @@ import { ToastViewport } from './components/overlays/toast/toast-viewport'
|
|
|
45
47
|
import { useTheme } from './theme'
|
|
46
48
|
|
|
47
49
|
const EMPTY_WORKTREES: WorktreeRecord[] = []
|
|
50
|
+
const EMPTY_BASE_BRANCHES: string[] = []
|
|
48
51
|
|
|
49
52
|
function getCreateSessionFields(modal: ModalState) {
|
|
50
53
|
if (modal.type !== 'create-session') {
|
|
@@ -92,6 +95,7 @@ function renderModal(
|
|
|
92
95
|
activeAssistant?: string
|
|
93
96
|
autoCommitModel?: string
|
|
94
97
|
worktreeDivergence: Record<string, { ahead: number; behind: number }>
|
|
98
|
+
worktreeTemplates: WorktreeTemplate[]
|
|
95
99
|
}
|
|
96
100
|
) {
|
|
97
101
|
switch (modal.type) {
|
|
@@ -112,8 +116,10 @@ function renderModal(
|
|
|
112
116
|
createWorktree={modal.type === 'new-tab' ? modal.createWorktree : false}
|
|
113
117
|
selectedAssistantId={modal.type === 'new-tab' ? modal.selectedAssistantId : null}
|
|
114
118
|
step={modal.type === 'new-tab' ? modal.step : 'assistant'}
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
baseQuery={modal.type === 'new-tab' ? modal.baseQuery : ''}
|
|
120
|
+
baseRef={modal.type === 'new-tab' ? modal.baseRef : ''}
|
|
121
|
+
baseBranches={modal.type === 'new-tab' ? modal.baseBranches : EMPTY_BASE_BRANCHES}
|
|
122
|
+
worktreeDeletePrompt={modal.type === 'new-tab' ? modal.worktreeDeletePrompt : null}
|
|
117
123
|
worktrees={
|
|
118
124
|
options.currentSessionId != null && options.currentSessionId !== ''
|
|
119
125
|
? (options.sessions.find((session) => session.id === options.currentSessionId)
|
|
@@ -121,6 +127,8 @@ function renderModal(
|
|
|
121
127
|
: EMPTY_WORKTREES
|
|
122
128
|
}
|
|
123
129
|
worktreeName={modal.type === 'new-tab' ? modal.worktreeName : ''}
|
|
130
|
+
worktreeTemplates={options.worktreeTemplates}
|
|
131
|
+
allowTemplateShortcut={modal.type === 'new-tab'}
|
|
124
132
|
/>
|
|
125
133
|
)
|
|
126
134
|
case 'session-picker':
|
|
@@ -221,6 +229,14 @@ function renderModal(
|
|
|
221
229
|
)
|
|
222
230
|
case 'ai-usage':
|
|
223
231
|
return <AIUsageModal />
|
|
232
|
+
case 'worktree-delete-confirm':
|
|
233
|
+
return (
|
|
234
|
+
<WorktreeDeleteConfirm
|
|
235
|
+
keybindsModeId="modal.worktree-delete-confirm"
|
|
236
|
+
reason={modal.reason}
|
|
237
|
+
worktreeLabel={modal.worktreeLabel}
|
|
238
|
+
/>
|
|
239
|
+
)
|
|
224
240
|
case 'git-commit': {
|
|
225
241
|
const titleText =
|
|
226
242
|
modal.activeField === 'title' ? (modal.editBuffer ?? '') : modal.contentBuffer
|
|
@@ -316,6 +332,7 @@ export function RootView({
|
|
|
316
332
|
const sessions = useAppStore((s) => s.sessions)
|
|
317
333
|
const currentSessionId = useAppStore((s) => s.currentSessionId)
|
|
318
334
|
const worktreeDivergence = useAppStore((s) => s.worktreeDivergence)
|
|
335
|
+
const worktreeTemplates = useAppStore((s) => s.worktreeTemplates)
|
|
319
336
|
const gitPaneMode = useAppStore((s) => s.gitPane.mode)
|
|
320
337
|
const gitPaneVisible = useAppStore((s) => s.gitPane.visible)
|
|
321
338
|
const gitPanePosition = useAppStore((s) => s.gitPane.position)
|
|
@@ -410,6 +427,7 @@ export function RootView({
|
|
|
410
427
|
snippets,
|
|
411
428
|
themeId,
|
|
412
429
|
worktreeDivergence,
|
|
430
|
+
worktreeTemplates,
|
|
413
431
|
})}
|
|
414
432
|
<ToastViewport />
|
|
415
433
|
</box>
|
|
@@ -507,6 +525,7 @@ export function RootView({
|
|
|
507
525
|
snippets,
|
|
508
526
|
themeId,
|
|
509
527
|
worktreeDivergence,
|
|
528
|
+
worktreeTemplates,
|
|
510
529
|
})}
|
|
511
530
|
<ToastViewport />
|
|
512
531
|
</box>
|