@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,69 @@
|
|
|
1
|
+
import type { TabSession } from '../state/types'
|
|
2
|
+
|
|
3
|
+
import { extractStreamText } from '../input/terminal-text-extraction'
|
|
4
|
+
|
|
5
|
+
interface PositionedNode {
|
|
6
|
+
parent?: unknown
|
|
7
|
+
x: number
|
|
8
|
+
y: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface OtuiSelection {
|
|
12
|
+
isDragging?: boolean
|
|
13
|
+
anchor: { x: number; y: number }
|
|
14
|
+
focus: { x: number; y: number }
|
|
15
|
+
touchedRenderables?: unknown[]
|
|
16
|
+
getSelectedText(): string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isPositionedNode(value: unknown): value is PositionedNode {
|
|
20
|
+
return (
|
|
21
|
+
typeof value === 'object' &&
|
|
22
|
+
value !== null &&
|
|
23
|
+
typeof Reflect.get(value, 'x') === 'number' &&
|
|
24
|
+
typeof Reflect.get(value, 'y') === 'number'
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function computeStreamSelectedText(
|
|
29
|
+
selection: OtuiSelection,
|
|
30
|
+
lines: { spans: { text: string }[] }[]
|
|
31
|
+
): string | null {
|
|
32
|
+
const touched = selection.touchedRenderables
|
|
33
|
+
if (!touched || touched.length === 0) return null
|
|
34
|
+
|
|
35
|
+
const viewportText = touched[0]
|
|
36
|
+
if (!isPositionedNode(viewportText)) return null
|
|
37
|
+
|
|
38
|
+
const originX = viewportText.x
|
|
39
|
+
const originY = viewportText.y
|
|
40
|
+
|
|
41
|
+
const anchorRow = selection.anchor.y - originY
|
|
42
|
+
const anchorCol = selection.anchor.x - originX
|
|
43
|
+
const focusRow = selection.focus.y - originY
|
|
44
|
+
const focusCol = selection.focus.x - originX
|
|
45
|
+
|
|
46
|
+
return extractStreamText(
|
|
47
|
+
lines as Parameters<typeof extractStreamText>[0],
|
|
48
|
+
anchorRow,
|
|
49
|
+
anchorCol,
|
|
50
|
+
focusRow,
|
|
51
|
+
focusCol
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resolveSelectionClipboardText(
|
|
56
|
+
selection: OtuiSelection,
|
|
57
|
+
tab: TabSession | undefined
|
|
58
|
+
): { selectedText: string; streamLength: number | null; fallbackLength: number } {
|
|
59
|
+
const fallback = selection.getSelectedText()
|
|
60
|
+
const streamText =
|
|
61
|
+
tab?.viewport && tab.viewport.lines.length > 0
|
|
62
|
+
? computeStreamSelectedText(selection, tab.viewport.lines)
|
|
63
|
+
: null
|
|
64
|
+
return {
|
|
65
|
+
fallbackLength: fallback.length,
|
|
66
|
+
selectedText: streamText ?? fallback,
|
|
67
|
+
streamLength: streamText?.length ?? null,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
interface SelectableLike {
|
|
2
|
+
selectable?: boolean
|
|
3
|
+
isDestroyed?: boolean
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function isUsableTarget(target: SelectableLike | null | undefined): target is SelectableLike {
|
|
7
|
+
return Boolean(target?.selectable) && !target?.isDestroyed
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface SelectionLike {
|
|
11
|
+
isActive: boolean
|
|
12
|
+
isDragging: boolean
|
|
13
|
+
anchor: { x: number; y: number }
|
|
14
|
+
focus: { x: number; y: number }
|
|
15
|
+
selectedRenderables: SelectableLike[]
|
|
16
|
+
touchedRenderables: SelectableLike[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RendererSelectionApi {
|
|
20
|
+
getSelection(): SelectionLike | null
|
|
21
|
+
startSelection(target: unknown, x: number, y: number): void
|
|
22
|
+
updateSelection(target: unknown, x: number, y: number, opts: { finishDragging: boolean }): void
|
|
23
|
+
clearSelection(): void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ShiftState {
|
|
27
|
+
target: SelectableLike | null
|
|
28
|
+
anchor: { x: number; y: number } | null
|
|
29
|
+
focus: { x: number; y: number } | null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const states = new WeakMap<object, ShiftState>()
|
|
33
|
+
|
|
34
|
+
function getState(renderer: object): ShiftState {
|
|
35
|
+
let s = states.get(renderer)
|
|
36
|
+
if (!s) {
|
|
37
|
+
s = { anchor: null, focus: null, target: null }
|
|
38
|
+
states.set(renderer, s)
|
|
39
|
+
}
|
|
40
|
+
return s
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function resetSelectionShiftState(renderer: object): void {
|
|
44
|
+
states.set(renderer, { anchor: null, focus: null, target: null })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function pickSelectionTarget(selection: SelectionLike): SelectableLike | null {
|
|
48
|
+
for (const r of selection.selectedRenderables) {
|
|
49
|
+
if (isUsableTarget(r)) return r
|
|
50
|
+
}
|
|
51
|
+
for (const r of selection.touchedRenderables) {
|
|
52
|
+
if (isUsableTarget(r)) return r
|
|
53
|
+
}
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function liveDiffersFromCache(live: SelectionLike, state: ShiftState): boolean {
|
|
58
|
+
if (!state.anchor || !state.focus) return true
|
|
59
|
+
return (
|
|
60
|
+
live.anchor.x !== state.anchor.x ||
|
|
61
|
+
live.anchor.y !== state.anchor.y ||
|
|
62
|
+
live.focus.x !== state.focus.x ||
|
|
63
|
+
live.focus.y !== state.focus.y
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function shiftSelectionByScroll(renderer: RendererSelectionApi, deltaLines: number): void {
|
|
68
|
+
if (deltaLines === 0) return
|
|
69
|
+
|
|
70
|
+
const live = renderer.getSelection()
|
|
71
|
+
|
|
72
|
+
if (!live?.isActive) {
|
|
73
|
+
resetSelectionShiftState(renderer)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (live.isDragging) {
|
|
78
|
+
// Mid-drag (e.g. user holds the mouse and scrolls past the viewport edge to
|
|
79
|
+
// extend the selection): only the anchor needs to follow the content. The
|
|
80
|
+
// focus is owned by opentui's live pointer tracking, which keeps it pinned
|
|
81
|
+
// to the cursor in screen-space — leave it alone.
|
|
82
|
+
const target = pickSelectionTarget(live)
|
|
83
|
+
if (!isUsableTarget(target)) return
|
|
84
|
+
const anchorY = live.anchor.y - deltaLines
|
|
85
|
+
renderer.startSelection(target, live.anchor.x, anchorY)
|
|
86
|
+
renderer.updateSelection(target, live.focus.x, live.focus.y, { finishDragging: false })
|
|
87
|
+
resetSelectionShiftState(renderer)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const state = getState(renderer)
|
|
92
|
+
|
|
93
|
+
if (liveDiffersFromCache(live, state)) {
|
|
94
|
+
const target = pickSelectionTarget(live) ?? (isUsableTarget(state.target) ? state.target : null)
|
|
95
|
+
if (target) {
|
|
96
|
+
state.target = target
|
|
97
|
+
state.anchor = { x: live.anchor.x, y: live.anchor.y }
|
|
98
|
+
state.focus = { x: live.focus.x, y: live.focus.y }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!isUsableTarget(state.target) || !state.anchor || !state.focus) {
|
|
103
|
+
if (!isUsableTarget(state.target)) {
|
|
104
|
+
resetSelectionShiftState(renderer)
|
|
105
|
+
}
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const anchor = { x: state.anchor.x, y: state.anchor.y - deltaLines }
|
|
110
|
+
const focus = { x: state.focus.x, y: state.focus.y - deltaLines }
|
|
111
|
+
state.anchor = anchor
|
|
112
|
+
state.focus = focus
|
|
113
|
+
|
|
114
|
+
renderer.startSelection(state.target, anchor.x, anchor.y)
|
|
115
|
+
renderer.updateSelection(state.target, focus.x, focus.y, { finishDragging: true })
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ViewportObservation {
|
|
119
|
+
tabId: string
|
|
120
|
+
y: number
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function applyViewportObservation(
|
|
124
|
+
renderer: RendererSelectionApi,
|
|
125
|
+
prior: ViewportObservation | null,
|
|
126
|
+
next: ViewportObservation | null
|
|
127
|
+
): ViewportObservation | null {
|
|
128
|
+
if (!next) {
|
|
129
|
+
if (prior !== null) resetSelectionShiftState(renderer)
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!prior || prior.tabId !== next.tabId) {
|
|
134
|
+
resetSelectionShiftState(renderer)
|
|
135
|
+
return next
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const delta = next.y - prior.y
|
|
139
|
+
if (delta !== 0) {
|
|
140
|
+
shiftSelectionByScroll(renderer, delta)
|
|
141
|
+
}
|
|
142
|
+
return next
|
|
143
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { SessionBackend } from '../session-backend/types'
|
|
2
|
+
import type { AppAction, AppState, SessionRecord, TabSession } from '../state/types'
|
|
3
|
+
|
|
4
|
+
import { logInputDebug } from '../debug/input-log'
|
|
5
|
+
import { createPrefixedId } from '../platform/id'
|
|
6
|
+
import { saveSessionCatalog } from '../state/session-catalog'
|
|
7
|
+
import { createEmptyWorkspaceSnapshot, serializeWorkspace } from '../state/session-persistence'
|
|
8
|
+
import { buildSessionsWithCurrentSnapshot } from '../state/workspace-save'
|
|
9
|
+
|
|
10
|
+
export function createSessionFromCurrentState(
|
|
11
|
+
state: AppState,
|
|
12
|
+
name: string,
|
|
13
|
+
projectPath?: string
|
|
14
|
+
): { session: SessionRecord; sessions: SessionRecord[] } {
|
|
15
|
+
const now = new Date().toISOString()
|
|
16
|
+
const workspaceSnapshot =
|
|
17
|
+
state.currentSessionId || state.tabs.length === 0
|
|
18
|
+
? createEmptyWorkspaceSnapshot()
|
|
19
|
+
: serializeWorkspace(state)
|
|
20
|
+
const session: SessionRecord = {
|
|
21
|
+
createdAt: now,
|
|
22
|
+
id: createPrefixedId('session'),
|
|
23
|
+
lastOpenedAt: now,
|
|
24
|
+
name,
|
|
25
|
+
projectPath,
|
|
26
|
+
updatedAt: now,
|
|
27
|
+
workspaceSnapshot,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let updatedSessions = state.sessions
|
|
31
|
+
if (state.currentSessionId) {
|
|
32
|
+
const currentSnapshot = serializeWorkspace(state)
|
|
33
|
+
updatedSessions = state.sessions.map((entry) =>
|
|
34
|
+
entry.id === state.currentSessionId
|
|
35
|
+
? { ...entry, updatedAt: now, workspaceSnapshot: currentSnapshot }
|
|
36
|
+
: entry
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
session,
|
|
42
|
+
sessions: [...updatedSessions, session],
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function renameSessionRecords(
|
|
47
|
+
sessions: SessionRecord[],
|
|
48
|
+
sessionId: string,
|
|
49
|
+
name: string
|
|
50
|
+
): SessionRecord[] {
|
|
51
|
+
return sessions.map((session) =>
|
|
52
|
+
session.id === sessionId ? { ...session, name, updatedAt: new Date().toISOString() } : session
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function switchSessionRecords(state: AppState, session: SessionRecord): SessionRecord[] {
|
|
57
|
+
const sessionsWithSnapshot = buildSessionsWithCurrentSnapshot(
|
|
58
|
+
state.sessions,
|
|
59
|
+
state.currentSessionId,
|
|
60
|
+
state
|
|
61
|
+
)
|
|
62
|
+
return sessionsWithSnapshot.map((entry) =>
|
|
63
|
+
entry.id === session.id ? { ...entry, lastOpenedAt: new Date().toISOString() } : entry
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function deleteSessionRecords(
|
|
68
|
+
sessions: SessionRecord[],
|
|
69
|
+
sessionId: string
|
|
70
|
+
): SessionRecord[] {
|
|
71
|
+
return sessions.filter((session) => session.id !== sessionId)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function handleCreateSessionEffect(
|
|
75
|
+
state: AppState,
|
|
76
|
+
dispatch: (action: AppAction) => void,
|
|
77
|
+
name: string,
|
|
78
|
+
projectPath?: string
|
|
79
|
+
): void {
|
|
80
|
+
const { session, sessions } = createSessionFromCurrentState(state, name, projectPath)
|
|
81
|
+
logInputDebug('app.session.create', {
|
|
82
|
+
fromCurrentWorkspace: !state.currentSessionId && state.tabs.length > 0,
|
|
83
|
+
name,
|
|
84
|
+
sessionId: session.id,
|
|
85
|
+
tabCount: session.workspaceSnapshot?.tabs.length ?? 0,
|
|
86
|
+
})
|
|
87
|
+
saveSessionCatalog(sessions)
|
|
88
|
+
dispatch({ sessions, type: 'set-sessions' })
|
|
89
|
+
dispatch({
|
|
90
|
+
sessionId: session.id,
|
|
91
|
+
type: 'load-session',
|
|
92
|
+
workspaceSnapshot: session.workspaceSnapshot,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function handleRenameSessionEffect(
|
|
97
|
+
sessions: SessionRecord[],
|
|
98
|
+
dispatch: (action: AppAction) => void,
|
|
99
|
+
sessionId: string,
|
|
100
|
+
name: string
|
|
101
|
+
): void {
|
|
102
|
+
logInputDebug('app.session.rename', { name, sessionId })
|
|
103
|
+
const renamed = renameSessionRecords(sessions, sessionId, name)
|
|
104
|
+
saveSessionCatalog(renamed)
|
|
105
|
+
dispatch({ name, sessionId, type: 'rename-session-record' })
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function handleSwitchSessionEffect(
|
|
109
|
+
state: AppState,
|
|
110
|
+
backend: SessionBackend,
|
|
111
|
+
dispatch: (action: AppAction) => void,
|
|
112
|
+
session: SessionRecord
|
|
113
|
+
): void {
|
|
114
|
+
logInputDebug('app.session.switch.start', {
|
|
115
|
+
currentTabCount: state.tabs.length,
|
|
116
|
+
fromSessionId: state.currentSessionId,
|
|
117
|
+
restoredTabCount: session.workspaceSnapshot?.tabs.length ?? 0,
|
|
118
|
+
toName: session.name,
|
|
119
|
+
toSessionId: session.id,
|
|
120
|
+
})
|
|
121
|
+
const sessions = switchSessionRecords(state, session)
|
|
122
|
+
saveSessionCatalog(sessions)
|
|
123
|
+
void backend.destroy(true)
|
|
124
|
+
dispatch({ sessions, type: 'set-sessions' })
|
|
125
|
+
dispatch({
|
|
126
|
+
sessionId: session.id,
|
|
127
|
+
type: 'load-session',
|
|
128
|
+
workspaceSnapshot: session.workspaceSnapshot,
|
|
129
|
+
})
|
|
130
|
+
logInputDebug('app.session.switch.dispatched', { toSessionId: session.id })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function handleDeleteSessionEffect(
|
|
134
|
+
state: AppState,
|
|
135
|
+
backend: SessionBackend,
|
|
136
|
+
dispatch: (action: AppAction) => void,
|
|
137
|
+
sessionId: string
|
|
138
|
+
): void {
|
|
139
|
+
const remaining = deleteSessionRecords(state.sessions, sessionId)
|
|
140
|
+
logInputDebug('app.session.delete', {
|
|
141
|
+
remainingCount: remaining.length,
|
|
142
|
+
sessionId,
|
|
143
|
+
wasCurrent: sessionId === state.currentSessionId,
|
|
144
|
+
})
|
|
145
|
+
saveSessionCatalog(remaining)
|
|
146
|
+
if (sessionId === state.currentSessionId) {
|
|
147
|
+
void backend.destroy(true)
|
|
148
|
+
}
|
|
149
|
+
dispatch({ sessionId, type: 'delete-session-record' })
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function restartTabSession(
|
|
153
|
+
backend: SessionBackend,
|
|
154
|
+
dispatch: (action: AppAction) => void,
|
|
155
|
+
clearIdleTimer: (tabId: string) => void,
|
|
156
|
+
clearStartupGrace: (tabId: string) => void,
|
|
157
|
+
startTabSession: (tab: TabSession) => void,
|
|
158
|
+
tab: TabSession
|
|
159
|
+
): void {
|
|
160
|
+
logInputDebug('app.restartTab', {
|
|
161
|
+
command: tab.command,
|
|
162
|
+
status: tab.status,
|
|
163
|
+
tabId: tab.id,
|
|
164
|
+
})
|
|
165
|
+
clearIdleTimer(tab.id)
|
|
166
|
+
clearStartupGrace(tab.id)
|
|
167
|
+
backend.disposeSession(tab.id)
|
|
168
|
+
dispatch({ tabId: tab.id, type: 'reset-tab-session' })
|
|
169
|
+
startTabSession(tab)
|
|
170
|
+
}
|