@brimveyn/aimux 1.13.2 → 1.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimveyn/aimux",
3
- "version": "1.13.2",
3
+ "version": "1.14.0",
4
4
  "description": "A terminal multiplexer for AI CLIs. Run Claude, Codex, OpenCode side-by-side with tabbed navigation, split panes, and persistent sessions.",
5
5
  "keywords": [
6
6
  "ai",
@@ -60,7 +60,7 @@
60
60
  "bump": "bun run scripts/bump.ts"
61
61
  },
62
62
  "dependencies": {
63
- "@brimveyn/aimux-config": "0.6.2",
63
+ "@brimveyn/aimux-config": "0.6.3",
64
64
  "@opentui/core": "^0.1.90",
65
65
  "@opentui/react": "^0.1.90",
66
66
  "@resvg/resvg-wasm": "^2.6.2",
@@ -10,7 +10,6 @@ import {
10
10
  getSnapshotTrees,
11
11
  toTerminalContentSize,
12
12
  } from '../state/layout-resize'
13
- import { getSnapshotScrollIntents } from '../state/session-persistence'
14
13
 
15
14
  export function resizeSnapshotPanes(
16
15
  snapshot: WorkspaceSnapshotV1 | undefined,
@@ -22,14 +21,13 @@ export function resizeSnapshotPanes(
22
21
  }
23
22
 
24
23
  const trees = getSnapshotTrees(snapshot)
25
- const intents = getSnapshotScrollIntents(snapshot)
26
24
  const bounds = createTerminalBounds(
27
25
  layoutRef.current.terminalCols,
28
26
  layoutRef.current.terminalRows
29
27
  )
30
28
  forEachSplitPaneRect(trees, bounds, (tabId, rect) => {
31
29
  const size = toTerminalContentSize(rect)
32
- backend.resizeTab(tabId, size.cols, size.rows, intents.get(tabId))
30
+ backend.resizeTab(tabId, size.cols, size.rows)
33
31
  })
34
32
  }
35
33
 
@@ -1,21 +1,33 @@
1
1
  // On multi-click selection mouseUp opentui's finishSelection() fires the
2
2
  // 'selection' event *after* our handleTerminalMouseUp has already written
3
- // the clipboard from drag.capturedLines (anchored at click time). The event
4
- // handler would then call computeStreamSelectedText against the *current*
5
- // tab.viewport.lines and overwrite our write with a value that can diverge
6
- // by a few rows when output landed between mouseDown and mouseUp.
3
+ // the clipboard from drag.capturedLines (anchored in absolute buffer rows).
4
+ // The event handler would then call computeStreamSelectedText against the
5
+ // *current* tab.viewport.lines and overwrite our write with a value that can
6
+ // diverge by a few rows when output/auto-scroll moved the viewport between
7
+ // mouseDown and mouseUp.
7
8
  //
8
- // To avoid that, handleTerminalMouseUp pings the guard right after it
9
- // copies; handleSelection consults the guard and skips its own copy for a
10
- // short window.
11
-
12
- const SUPPRESS_WINDOW_MS = 100
13
- let lastMultiClickWriteAt = 0
9
+ // finishSelection() emits exactly one 'selection' event, so we suppress
10
+ // exactly one a consume-once flag rather than a time window (which could
11
+ // either expire before the event arrived, letting a wrong value through, or
12
+ // swallow an unrelated selection that happened to land in the window).
13
+ //
14
+ // A short guard window bounds the flag: if the expected event never arrives
15
+ // (no finishSelection fired), the flag self-heals instead of silently eating
16
+ // the user's next genuine selection.
17
+ const ARM_WINDOW_MS = 250
18
+ let armedAt = 0
14
19
 
15
20
  export function recordMultiClickClipboardWrite(): void {
16
- lastMultiClickWriteAt = Date.now()
21
+ armedAt = Date.now()
17
22
  }
18
23
 
19
24
  export function shouldSuppressSelectionCopy(): boolean {
20
- return Date.now() - lastMultiClickWriteAt < SUPPRESS_WINDOW_MS
25
+ if (armedAt === 0) {
26
+ return false
27
+ }
28
+ const withinWindow = Date.now() - armedAt < ARM_WINDOW_MS
29
+ // Consume the arm regardless: the one finishSelection event is now handled,
30
+ // and a stale arm must not suppress a later, unrelated selection.
31
+ armedAt = 0
32
+ return withinWindow
21
33
  }
@@ -1,5 +1,5 @@
1
1
  import type { SessionBackend } from '../session-backend/types'
2
- import type { AppAction, TabSession } from '../state/types'
2
+ import type { TabSession } from '../state/types'
3
3
 
4
4
  import { logInputDebug } from '../debug/input-log'
5
5
  import { buildPtyPastePayload } from '../input/paste'
@@ -18,7 +18,6 @@ export function writeToTab(
18
18
  tabId: string,
19
19
  tab: TabSession | undefined,
20
20
  input: string,
21
- dispatch?: (action: AppAction) => void,
22
21
  options?: PtyWriteOptions
23
22
  ): void {
24
23
  logInputDebug('ptyWrite.writeToTab', {
@@ -28,8 +27,9 @@ export function writeToTab(
28
27
  tabId,
29
28
  })
30
29
  if ((options?.autoBottom ?? true) && tab && shouldScrollViewportToBottom(tab)) {
30
+ // Backend owns the scroll position; the snapshot it emits reflects the new
31
+ // bottom, so the frontend mirror updates without a separate dispatch.
31
32
  backend.scrollViewportToBottom(tabId)
32
- dispatch?.({ intent: { kind: 'bottom' }, tabId, type: 'set-scroll-intent' })
33
33
  }
34
34
 
35
35
  backend.write(tabId, input)
@@ -39,11 +39,10 @@ export function writePasteToTab(
39
39
  backend: SessionBackend,
40
40
  tabId: string,
41
41
  tab: TabSession | undefined,
42
- text: string,
43
- dispatch?: (action: AppAction) => void
42
+ text: string
44
43
  ): void {
45
44
  const payload = buildPtyPastePayload(text, tab?.terminalModes.bracketedPasteMode ?? false)
46
- writeToTab(backend, tabId, tab, payload, dispatch, { autoBottom: true })
45
+ writeToTab(backend, tabId, tab, payload, { autoBottom: true })
47
46
  }
48
47
 
49
48
  const DEL = '\x7f'
@@ -63,12 +62,10 @@ export function writeMacroExpansionToTab(
63
62
  tab: TabSession | undefined,
64
63
  eraseCount: number,
65
64
  expandedText: string,
66
- cursorOffset: number,
67
- dispatch?: (action: AppAction) => void
65
+ cursorOffset: number
68
66
  ): void {
69
67
  if (tab && shouldScrollViewportToBottom(tab)) {
70
68
  backend.scrollViewportToBottom(tabId)
71
- dispatch?.({ intent: { kind: 'bottom' }, tabId, type: 'set-scroll-intent' })
72
69
  }
73
70
 
74
71
  const erase = eraseCount > 0 ? DEL.repeat(eraseCount) : ''
@@ -13,6 +13,7 @@ import { dirname, join as joinPath, resolve as resolvePath } from 'node:path'
13
13
 
14
14
  import type { SideEffect } from '../input/modes/types'
15
15
  import type { SessionBackend } from '../session-backend/types'
16
+ import type { AppAction, AppState, AssistantId, TabSession, WorktreeRecord } from '../state/types'
16
17
  import type { ThemeId } from '../ui/themes'
17
18
 
18
19
  import { loadConfig, saveConfig } from '../config'
@@ -59,14 +60,6 @@ import { getActiveWorktree, getSessionProjectPath } from '../state/session-workt
59
60
  import { getSnippetsCatalogPath, isConfigSnippetId } from '../state/snippet-catalog'
60
61
  import { createDefaultTerminalModes } from '../state/terminal-modes'
61
62
  import { toast } from '../state/toast-store'
62
- import {
63
- type AppAction,
64
- type AppState,
65
- type AssistantId,
66
- DEFAULT_SCROLL_INTENT,
67
- type TabSession,
68
- type WorktreeRecord,
69
- } from '../state/types'
70
63
  import { saveCurrentWorkspace } from '../state/workspace-save'
71
64
  import { filterThemeIds } from '../ui/filter-themes'
72
65
  import { scrollGitDiff } from '../ui/git-view-controls'
@@ -173,14 +166,14 @@ function pasteSnippetToActiveGroup(ctx: SideEffectContext): void {
173
166
  const groupId = getGroupIdForTab(state.tabGroupMap, state.activeTabId)
174
167
  const groupTree = groupId != null && groupId !== '' ? state.layoutTrees[groupId] : null
175
168
  if (!groupTree) {
176
- pasteSnippetToTab(backend, state.activeTabId, activeTab, snippet, ctx.dispatch)
169
+ pasteSnippetToTab(backend, state.activeTabId, activeTab, snippet)
177
170
  return
178
171
  }
179
172
 
180
173
  for (const tabId of allLeafIds(groupTree)) {
181
174
  const tab = state.tabs.find((entry) => entry.id === tabId)
182
175
  if (tab) {
183
- pasteSnippetToTab(backend, tabId, tab, snippet, ctx.dispatch)
176
+ pasteSnippetToTab(backend, tabId, tab, snippet)
184
177
  }
185
178
  }
186
179
  }
@@ -294,7 +287,6 @@ export function createTabSession(
294
287
  buffer: '',
295
288
  command: customCommand ?? option.command,
296
289
  id: createTabId(),
297
- scrollIntent: DEFAULT_SCROLL_INTENT,
298
290
  status: 'starting',
299
291
  terminalModes: createDefaultTerminalModes(),
300
292
  title: option.label,
@@ -620,13 +612,7 @@ export function executeSideEffect(effect: SideEffect, ctx: SideEffectContext): v
620
612
  )
621
613
  return
622
614
  case 'paste-selected-snippet': {
623
- pasteSnippetToTab(
624
- backend,
625
- state.activeTabId,
626
- ctx.activeTab,
627
- getSelectedSnippet(state),
628
- dispatch
629
- )
615
+ pasteSnippetToTab(backend, state.activeTabId, ctx.activeTab, getSelectedSnippet(state))
630
616
  return
631
617
  }
632
618
  case 'paste-snippet-to-group': {
@@ -71,14 +71,13 @@ export function pasteSnippetToTab(
71
71
  backend: SessionBackend,
72
72
  activeTabId: string | null,
73
73
  activeTab: TabSession | undefined,
74
- snippet: SnippetRecord | undefined,
75
- dispatch?: (action: AppAction) => void
74
+ snippet: SnippetRecord | undefined
76
75
  ): void {
77
76
  if (!snippet || activeTabId == null || activeTabId === '' || !activeTab) {
78
77
  return
79
78
  }
80
79
 
81
- writePasteToTab(backend, activeTabId, activeTab, snippet.content, dispatch)
80
+ writePasteToTab(backend, activeTabId, activeTab, snippet.content)
82
81
  }
83
82
 
84
83
  export function handleDeleteSnippetEffect(
@@ -1,7 +1,7 @@
1
1
  import { type MutableRefObject, useEffect, useRef } from 'react'
2
2
 
3
3
  import type { SessionBackend } from '../session-backend/types'
4
- import type { AppAction, LayoutState, ScrollIntent } from '../state/types'
4
+ import type { AppAction, LayoutState } from '../state/types'
5
5
 
6
6
  import { attachCurrentSession } from './backend-attach-runtime'
7
7
  import { bindBackendRuntimeEvents } from './backend-runtime-events'
@@ -11,7 +11,6 @@ interface BackendRuntimeOptions {
11
11
  backend: SessionBackend
12
12
  dispatch: (action: AppAction) => void
13
13
  activeTabId: string | null
14
- activeTabScrollIntentRef: MutableRefObject<ScrollIntent | null>
15
14
  currentSessionId: string | null
16
15
  layoutRef: MutableRefObject<LayoutState>
17
16
  resizingRef: MutableRefObject<boolean>
@@ -27,7 +26,6 @@ export interface TabRuntimeControls {
27
26
 
28
27
  export function useBackendRuntime({
29
28
  activeTabId,
30
- activeTabScrollIntentRef,
31
29
  backend,
32
30
  currentSessionId,
33
31
  currentSessionWorkspaceSnapshot,
@@ -61,11 +59,10 @@ export function useBackendRuntime({
61
59
  return
62
60
  }
63
61
 
62
+ // The backend owns each tab's scroll position; a backgrounded tab keeps it
63
+ // in its own emulator, so switching back needs no re-anchor from here.
64
64
  backend.setActiveTab(activeTabId)
65
- if (activeTabId != null && activeTabId !== '' && activeTabScrollIntentRef.current) {
66
- backend.reapplyScrollIntent(activeTabId, activeTabScrollIntentRef.current)
67
- }
68
- }, [activeTabId, activeTabScrollIntentRef, backend, currentSessionId])
65
+ }, [activeTabId, backend, currentSessionId])
69
66
 
70
67
  useEffect(() => {
71
68
  return bindBackendRuntimeEvents({
@@ -60,6 +60,16 @@ interface MultiClickDragState {
60
60
  capturedLines: Map<number, TerminalLine>
61
61
  autoScrollTimer: ReturnType<typeof setInterval> | null
62
62
  autoScrollDir: -1 | 0 | 1
63
+ // A scroll command was sent to the backend and we are still waiting for the
64
+ // snapshot it produces. Gates the next auto-scroll tick so the backend's
65
+ // viewportY can never run ahead of the frontend mirror (which all the
66
+ // selection math reads) — the source of the copy-offset drift.
67
+ autoScrollPending: boolean
68
+ // The drag tab's viewport object last handled by the snapshot effect. Used
69
+ // to ignore snapshot churn from *other* tabs: updateTab keeps unchanged tabs
70
+ // by reference, so an unrelated tab's output must not clear autoScrollPending
71
+ // or re-extend the selection.
72
+ lastProcessedViewport: TabSession['viewport']
63
73
  }
64
74
 
65
75
  interface UseMouseHandlersOptions {
@@ -161,6 +171,7 @@ export function useMouseHandlers({
161
171
  }
162
172
  drag.autoScrollTimer = null
163
173
  drag.autoScrollDir = 0
174
+ drag.autoScrollPending = false
164
175
  }
165
176
 
166
177
  const canScroll = (tab: TabSession | undefined, dir: -1 | 1): boolean => {
@@ -174,14 +185,20 @@ export function useMouseHandlers({
174
185
  clearAutoScroll(drag)
175
186
  drag.autoScrollDir = dir
176
187
  drag.autoScrollTimer = setInterval(() => {
188
+ // Wait for the previous scroll's snapshot before issuing the next one, so
189
+ // the backend viewportY stays in lockstep with the frontend mirror.
190
+ if (drag.autoScrollPending) {
191
+ return
192
+ }
177
193
  const tab = stateRef.current.tabs.find((t: TabSession) => t.id === drag.tabId)
178
194
  if (!canScroll(tab, dir)) {
179
195
  clearAutoScroll(drag)
180
196
  return
181
197
  }
198
+ drag.autoScrollPending = true
182
199
  backend.scrollViewport(drag.tabId, dir)
183
200
  // The snapshot dispatch will trigger the viewport-change effect below,
184
- // which recaptures lines and re-extends the selection focus.
201
+ // which clears autoScrollPending, recaptures lines, and re-extends focus.
185
202
  }, AUTO_SCROLL_INTERVAL_MS)
186
203
  }
187
204
 
@@ -216,6 +233,16 @@ export function useMouseHandlers({
216
233
  const tab = stateRef.current.tabs.find((t: TabSession) => t.id === drag.tabId)
217
234
  if (!tab?.viewport) return
218
235
 
236
+ // The effect fires on any tab's snapshot (state.tabs is a fresh array), but
237
+ // the drag tab's viewport object only changes when *its* snapshot lands.
238
+ // Ignore churn from other tabs so the auto-scroll lockstep below stays 1:1.
239
+ if (tab.viewport === drag.lastProcessedViewport) return
240
+ drag.lastProcessedViewport = tab.viewport
241
+
242
+ // A fresh snapshot for the drag tab landed: the backend has caught up, so
243
+ // the auto-scroll loop may issue its next step.
244
+ drag.autoScrollPending = false
245
+
219
246
  captureViewportLines(drag, tab)
220
247
 
221
248
  const viewportRows = tab.viewport.lines.length
@@ -425,6 +452,7 @@ export function useMouseHandlers({
425
452
  anchorEndCol: selection.endCol,
426
453
  anchorStartCol: selection.startCol,
427
454
  autoScrollDir: 0,
455
+ autoScrollPending: false,
428
456
  autoScrollTimer: null,
429
457
  baseX: anchor.baseX,
430
458
  baseY: anchor.baseY,
@@ -433,6 +461,7 @@ export function useMouseHandlers({
433
461
  focusCol: selection.endCol,
434
462
  lastEventScreenX: event.x,
435
463
  lastEventScreenY: event.y,
464
+ lastProcessedViewport: tab.viewport,
436
465
  mode: selection.mode,
437
466
  tabId: targetTabId,
438
467
  target: selection.target,
@@ -121,15 +121,7 @@ export function useRendererBindings({
121
121
  cwd,
122
122
  now,
123
123
  })
124
- writeMacroExpansionToTab(
125
- backend,
126
- tabId,
127
- tab,
128
- match.triggerText.length,
129
- text,
130
- cursorOffset,
131
- dispatch
132
- )
124
+ writeMacroExpansionToTab(backend, tabId, tab, match.triggerText.length, text, cursorOffset)
133
125
  registerUndo(text, cursorOffset)
134
126
  return
135
127
  }
@@ -154,7 +146,7 @@ export function useRendererBindings({
154
146
  now,
155
147
  })
156
148
  // Trigger already erased above; eraseCount = 0 here.
157
- writeMacroExpansionToTab(backend, tabId, tab, 0, text, cursorOffset, dispatch)
149
+ writeMacroExpansionToTab(backend, tabId, tab, 0, text, cursorOffset)
158
150
  registerUndo(text, cursorOffset)
159
151
  } finally {
160
152
  inFlightAsyncExpansionRef.current.delete(tabId)
@@ -194,7 +186,7 @@ export function useRendererBindings({
194
186
  resetTrigger: (tabId) => triggerDetectorsRef.current.get(tabId)?.reset(),
195
187
  tryConsumeMacroUndo,
196
188
  writeToPty: (tabId, data, options) =>
197
- writeToTab(backend, tabId, activeTabRef.current, data, dispatch, options),
189
+ writeToTab(backend, tabId, activeTabRef.current, data, options),
198
190
  })
199
191
 
200
192
  const handlePasteEvent = (event: { bytes: Uint8Array; defaultPrevented?: boolean }) => {
@@ -230,7 +222,7 @@ export function useRendererBindings({
230
222
  return
231
223
  }
232
224
 
233
- writePasteToTab(backend, tabId, tab, payload, dispatch)
225
+ writePasteToTab(backend, tabId, tab, payload)
234
226
  }
235
227
 
236
228
  const handleSelection = (selection: OtuiSelection) => {
@@ -10,7 +10,7 @@ import {
10
10
 
11
11
  import type { TerminalContentOrigin } from '../input/raw-input-handler'
12
12
  import type { SessionBackend } from '../session-backend/types'
13
- import type { AppAction, AppState, ScrollIntent } from '../state/types'
13
+ import type { AppAction, AppState } from '../state/types'
14
14
  import type { MeasuredPaneRect } from './use-pane-size-report'
15
15
 
16
16
  import { getGitPaneWidthFromRatio } from '../state/git-pane-sizing'
@@ -44,7 +44,6 @@ function resizeSplitTabs(
44
44
  tabIds: string[],
45
45
  cols: number,
46
46
  rows: number,
47
- intents: Map<string, ScrollIntent>,
48
47
  options?: { sync?: boolean }
49
48
  ): void {
50
49
  const bounds = getTerminalBounds(cols, rows)
@@ -52,13 +51,13 @@ function resizeSplitTabs(
52
51
 
53
52
  forEachSplitPaneRect(Object.values(layoutTrees), bounds, (tabId, rect) => {
54
53
  const size = toTerminalContentSize(rect)
55
- backend.resizeTab(tabId, size.cols, size.rows, intents.get(tabId), options)
54
+ backend.resizeTab(tabId, size.cols, size.rows, options)
56
55
  resizedTabIds.add(tabId)
57
56
  })
58
57
 
59
58
  for (const id of tabIds) {
60
59
  if (!resizedTabIds.has(id)) {
61
- backend.resizeTab(id, cols, rows, intents.get(id), options)
60
+ backend.resizeTab(id, cols, rows, options)
62
61
  }
63
62
  }
64
63
  }
@@ -81,7 +80,6 @@ interface RunResizeCascadeArgs {
81
80
  rows: number
82
81
  layoutTrees: AppState['layoutTrees']
83
82
  stableTabIds: string[]
84
- intents: Map<string, ScrollIntent>
85
83
  sync: boolean
86
84
  }
87
85
 
@@ -89,7 +87,6 @@ function runResizeCascade({
89
87
  backend,
90
88
  cols,
91
89
  dispatch,
92
- intents,
93
90
  layoutTrees,
94
91
  resizingRef,
95
92
  resizingTimerRef,
@@ -107,9 +104,9 @@ function runResizeCascade({
107
104
  clearTimeout(resizingTimerRef.current)
108
105
  }
109
106
  if (hasSplits) {
110
- resizeSplitTabs(backend, layoutTrees, stableTabIds, cols, rows, intents, options)
107
+ resizeSplitTabs(backend, layoutTrees, stableTabIds, cols, rows, options)
111
108
  } else {
112
- backend.resizeAll(cols, rows, intents, options)
109
+ backend.resizeAll(cols, rows, options)
113
110
  }
114
111
  resizingTimerRef.current = setTimeout(() => {
115
112
  resizingRef.current = false
@@ -133,7 +130,6 @@ export function useTerminalResize({
133
130
  }: UseTerminalResizeOptions) {
134
131
  const resizingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
135
132
  const tabIdsRef = useRef<string[]>([])
136
- const intentsRef = useRef<Map<string, ScrollIntent>>(new Map())
137
133
  const handledBySyncRef = useRef(false)
138
134
  const sidebarMountedRef = useRef(false)
139
135
 
@@ -146,12 +142,6 @@ export function useTerminalResize({
146
142
  }
147
143
  const stableTabIds = tabIdsRef.current
148
144
 
149
- intentsRef.current = new Map(
150
- state.tabs
151
- .filter((t): t is typeof t & { scrollIntent: ScrollIntent } => t.scrollIntent !== undefined)
152
- .map((t) => [t.id, t.scrollIntent])
153
- )
154
-
155
145
  const activeTabIdRef = useRef(state.activeTabId)
156
146
  activeTabIdRef.current = state.activeTabId
157
147
  // Last size we pushed to the backend per tab, used to dedupe the measurement
@@ -176,7 +166,7 @@ export function useTerminalResize({
176
166
  return
177
167
  }
178
168
  measuredRef.current.set(tabId, { cols, rows })
179
- backend.resizeTab(tabId, cols, rows, intentsRef.current.get(tabId))
169
+ backend.resizeTab(tabId, cols, rows)
180
170
  },
181
171
  [backend, contentOriginRef]
182
172
  )
@@ -230,7 +220,6 @@ export function useTerminalResize({
230
220
  backend,
231
221
  cols: terminalSize.cols,
232
222
  dispatch,
233
- intents: intentsRef.current,
234
223
  layoutTrees: state.layoutTrees,
235
224
  resizingRef,
236
225
  resizingTimerRef,
@@ -259,7 +248,6 @@ export function useTerminalResize({
259
248
  backend,
260
249
  cols: terminalSize.cols,
261
250
  dispatch,
262
- intents: intentsRef.current,
263
251
  layoutTrees: state.layoutTrees,
264
252
  resizingRef,
265
253
  resizingTimerRef,
package/src/app.tsx CHANGED
@@ -258,8 +258,6 @@ export function App({
258
258
  activeTabIdRef.current = state.activeTabId
259
259
  const activeTabRef = useRef(activeTab)
260
260
  activeTabRef.current = activeTab
261
- const activeTabScrollIntentRef = useRef(activeTab?.scrollIntent ?? null)
262
- activeTabScrollIntentRef.current = activeTab?.scrollIntent ?? null
263
261
 
264
262
  const stateRef = useRef(state)
265
263
  stateRef.current = state
@@ -307,7 +305,6 @@ export function App({
307
305
 
308
306
  const { clearIdleTimer, clearStartupGrace, startStartupGrace } = useBackendRuntime({
309
307
  activeTabId: state.activeTabId,
310
- activeTabScrollIntentRef,
311
308
  backend,
312
309
  currentSessionId: state.currentSessionId,
313
310
  currentSessionWorkspaceSnapshot,
@@ -472,12 +472,7 @@ export async function runDaemon(): Promise<void> {
472
472
  case 'resizeClient': {
473
473
  const sessionId = requireSession(socket, attachedSessions)
474
474
  requireNegotiatedVersion(socket, negotiatedVersions)
475
- await manager.resize(
476
- sessionId,
477
- message.payload.cols,
478
- message.payload.rows,
479
- message.payload.intents
480
- )
475
+ await manager.resize(sessionId, message.payload.cols, message.payload.rows)
481
476
  sendOk(socket, message.id)
482
477
  break
483
478
  }
@@ -488,8 +483,7 @@ export async function runDaemon(): Promise<void> {
488
483
  sessionId,
489
484
  message.payload.tabId,
490
485
  message.payload.cols,
491
- message.payload.rows,
492
- message.payload.intent
486
+ message.payload.rows
493
487
  )
494
488
  sendOk(socket, message.id)
495
489
  break
@@ -508,17 +502,6 @@ export async function runDaemon(): Promise<void> {
508
502
  sendOk(socket, message.id)
509
503
  break
510
504
  }
511
- case 'reapplyScrollIntent': {
512
- const sessionId = requireSession(socket, attachedSessions)
513
- requireNegotiatedVersion(socket, negotiatedVersions)
514
- await manager.reapplyScrollIntent(
515
- sessionId,
516
- message.payload.tabId,
517
- message.payload.intent
518
- )
519
- sendOk(socket, message.id)
520
- break
521
- }
522
505
  case 'setActiveTab': {
523
506
  const sessionId = requireSession(socket, attachedSessions)
524
507
  requireNegotiatedVersion(socket, negotiatedVersions)
@@ -1,7 +1,6 @@
1
1
  import { EventEmitter } from 'node:events'
2
2
 
3
3
  import type {
4
- ScrollIntent,
5
4
  TabSession,
6
5
  TerminalModeState,
7
6
  TerminalSnapshot,
@@ -68,14 +67,8 @@ export class SessionManager extends EventEmitter<SessionManagerEvents> {
68
67
  this.getOrCreateRegistry(sessionId).write(tabId, data)
69
68
  }
70
69
 
71
- resize(
72
- sessionId: string,
73
- cols: number,
74
- rows: number,
75
- intents?: Map<string, ScrollIntent>,
76
- options?: { sync?: boolean }
77
- ): void {
78
- this.getOrCreateRegistry(sessionId).resizeAll(cols, rows, intents, options)
70
+ resize(sessionId: string, cols: number, rows: number, options?: { sync?: boolean }): void {
71
+ this.getOrCreateRegistry(sessionId).resizeAll(cols, rows, options)
79
72
  }
80
73
 
81
74
  resizeTab(
@@ -83,10 +76,9 @@ export class SessionManager extends EventEmitter<SessionManagerEvents> {
83
76
  tabId: string,
84
77
  cols: number,
85
78
  rows: number,
86
- intent?: ScrollIntent,
87
79
  options?: { sync?: boolean }
88
80
  ): void {
89
- this.getOrCreateRegistry(sessionId).resizeTab(tabId, cols, rows, intent, options)
81
+ this.getOrCreateRegistry(sessionId).resizeTab(tabId, cols, rows, options)
90
82
  }
91
83
 
92
84
  scroll(sessionId: string, tabId: string, deltaLines: number): void {
@@ -97,10 +89,6 @@ export class SessionManager extends EventEmitter<SessionManagerEvents> {
97
89
  this.getOrCreateRegistry(sessionId).scrollViewportToBottom(tabId)
98
90
  }
99
91
 
100
- reapplyScrollIntent(sessionId: string, tabId: string, intent: ScrollIntent): void {
101
- this.getOrCreateRegistry(sessionId).reapplyScrollIntent(tabId, intent)
102
- }
103
-
104
92
  setActiveTab(sessionId: string, tabId: string | null): void {
105
93
  this.getOrCreateRegistry(sessionId).setActiveTab(tabId)
106
94
  }