@brimveyn/aimux 1.1.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 +206 -0
- package/package.json +79 -0
- package/src/app-runtime/backend-attach-runtime.ts +135 -0
- package/src/app-runtime/backend-runtime-events.ts +78 -0
- package/src/app-runtime/click-selection-resolver.ts +130 -0
- package/src/app-runtime/pty-write.ts +32 -0
- package/src/app-runtime/render-invalidation.ts +20 -0
- package/src/app-runtime/selection-clipboard.ts +69 -0
- package/src/app-runtime/selection-scroll.ts +143 -0
- package/src/app-runtime/session-actions.ts +170 -0
- package/src/app-runtime/side-effects.ts +434 -0
- package/src/app-runtime/snippet-actions.ts +90 -0
- package/src/app-runtime/split-drag-controller.ts +15 -0
- package/src/app-runtime/tab-runtime-timeouts.ts +86 -0
- package/src/app-runtime/terminal-mouse-adapter.ts +31 -0
- package/src/app-runtime/use-backend-runtime.ts +99 -0
- package/src/app-runtime/use-directory-search.ts +52 -0
- package/src/app-runtime/use-mouse-handlers.ts +183 -0
- package/src/app-runtime/use-renderer-bindings.ts +163 -0
- package/src/app-runtime/use-terminal-resize.ts +141 -0
- package/src/app-runtime/use-workspace-autosave.ts +39 -0
- package/src/app.tsx +247 -0
- package/src/config/loader.ts +36 -0
- package/src/config.ts +146 -0
- package/src/daemon/daemon.ts +236 -0
- package/src/daemon/runtime-paths.ts +69 -0
- package/src/daemon/session-manager.ts +109 -0
- package/src/daemon/session-registry.ts +207 -0
- package/src/debug/input-log.ts +29 -0
- package/src/doctor.ts +106 -0
- package/src/git/git-poller.ts +49 -0
- package/src/git/git-status.ts +183 -0
- package/src/index.tsx +56 -0
- package/src/input/keymap/build-handlers.ts +34 -0
- package/src/input/keymap/key-chord.ts +201 -0
- package/src/input/keymap/keymap-mode-handler.ts +88 -0
- package/src/input/keymap/sequence-resolver.ts +117 -0
- package/src/input/keymap/trie.ts +103 -0
- package/src/input/modes/bridge.ts +58 -0
- package/src/input/modes/handlers/index.ts +18 -0
- package/src/input/modes/handlers/shared.ts +70 -0
- package/src/input/modes/registry.ts +34 -0
- package/src/input/modes/transitions.ts +37 -0
- package/src/input/modes/types.ts +63 -0
- package/src/input/mouse-forwarding.ts +98 -0
- package/src/input/multi-click-detector.ts +55 -0
- package/src/input/paste.ts +12 -0
- package/src/input/raw-input-handler.ts +191 -0
- package/src/input/terminal-text-extraction.ts +78 -0
- package/src/ipc/protocol.ts +341 -0
- package/src/platform/clipboard.ts +13 -0
- package/src/platform/daemon-control.ts +62 -0
- package/src/platform/id.ts +7 -0
- package/src/platform/project-search.ts +75 -0
- package/src/pty/command-registry.ts +69 -0
- package/src/pty/pty-manager.ts +268 -0
- package/src/pty/terminal-snapshot.ts +210 -0
- package/src/restart-daemon.ts +28 -0
- package/src/session-backend/bootstrap.ts +107 -0
- package/src/session-backend/local-session-backend.ts +164 -0
- package/src/session-backend/remote-session-backend.ts +373 -0
- package/src/session-backend/types.ts +47 -0
- package/src/state/app-store.ts +19 -0
- package/src/state/layout-resize.ts +56 -0
- package/src/state/layout-tree.ts +323 -0
- package/src/state/reducers/git-panel-state.ts +102 -0
- package/src/state/reducers/modal-state.ts +358 -0
- package/src/state/reducers/session-state.ts +86 -0
- package/src/state/reducers/tab-state.ts +531 -0
- package/src/state/reducers/ui-state.ts +29 -0
- package/src/state/selectors.ts +30 -0
- package/src/state/session-catalog.ts +96 -0
- package/src/state/session-persistence.ts +210 -0
- package/src/state/snippet-catalog.ts +90 -0
- package/src/state/store.ts +93 -0
- package/src/state/terminal-modes.ts +11 -0
- package/src/state/types.ts +367 -0
- package/src/state/validation.ts +154 -0
- package/src/state/workspace-save.ts +33 -0
- package/src/ui/components/create-session-modal.tsx +100 -0
- package/src/ui/components/git-panel.tsx +200 -0
- package/src/ui/components/help-modal.tsx +79 -0
- package/src/ui/components/input-field.tsx +18 -0
- package/src/ui/components/list-item.tsx +30 -0
- package/src/ui/components/modal-filter-bar.tsx +18 -0
- package/src/ui/components/modal-shell.tsx +47 -0
- package/src/ui/components/new-tab-modal.tsx +52 -0
- package/src/ui/components/pending-chord-indicator.tsx +41 -0
- package/src/ui/components/session-name-modal.tsx +15 -0
- package/src/ui/components/session-picker-modal.tsx +93 -0
- package/src/ui/components/sidebar-group-metadata.ts +44 -0
- package/src/ui/components/sidebar-scroll.ts +33 -0
- package/src/ui/components/sidebar.tsx +225 -0
- package/src/ui/components/snippet-editor-modal.tsx +39 -0
- package/src/ui/components/snippet-picker-modal.tsx +56 -0
- package/src/ui/components/split-layout.tsx +201 -0
- package/src/ui/components/status-bar.tsx +60 -0
- package/src/ui/components/surface.tsx +63 -0
- package/src/ui/components/tab-item.tsx +118 -0
- package/src/ui/components/terminal-pane.tsx +215 -0
- package/src/ui/components/theme-picker-modal.tsx +35 -0
- package/src/ui/components/use-sidebar-auto-scroll.ts +63 -0
- package/src/ui/components/use-sidebar-branch.ts +36 -0
- package/src/ui/directory-search.ts +1 -0
- package/src/ui/git-branch.ts +11 -0
- package/src/ui/path-format.ts +6 -0
- package/src/ui/root.tsx +261 -0
- package/src/ui/status-bar-model.ts +87 -0
- package/src/ui/theme.ts +9 -0
- package/src/ui/themes.ts +255 -0
- package/src/ui/ui-tokens.ts +11 -0
- package/src/update.ts +62 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { AppState, TabSession, TabStatus, WorkspaceSnapshotV1 } from './types'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
allLeafIds,
|
|
5
|
+
createGroupId,
|
|
6
|
+
createLeaf,
|
|
7
|
+
type LayoutNode,
|
|
8
|
+
pruneLayoutTree,
|
|
9
|
+
} from './layout-tree'
|
|
10
|
+
|
|
11
|
+
export function createEmptyWorkspaceSnapshot(): WorkspaceSnapshotV1 {
|
|
12
|
+
return {
|
|
13
|
+
activeTabId: null,
|
|
14
|
+
savedAt: new Date().toISOString(),
|
|
15
|
+
sidebar: {
|
|
16
|
+
visible: true,
|
|
17
|
+
width: 28,
|
|
18
|
+
},
|
|
19
|
+
tabs: [],
|
|
20
|
+
version: 1,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getDisconnectedStatus(status: WorkspaceSnapshotV1['tabs'][number]['status']): TabStatus {
|
|
25
|
+
if (status === 'running' || status === 'starting') {
|
|
26
|
+
return 'disconnected'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return status
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function serializeWorkspace(state: AppState): WorkspaceSnapshotV1 {
|
|
33
|
+
return {
|
|
34
|
+
activeTabId: state.activeTabId,
|
|
35
|
+
layoutTree: Object.values(state.layoutTrees)[0] ?? undefined,
|
|
36
|
+
layoutTrees: Object.keys(state.layoutTrees).length > 0 ? state.layoutTrees : undefined,
|
|
37
|
+
savedAt: new Date().toISOString(),
|
|
38
|
+
sidebar: {
|
|
39
|
+
visible: state.sidebar.visible,
|
|
40
|
+
width: state.sidebar.width,
|
|
41
|
+
},
|
|
42
|
+
tabGroupMap: Object.keys(state.tabGroupMap).length > 0 ? state.tabGroupMap : undefined,
|
|
43
|
+
tabs: state.tabs.map((tab) => ({
|
|
44
|
+
assistant: tab.assistant,
|
|
45
|
+
buffer: tab.buffer,
|
|
46
|
+
command: tab.command,
|
|
47
|
+
errorMessage: tab.errorMessage,
|
|
48
|
+
exitCode: tab.exitCode,
|
|
49
|
+
id: tab.id,
|
|
50
|
+
status: tab.status === 'disconnected' ? 'running' : tab.status,
|
|
51
|
+
terminalModes: tab.terminalModes,
|
|
52
|
+
title: tab.title,
|
|
53
|
+
viewport: tab.viewport,
|
|
54
|
+
})),
|
|
55
|
+
version: 1,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function restoreTabsFromWorkspace(snapshot: WorkspaceSnapshotV1 | undefined): TabSession[] {
|
|
60
|
+
if (!snapshot || snapshot.version !== 1) {
|
|
61
|
+
return []
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return snapshot.tabs.map((tab) => ({
|
|
65
|
+
activity: 'idle',
|
|
66
|
+
assistant: tab.assistant,
|
|
67
|
+
buffer: tab.buffer,
|
|
68
|
+
command: tab.command,
|
|
69
|
+
errorMessage: tab.errorMessage,
|
|
70
|
+
exitCode: tab.exitCode,
|
|
71
|
+
id: tab.id,
|
|
72
|
+
status: getDisconnectedStatus(tab.status),
|
|
73
|
+
terminalModes: tab.terminalModes,
|
|
74
|
+
title: tab.title,
|
|
75
|
+
viewport: tab.viewport,
|
|
76
|
+
}))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function restoreLayoutTree(
|
|
80
|
+
snapshot: WorkspaceSnapshotV1 | undefined,
|
|
81
|
+
tabs: TabSession[]
|
|
82
|
+
): LayoutNode | null {
|
|
83
|
+
if (snapshot?.layoutTree) {
|
|
84
|
+
const validTabIds = new Set(tabs.map((t) => t.id))
|
|
85
|
+
const pruned = pruneLayoutTree(snapshot.layoutTree, validTabIds)
|
|
86
|
+
if (pruned) return pruned
|
|
87
|
+
}
|
|
88
|
+
// Fallback: single leaf for the first tab
|
|
89
|
+
return tabs[0] ? createLeaf(tabs[0].id) : null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function restoreLayoutTrees(
|
|
93
|
+
snapshot: WorkspaceSnapshotV1 | undefined,
|
|
94
|
+
tabs: TabSession[]
|
|
95
|
+
): { layoutTrees: Record<string, LayoutNode>; tabGroupMap: Record<string, string> } {
|
|
96
|
+
const validTabIds = new Set(tabs.map((t) => t.id))
|
|
97
|
+
const layoutTrees: Record<string, LayoutNode> = {}
|
|
98
|
+
const tabGroupMap: Record<string, string> = {}
|
|
99
|
+
|
|
100
|
+
if (snapshot?.layoutTrees && snapshot?.tabGroupMap) {
|
|
101
|
+
// New format
|
|
102
|
+
for (const [gId, tree] of Object.entries(snapshot.layoutTrees)) {
|
|
103
|
+
const pruned = pruneLayoutTree(tree, validTabIds)
|
|
104
|
+
if (pruned && pruned.type === 'split') {
|
|
105
|
+
layoutTrees[gId] = pruned
|
|
106
|
+
for (const leafId of allLeafIds(pruned)) {
|
|
107
|
+
tabGroupMap[leafId] = gId
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} else if (snapshot?.layoutTree) {
|
|
112
|
+
// Legacy migration
|
|
113
|
+
const pruned = pruneLayoutTree(snapshot.layoutTree, validTabIds)
|
|
114
|
+
if (pruned && pruned.type === 'split') {
|
|
115
|
+
const gId = createGroupId()
|
|
116
|
+
layoutTrees[gId] = pruned
|
|
117
|
+
for (const leafId of allLeafIds(pruned)) {
|
|
118
|
+
tabGroupMap[leafId] = gId
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { layoutTrees, tabGroupMap }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function normalizeGroupedTabOrder(
|
|
127
|
+
tabs: TabSession[],
|
|
128
|
+
layoutTrees: Record<string, LayoutNode>,
|
|
129
|
+
tabGroupMap: Record<string, string>
|
|
130
|
+
): TabSession[] {
|
|
131
|
+
const groupedTabsByGroupId = new Map<string, TabSession[]>()
|
|
132
|
+
const emittedGroupIds = new Set<string>()
|
|
133
|
+
const orderedTabs: TabSession[] = []
|
|
134
|
+
|
|
135
|
+
for (const tab of tabs) {
|
|
136
|
+
const groupId = tabGroupMap[tab.id]
|
|
137
|
+
if (!groupId) {
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const groupTree = layoutTrees[groupId]
|
|
142
|
+
if (!groupTree || groupTree.type !== 'split') {
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const groupedTabs = groupedTabsByGroupId.get(groupId)
|
|
147
|
+
if (groupedTabs) {
|
|
148
|
+
groupedTabs.push(tab)
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
groupedTabsByGroupId.set(groupId, [tab])
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const tab of tabs) {
|
|
156
|
+
const groupId = tabGroupMap[tab.id]
|
|
157
|
+
const groupTree = groupId ? layoutTrees[groupId] : undefined
|
|
158
|
+
|
|
159
|
+
if (!groupId || !groupTree || groupTree.type !== 'split') {
|
|
160
|
+
orderedTabs.push(tab)
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (emittedGroupIds.has(groupId)) {
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
emittedGroupIds.add(groupId)
|
|
169
|
+
const groupedTabs = groupedTabsByGroupId.get(groupId)
|
|
170
|
+
if (!groupedTabs) {
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const groupTab of groupedTabs) {
|
|
175
|
+
orderedTabs.push(groupTab)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return orderedTabs
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function restoreWorkspaceState(
|
|
183
|
+
state: AppState,
|
|
184
|
+
workspaceSnapshot: WorkspaceSnapshotV1 | undefined
|
|
185
|
+
): Pick<
|
|
186
|
+
AppState,
|
|
187
|
+
'tabs' | 'activeTabId' | 'focusMode' | 'sidebar' | 'layoutTrees' | 'tabGroupMap'
|
|
188
|
+
> {
|
|
189
|
+
const tabs = restoreTabsFromWorkspace(workspaceSnapshot)
|
|
190
|
+
const activeTabId =
|
|
191
|
+
workspaceSnapshot?.activeTabId && tabs.some((tab) => tab.id === workspaceSnapshot.activeTabId)
|
|
192
|
+
? workspaceSnapshot.activeTabId
|
|
193
|
+
: (tabs[0]?.id ?? null)
|
|
194
|
+
|
|
195
|
+
const { layoutTrees, tabGroupMap } = restoreLayoutTrees(workspaceSnapshot, tabs)
|
|
196
|
+
const orderedTabs = normalizeGroupedTabOrder(tabs, layoutTrees, tabGroupMap)
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
activeTabId,
|
|
200
|
+
focusMode: 'navigation',
|
|
201
|
+
layoutTrees,
|
|
202
|
+
sidebar: {
|
|
203
|
+
...state.sidebar,
|
|
204
|
+
visible: workspaceSnapshot?.sidebar.visible ?? state.sidebar.visible,
|
|
205
|
+
width: workspaceSnapshot?.sidebar.width ?? state.sidebar.width,
|
|
206
|
+
},
|
|
207
|
+
tabGroupMap,
|
|
208
|
+
tabs: orderedTabs,
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join } from 'node:path'
|
|
3
|
+
|
|
4
|
+
import { CONFIG_PATH } from '../config'
|
|
5
|
+
import { logDebug } from '../debug/input-log'
|
|
6
|
+
import { isSnippetRecord } from './validation'
|
|
7
|
+
|
|
8
|
+
export interface SnippetRecord {
|
|
9
|
+
id: string
|
|
10
|
+
name: string
|
|
11
|
+
content: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const SNIPPETS_PATH = join(dirname(CONFIG_PATH), 'aimux-snippets.json')
|
|
15
|
+
|
|
16
|
+
const DEFAULT_SNIPPETS: SnippetRecord[] = [
|
|
17
|
+
{
|
|
18
|
+
content: 'Review this code for bugs, security issues, and suggest improvements.',
|
|
19
|
+
id: 'default-review',
|
|
20
|
+
name: 'Code review',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
content: 'Explain how this code works step by step.',
|
|
24
|
+
id: 'default-explain',
|
|
25
|
+
name: 'Explain',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
content: 'Write comprehensive unit tests for this code with edge cases.',
|
|
29
|
+
id: 'default-tests',
|
|
30
|
+
name: 'Write tests',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
content:
|
|
34
|
+
'Refactor this code to improve readability and maintainability without changing behavior.',
|
|
35
|
+
id: 'default-refactor',
|
|
36
|
+
name: 'Refactor',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
content: 'Analyze this error and provide a fix with explanation.',
|
|
40
|
+
id: 'default-fix',
|
|
41
|
+
name: 'Fix error',
|
|
42
|
+
},
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
export function loadSnippetCatalog(): SnippetRecord[] {
|
|
46
|
+
try {
|
|
47
|
+
if (!existsSync(SNIPPETS_PATH)) {
|
|
48
|
+
saveSnippetCatalog(DEFAULT_SNIPPETS)
|
|
49
|
+
return DEFAULT_SNIPPETS
|
|
50
|
+
}
|
|
51
|
+
const parsed = JSON.parse(readFileSync(SNIPPETS_PATH, 'utf8')) as {
|
|
52
|
+
version?: unknown
|
|
53
|
+
snippets?: unknown
|
|
54
|
+
}
|
|
55
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.snippets)) {
|
|
56
|
+
logDebug('snippets.catalog.loadIssue', {
|
|
57
|
+
issue: 'invalid snippet catalog header',
|
|
58
|
+
path: SNIPPETS_PATH,
|
|
59
|
+
})
|
|
60
|
+
return []
|
|
61
|
+
}
|
|
62
|
+
if (!parsed.snippets.every(isSnippetRecord)) {
|
|
63
|
+
logDebug('snippets.catalog.loadIssue', {
|
|
64
|
+
issue: 'invalid snippet catalog entries',
|
|
65
|
+
path: SNIPPETS_PATH,
|
|
66
|
+
})
|
|
67
|
+
return []
|
|
68
|
+
}
|
|
69
|
+
return parsed.snippets
|
|
70
|
+
} catch (error) {
|
|
71
|
+
logDebug('snippets.catalog.loadIssue', {
|
|
72
|
+
issue: error instanceof Error ? error.message : String(error),
|
|
73
|
+
path: SNIPPETS_PATH,
|
|
74
|
+
})
|
|
75
|
+
return []
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function saveSnippetCatalog(snippets: SnippetRecord[]): void {
|
|
80
|
+
try {
|
|
81
|
+
mkdirSync(dirname(SNIPPETS_PATH), { recursive: true })
|
|
82
|
+
writeFileSync(SNIPPETS_PATH, `${JSON.stringify({ snippets, version: 1 }, null, 2)}\n`)
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logDebug('snippets.catalog.saveError', {
|
|
85
|
+
error: error instanceof Error ? error.message : String(error),
|
|
86
|
+
path: SNIPPETS_PATH,
|
|
87
|
+
snippetCount: snippets.length,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { AppAction, AppState, SessionRecord, SnippetRecord } from './types'
|
|
2
|
+
|
|
3
|
+
import { emptyGitPanel, reduceGitPanelState } from './reducers/git-panel-state'
|
|
4
|
+
import { emptyModal, reduceModalState } from './reducers/modal-state'
|
|
5
|
+
import { reduceSessionState } from './reducers/session-state'
|
|
6
|
+
import { reduceTabState } from './reducers/tab-state'
|
|
7
|
+
import { reduceUIState } from './reducers/ui-state'
|
|
8
|
+
import { filterSnippets } from './selectors'
|
|
9
|
+
|
|
10
|
+
const DEFAULT_SIDEBAR_WIDTH = 28
|
|
11
|
+
const DEFAULT_SIDEBAR_MIN_WIDTH = 18
|
|
12
|
+
const DEFAULT_SIDEBAR_MAX_WIDTH = 42
|
|
13
|
+
const DEFAULT_TERMINAL_COLS = 80
|
|
14
|
+
const DEFAULT_TERMINAL_ROWS = 24
|
|
15
|
+
|
|
16
|
+
export interface InitialStateOverrides {
|
|
17
|
+
gitPanelVisible?: boolean
|
|
18
|
+
gitPanelRatio?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createInitialState(
|
|
22
|
+
customCommands: Record<string, string> = {},
|
|
23
|
+
sessions: SessionRecord[] = [],
|
|
24
|
+
snippets: SnippetRecord[] = [],
|
|
25
|
+
showSessionPicker = false,
|
|
26
|
+
overrides: InitialStateOverrides = {}
|
|
27
|
+
): AppState {
|
|
28
|
+
return {
|
|
29
|
+
activeTabId: null,
|
|
30
|
+
currentSessionId: null,
|
|
31
|
+
customCommands,
|
|
32
|
+
focusMode: showSessionPicker ? 'modal' : 'navigation',
|
|
33
|
+
gitPanel: emptyGitPanel(),
|
|
34
|
+
layout: {
|
|
35
|
+
terminalCols: DEFAULT_TERMINAL_COLS,
|
|
36
|
+
terminalRows: DEFAULT_TERMINAL_ROWS,
|
|
37
|
+
},
|
|
38
|
+
layoutTrees: {},
|
|
39
|
+
modal: showSessionPicker
|
|
40
|
+
? { editBuffer: null, selectedIndex: 0, sessionTargetId: null, type: 'session-picker' }
|
|
41
|
+
: emptyModal(),
|
|
42
|
+
pendingChords: null,
|
|
43
|
+
sessions,
|
|
44
|
+
sidebar: {
|
|
45
|
+
gitPanelRatio: overrides.gitPanelRatio ?? 0.5,
|
|
46
|
+
gitPanelVisible: overrides.gitPanelVisible ?? true,
|
|
47
|
+
maxWidth: DEFAULT_SIDEBAR_MAX_WIDTH,
|
|
48
|
+
minWidth: DEFAULT_SIDEBAR_MIN_WIDTH,
|
|
49
|
+
visible: true,
|
|
50
|
+
width: DEFAULT_SIDEBAR_WIDTH,
|
|
51
|
+
},
|
|
52
|
+
snippets,
|
|
53
|
+
tabGroupMap: {},
|
|
54
|
+
tabs: [],
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function appReducer(state: AppState, action: AppAction): AppState {
|
|
59
|
+
const sessionState = reduceSessionState(state, action)
|
|
60
|
+
if (sessionState) return sessionState
|
|
61
|
+
|
|
62
|
+
const tabState = reduceTabState(state, action)
|
|
63
|
+
if (tabState) return tabState
|
|
64
|
+
|
|
65
|
+
const modalState = reduceModalState(state, action)
|
|
66
|
+
if (modalState) return modalState
|
|
67
|
+
|
|
68
|
+
const uiState = reduceUIState(state, action)
|
|
69
|
+
if (uiState) return uiState
|
|
70
|
+
|
|
71
|
+
const gitPanelState = reduceGitPanelState(state, action)
|
|
72
|
+
if (gitPanelState) return gitPanelState
|
|
73
|
+
|
|
74
|
+
switch (action.type) {
|
|
75
|
+
case 'set-snippets':
|
|
76
|
+
return { ...state, snippets: action.snippets }
|
|
77
|
+
case 'delete-snippet': {
|
|
78
|
+
const newSnippets = state.snippets.filter((s) => s.id !== action.snippetId)
|
|
79
|
+
const filteredNew = filterSnippets(newSnippets, state.modal.editBuffer)
|
|
80
|
+
const maxIndex = Math.max(0, filteredNew.length - 1)
|
|
81
|
+
return {
|
|
82
|
+
...state,
|
|
83
|
+
modal: {
|
|
84
|
+
...state.modal,
|
|
85
|
+
selectedIndex: Math.min(state.modal.selectedIndex, maxIndex),
|
|
86
|
+
},
|
|
87
|
+
snippets: newSnippets,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
default:
|
|
91
|
+
return state
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TerminalModeState } from './types'
|
|
2
|
+
|
|
3
|
+
export function createDefaultTerminalModes(): TerminalModeState {
|
|
4
|
+
return {
|
|
5
|
+
alternateScrollMode: false,
|
|
6
|
+
bracketedPasteMode: false,
|
|
7
|
+
isAlternateBuffer: false,
|
|
8
|
+
mouseTrackingMode: 'none',
|
|
9
|
+
sendFocusMode: false,
|
|
10
|
+
}
|
|
11
|
+
}
|