AIClaw Multi-File Review Upgrade: Docked Tabs Preserve Reading Position

AIClaw uses docked multi-file tabs and independent reading state so reviews don't lose context when switching.
AIClaw introduced a docked reading area with multi-file tabs in v3.7.11, and added independent reading state per file in v3.7.12, aiming to solve the problem of losing context when switching tabs during multi-file review. The app places the reader next to the chat window; normal windows automatically expand by up to 640 pixels and restore on close, while maximized and full-screen skip the adjustment. Each open file has its own view, diff layout, and scroll position; cache reuse doesn't trigger a disk refresh—use Refresh to reread and Compare to check change records. To handle background read races, the state store uses request identifiers to verify tab and session ownership, avoiding stealing the user's currently selected tab. Smoke tests cover tab reuse, position restoration, and ignoring late results, but suite totals aren't tab-specific test counts and don't prove native window behavior on each platform.
The Pain Point of Multi-File Review: Losing Context When Switching Tabs
Reviewing the output of an AI Agent often requires jumping back and forth between multiple files: a Markdown description, a SQL query, and a change record linking the two. If every switch loses your reading position—or replaces the conversation you're currently cross-checking—the value of the preview feature drops sharply.
AIClaw introduced a docked reading area in v3.7.11, supporting multi-file tabs; the subsequent v3.7.12 added independent reading state per file. The relevant changes were released on October 13, and the latest public version v3.7.14 still retains this implementation. Earlier, in the article Separation of Preview and Change Records, the author already explained the positioning of each of the two views. This article focuses on another engineering problem: how to keep the review process usable when moving between multiple files.
Opening Up Reading Space Beside the Conversation
The application layout places the reader next to the chat window. The layout design of App.vue keeps the chat always mounted, while the reading area independently displays the currently selected file.
For normal windows, the Electron host process attempts to add up to 640 pixels of width, capped by the available area of the current display. It remembers the original horizontal position and width, and restores them when the reading area is closed—likewise constrained by the available display area.
Maximized and full-screen windows skip this automatic adjustment. On small screens the space for each panel is still limited: docking doesn't magically create screen capacity. Its real value is making the conversation and file visible at the same time when space allows.
Electron is a framework for building cross-platform desktop applications with web technologies (HTML/CSS/JavaScript), and its architecture is divided into two layers: the main process and the renderer process. The main process runs in a Node.js environment with OS-level capabilities, responsible for creating windows, managing menus, accessing the file system, and controlling window size and position; the renderer process is essentially a browser page responsible for UI rendering. The two exchange messages via IPC (inter-process communication). The host process code mentioned in the article points to the main process entry, which is why it has permission to adjust window width, record the original position, and restore it after closing the reading area. This division of responsibilities also explains why window resizing happens in the main process, while reading state is stored in the renderer process's state store.
Every Open File Has Its Own Position
The preview state store maintains an array of file previews and an independent active selection. Opening a file that already exists in the current session reuses its tab and cached content; different files get a new tab.
The tab bar supports mouse selection as well as keyboard operations: left/right arrow keys, Home, and End. Closing the active tab selects the adjacent tab; closing a non-active tab doesn't affect the current selection; closing the last tab collapses the entire reader.
Cache reuse has a practical consequence: reopening the same file doesn't refresh from disk. When you need to reread a file, use Refresh; Compare looks up the latest change record for that file within its session. These two operations are independent—switching tabs doesn't turn a historical snapshot into the current file on disk.
Views and Scroll Positions Are Stored Together
The reader component stores the current view, diff layout, and scroll position together in the file's preview state.
Position keys are separate: source code, rendered Markdown, before/after snapshots, and each diff layout are independent. This is crucial—positions in rendered Markdown can't reliably map back to source positions, and the geometries of unified diff and side-by-side diff are completely different.
Picture a typical workflow: reading the middle of a report in render mode, switching to a SQL file to view unified diff mode, then returning to the report still at the rendered position. When switching back to SQL, the previously selected diff layout is preserved too. Both horizontal and vertical positions are recorded.
These states belong to the reading tabs open in memory. Closing a tab removes the state; switching sessions or leaving the chat view clears the reading area. It doesn't promise to restore open files after an app restart, and refreshing a file shouldn't be understood as preserving exact semantic positions after content changes.
Unified diff and side-by-side diff are two common diff display formats. Unified diff places deleted and added lines in the same column, marked with prefixes, keeping line counts compact and suitable for narrow panels; side-by-side diff places the old and new versions in left and right columns for line-by-line comparison, but requires wider display space. For the same change, the two have completely different line numbers, line heights, and scroll offsets, so they can't share the same scroll position. The article emphasizes that each diff layout stores its position key independently, precisely to avoid incorrectly mapping a position to another geometry when switching layouts, which would cause reading to jump.
Background Reads Shouldn't Steal Your Next Tab
Multiple tabs introduce a race condition that a single preview can't clearly express. Suppose file A is still loading when you select file B, which has already loaded. A's final result should update A, not grab the selection back from B.
The state store assigns a request identifier to each read. When the read completes, it checks whether the corresponding tab still exists and whether the session still matches, then updates the tab without unconditionally activating it. For tabs closed during the read, their results are ignored; results from a previous session won't reopen that session's reader in a new conversation.
These checks protect the association between the response and the target tab, but they don't cancel the underlying read, nor do they constitute a separate file system permission mechanism.
A race condition refers to a defect where the completion order of multiple asynchronous operations is uncertain, causing the result to depend on timing. In a multi-tab scenario, the user triggers a read of file A, then switches to file B which has already loaded; if A's read result callback sets the current selection without any check, it forces the view back from B to A—a classic race condition. The article uses a request identifier to give each read a unique tag; upon completion it first verifies whether the tab still exists and whether the session still matches, then decides whether to update. This pattern makes "which request does the response belong to" explicit, and is a common technique in frontend asynchronous state management for handling late responses.
Test Coverage and Boundaries
The public desktop smoke test uses real Vue/preload interfaces with fixture host responses, covering the following scenarios: tab reuse doesn't trigger an extra read, source and rendered scroll positions are restored, unified diff is preserved, keyboard switching, background completion doesn't change the selection, and late results for closed tabs are ignored.
The v3.7.11 release notes record 104 work-interface checks and simulated window boundary cases; v3.7.12 records a final total of 133 work-interface checks. These are suite totals, not the number of tab-specific tests. They don't prove native window behavior on every OS and every display arrangement. The author states that this article is based on inspection of the tagged-version implementation and test definitions, without re-running the suites.
A smoke test is a broad but shallow test used to quickly confirm that the system's main functional paths work, rather than exhaustively covering all boundaries and combinations. It typically serves as the first gate before a build or release, and failure indicates an obvious problem. The desktop smoke test in the article uses real Vue and preload interfaces with fixture host responses to simulate the window environment, thereby verifying tab interactions without launching a full Electron host. It's worth noting that passing a smoke test only shows the core paths work; it can't replace native window behavior verification for every OS and every display arrangement.
What This Change Means
The genuinely useful change is continuity during review: every open file keeps its own view and position, and asynchronous work only updates the file it belongs to, rather than deciding for you what to read next.
For developers who frequently cross-check between documents, SQL, and change records, this continuity reduces the cost of repeated re-location. It doesn't solve the physical limitation of insufficient screen space, nor does it promise state persistence across restarts, but in the specific scenario of multi-file review, it closes the loop at the implementation level on "not losing context."

Related articles

macOS Is No Longer UNIX: Apple Didn't Renew Certification, But the Technology Is Unchanged
Apple did not renew macOS's UNIX certification, and the Open Group has removed Apple's entry. This article analyzes the technical reality behind the expiration, Apple's possible intentions, and the practical impact on developers.

Building Stateful Multi-Agent Systems with Azure Cosmos DB
Based on the official Azure Cosmos DB tutorial, this article explains how to use the Agent Memory Toolkit to add shared persistent memory to a multi-agent travel planner, covering short-term, long-term, episodic, and semantic memory plus cross-session state continuity.

Production AI Agent Operations: A Three-Step Framework for Observing, Evaluating, and Optimizing
Azure Cosmos DB team proposes a three-step framework for production AI Agent operations: observe trace tracking, evaluate with LLM as a judge and regression testing, optimize cost across RU, retrieval, memory, and LLM layers.