Skip to content

[AGE-3965] refactor(drive): virtualize surfaces + unify Files drawer - #5400

Merged
ardaerzin merged 25 commits into
release/v0.105.7from
fe-refactor/drive-surfaces
Jul 20, 2026
Merged

[AGE-3965] refactor(drive): virtualize surfaces + unify Files drawer#5400
ardaerzin merged 25 commits into
release/v0.105.7from
fe-refactor/drive-surfaces

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

Context

The drive Files surfaces (the chat Files rail, the config Files section, and the Files drawer) rendered the whole file tree in the DOM. An agent that cloned an 11k-file repo into its cwd froze the main thread on open (#5367). The drawer also fetched the entire tree up front, so opening a multi-GB mount blocked for seconds. On top of that, two separate drawer implementations had drifted apart.

What this does

Rewrites the drive/files surfaces around lazy, virtualized loading, and unifies the two drawers into one.

  • Virtualized surfaces ([bug] Playground freezes opening pre-105.3 sessions #5367). The tree, grid, and flat list window their rows via @tanstack/react-virtual, so a 12k-entry folder never floods the DOM. The old O(n²) folder inference is now O(n).
  • Lazy per-directory loading. The drawer loads one directory level at a time (depth=1 plus child counts) instead of the whole tree, so opening a huge mount is instant.
  • Flat view is a cursor-paginated recursive listing. A new backend endpoint GET /mounts/{id}/files/page streams path-sorted pages via the object store's start_after, pruning .git and gitignored directories (it jumps past node_modules instead of paging through it). The frontend loads pages on scroll, so first paint is fast regardless of mount size.
  • One unified Files drawer. The two implementations (build DriveDrawer and chat FilesWindow) are replaced by a single controlled DriveExplorer with list / grid / flat view modes, shared by both hosts.
  • Download all. A streaming zip of the whole drive (cwd plus the folded agent-files), read with bounded concurrency. On Chromium it streams straight to disk via the File System Access API.
  • Keyboard navigation. Arrow roving plus Cmd+Down / Cmd+Up (Finder-style open and go-out) across the tree, grid, and flat list.
  • Transitions. Skeleton-to-content crossfades and staggered reveals across all surfaces.
  • Also: right-click context menus (copy path, open, download), a git-repo details panel, and chat file-link surfaces.

The flat listing shape, before and after:

Before (whole tree, built client-side):

GET /mounts/{id}/files   -> every object under the prefix

After (one cursor page):

GET /mounts/{id}/files/page?path=agenta&cursor=<opaque>&limit=100
-> { files: [ ...path-sorted... ], next_cursor: "<opaque>" }

Tests / notes

  • Backend: unit tests for the paged listing in api/oss/tests/pytest/unit/test_mounts_file_ops.py. The streaming-archive mtime fix (milliseconds to seconds, which was producing 0-byte zips) is included here.
  • Frontend: type-clean (tsc) and lint-clean across the Drives module.
  • The Fern client was not regenerated for /files/page. The drive module calls it via axios, the same way it reaches its other binary/download routes. Noted as a follow-up if we want it in the generated client.
  • The last commit touches the chat file-link surfaces (AgentChatSlice conversation, markdown, runtime inspector).

What to QA

  • Open the Files drawer on a session with a large cwd (an agent that cloned a repo). It opens right away, and the tree loads folders as you expand them.
  • Switch to Flat view. Files stream in (first page fast), scroll to load more, and the header count matches the list. Navigate into a folder via the tree, then switch to Flat: it scopes to that folder.
  • Use "Download all" from the drawer's overflow menu. You get a zip of the whole drive (Chromium streams it to disk).
  • Keyboard: click empty grid space, then arrow-navigate. Enter opens, Cmd+Down drills into a folder (focus lands on its first item), Cmd+Up goes out.
  • Regression: the config Files section and the chat Files rail still show recent files and counts. Opening a file preview (text, image, pdf) still works.

Opening a session whose agent wrote a large file tree (11k+ files) into its
cwd froze the main thread 2.5s+ and re-froze on every session update.

Compute: replace O(n^2) folder inference in driveTree.ts with a precomputed
ancestor-directory index (O(1) lookup); compute the file list and total size in
one pass (driveFileStats). Split the useSessionDrive memo so structural work
depends only on the listings, not the file-activity ticks, and index the recency
stamping by path instead of rescanning every file per signal.

Render: the listing is fetched whole and was rendered one DOM node per file.
Virtualize the chat Files grid, the explorer folder view (VirtualTileGrid) and
the file tree (flattened + windowed, index-based keyboard nav) with
@tanstack/react-virtual; defer search with useDeferredValue. Make overscan the
thumbnail prefetch window (FileThumb margin 600px, grid overscan 4 rows) so tiles
load ahead of scroll.
…on scroll

Follow-up to virtualizing the drive surfaces, from testing an 11k-file drive.

Thumbnails: drop the IntersectionObserver gate (the grids are virtualized, so a
mounted tile is already in view) and cache thumbnail bytes in a dedicated query
with a short gcTime, so scrolling a tile out of the window and back no longer
refetches and flashes. Memoize FileThumb so grid re-renders on scroll don't touch
resolved tiles, and show a skeleton (not the type icon) while a thumbnail loads.

Grid: the chat Files grid browsed nothing — it flat-dumped every file, unusable
after an agent clones a repo. It now browses folders (breadcrumb + folder tiles),
with search flattening to matches across the whole tree. Treat .mdx as markdown so
docs repos show snippets.
…summary tier, client thumbnails, view modes

Backend (mounts): get_mount_files/list_files gain order+limit+total for a bounded
'latest N' listing, and are git-aware in every mode — .git plumbing is never
listed and .gitignore-matched paths are dropped (pathspec). Gitignore matching
walks ancestor directories (git's model) so deeply-nested descendants like pnpm's
node_modules/.pnpm/<pkg>/node_modules/... are ignored, and .gitignore files inside
already-ignored dirs are never read (no read-storm/timeout after an install).

Frontend: a lightweight summary tier (one merged latest-5 query per mount serving
both the count via total and the recents) for the always-mounted chrome (rail,
config Files section, runtime lens) so they never pull the whole tree; the browse
surfaces keep the full drive, gated on open. Client-side downscaled thumbnails
(cache the small data-URL, drop the original) keep memory bounded. FilesWindow
gains Flat / Folders / List views with one persistent toolbar (search + origin +
sort + show-hidden), responsive tile columns, and the shared DriveExplorer tree.
…ive fix + tests

- GET /mounts/{id}/files/page: path-sorted cursor pages (store list_objects_page via start_after),
  git-aware pruning that jumps past ignored dirs so node_modules is never paged through
- fix stream_mounts_archive mtime ms->s (was crashing the zip generator mid-stream -> 0-byte files)
- unit tests for the paged listing
…veal transitions, grid/flat keyboard nav, download-all, context menus
@linear-code

linear-code Bot commented Jul 20, 2026

Copy link
Copy Markdown

AGE-3965

@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. Frontend refactoring A code change that neither fixes a bug nor adds a feature labels Jul 20, 2026
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 20, 2026 6:59pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 11be96ca-f5eb-4bde-b83e-460528c9faad

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds git-aware mount listing with totals, shallow traversal, folder counts, streamed multi-mount ZIP archives, session drive summaries, lazy loading, virtualization, contextual file actions, thumbnails, repository metadata, and a unified session-scoped file drawer.

Changes

Mount browsing and archive backend

Layer / File(s) Summary
Listing contracts and service behavior
api/oss/src/core/mounts/*, api/oss/src/core/store/storage.py, api/oss/tests/pytest/unit/test_mounts_file_ops.py
Mount listings support git-aware pruning, shallow directory results, counts, capped totals, recency rollups, and ordered archive-member iteration.
API and query wiring
api/oss/src/apis/fastapi/mounts/*, web/packages/agenta-entities/src/session/api/*, web/packages/agenta-entities/src/session/state/mounts.ts
FastAPI and client APIs expose listing options, archive requests, totals, directory queries, retry controls, cache keys, and revalidation.
Session drive data
web/oss/src/components/Drives/useSessionDrive.ts, driveTree.ts, useLazyDriveTree.tsx, agentDrive.ts
Drive summaries, recency matching, folder counts, lazy directory loading, and combined file statistics are added.
Virtualized explorer and drawers
web/oss/src/components/Drives/DriveExplorer.tsx, VirtualTileGrid.tsx, FilesDrawer.tsx, SessionFilesDrawer.tsx, DriveExplorerSkeleton.tsx
The explorer uses virtualized tree/grid rendering, lazy loading, previews, keyboard navigation, chrome-mode headers, skeletons, and session-scoped drawer state.
Drive actions and integrations
web/oss/src/components/Drives/DriveItemContextMenu.tsx, driveMedia.ts, DriveFileRow.tsx, FileThumb.tsx, driveRepo.ts, chatFileRefs.tsx, ContextRail.tsx, StorageSection.tsx
Drive rows support copy/download actions, streamed archive downloads, cached thumbnails, repository metadata, inline chat file links, capped counts, and refreshed recents UI.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SessionFilesDrawer
  participant FilesDrawer
  participant DriveExplorer
  participant MountsRouter
  participant MountsService
  SessionFilesDrawer->>FilesDrawer: provide session-scoped drive state
  FilesDrawer->>DriveExplorer: mount controlled explorer
  DriveExplorer->>MountsRouter: request root or directory listing
  MountsRouter->>MountsService: list_files with browsing options
  MountsService-->>MountsRouter: return files and totals
  MountsRouter-->>DriveExplorer: return listing response
  DriveExplorer-->>FilesDrawer: render virtualized explorer
Loading

Possibly related PRs

  • Agenta-AI/agenta#5306: Related mount file metadata and listing interface changes used by the archive and browsing logic.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 60.29% which is sufficient. The required threshold is 60.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and clearly points to the main theme: drive-surface virtualization and Files drawer consolidation.
Description check ✅ Passed The description is broadly aligned with the drive-surface refactor, even though some details reflect earlier pagination/flat-view work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fe-refactor/drive-surfaces

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-07-20T19:49:32.334Z

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
web/oss/src/components/Drives/driveFlatFiles.ts (1)

60-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Raw axios call deviates from the Fern-client guideline.

This endpoint is fetched via raw axios with {params}, and the hook drives pagination through useState/useEffect rather than atomWithQuery. Both deviate from the web guidelines. This appears intentional (the PR notes the Fern client wasn't regenerated for /mounts/{id}/files/page); flagging so the follow-up to regenerate the client and move this onto the Fern client is tracked. As per coding guidelines: "New frontend API calls must use the Fern client via getAgentaSdkClient({host: getAgentaApiUrl()}), not raw axios" and "Use atomWithQuery with TanStack Query for API data fetching … do not use useEffect with manual fetching for new code."

Source: Coding guidelines

web/oss/src/components/Drives/chatFileRefs.tsx (1)

34-37: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Tighten the file-candidate heuristic to cut spurious existence checks.

/[./]/.test(t) matches almost any token with a dot or slash — abbreviations ("e.g."), version numbers ("v1.2.3"), decimals ("3.14"), and property-access chains ("user.name") all qualify as "file candidates." Since these aren't already known from records, each triggers OnDemandFileRef's on-demand content fetch once scrolled into view — a guaranteed-404 network request for ordinary prose/code that happens to contain a dot.

♻️ Possible tightening
 const fileCandidate = (text: string): string | null => {
     const t = text.trim().replace(/^\.?\/+/, "")
-    return t && /[./]/.test(t) ? t : null
+    // Require a path-ish shape: a slash, or a short trailing extension (avoids matching
+    // decimals, abbreviations, and dotted identifiers like `user.name`).
+    return t && /\/|\.[A-Za-z0-9]{1,8}$/.test(t) ? t : null
 }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a9075beb-2572-466d-9719-0ffa16499772

📥 Commits

Reviewing files that changed from the base of the PR and between 3588f13 and 656c220.

⛔ Files ignored due to path filters (4)
  • api/uv.lock is excluded by !**/*.lock
  • web/packages/agenta-api-client/src/generated/api/resources/mounts/client/Client.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/mounts/client/requests/GetMountFilesRequest.ts is excluded by !**/generated/**
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (46)
  • api/oss/src/apis/fastapi/mounts/models.py
  • api/oss/src/apis/fastapi/mounts/router.py
  • api/oss/src/apis/fastapi/mounts/utils.py
  • api/oss/src/core/mounts/dtos.py
  • api/oss/src/core/mounts/service.py
  • api/oss/src/core/store/storage.py
  • api/oss/tests/pytest/unit/test_mounts_file_ops.py
  • api/pyproject.toml
  • web/oss/package.json
  • web/oss/src/components/AgentChatSlice/AgentConversation.tsx
  • web/oss/src/components/AgentChatSlice/assets/markdown.tsx
  • web/oss/src/components/AgentChatSlice/components/Inspector/lenses/RuntimeLens.tsx
  • web/oss/src/components/AgentChatSlice/state/fileLinks.ts
  • web/oss/src/components/Drives/ContextRail.tsx
  • web/oss/src/components/Drives/DriveDrawer.tsx
  • web/oss/src/components/Drives/DriveExplorer.tsx
  • web/oss/src/components/Drives/DriveExplorerSkeleton.tsx
  • web/oss/src/components/Drives/DriveFileLinkProvider.tsx
  • web/oss/src/components/Drives/DriveFileRow.tsx
  • web/oss/src/components/Drives/DriveItemContextMenu.tsx
  • web/oss/src/components/Drives/FileThumb.tsx
  • web/oss/src/components/Drives/FilesDrawer.tsx
  • web/oss/src/components/Drives/FilesDrawerBody.tsx
  • web/oss/src/components/Drives/FilesWindow.tsx
  • web/oss/src/components/Drives/SessionFilesDrawer.tsx
  • web/oss/src/components/Drives/StorageFilesHeader.tsx
  • web/oss/src/components/Drives/StorageSection.tsx
  • web/oss/src/components/Drives/VirtualTileGrid.tsx
  • web/oss/src/components/Drives/agentDrive.ts
  • web/oss/src/components/Drives/chatFileRefs.tsx
  • web/oss/src/components/Drives/configDrive.ts
  • web/oss/src/components/Drives/driveFlatFiles.ts
  • web/oss/src/components/Drives/driveKinds.ts
  • web/oss/src/components/Drives/driveMedia.ts
  • web/oss/src/components/Drives/driveRepo.ts
  • web/oss/src/components/Drives/driveTree.ts
  • web/oss/src/components/Drives/fileMeta.tsx
  • web/oss/src/components/Drives/repoMeta.tsx
  • web/oss/src/components/Drives/useLazyDriveTree.tsx
  • web/oss/src/components/Drives/useSessionDrive.ts
  • web/oss/src/styles/globals.css
  • web/packages/agenta-entities/src/session/api/api.ts
  • web/packages/agenta-entities/src/session/api/client.ts
  • web/packages/agenta-entities/src/session/core/schema.ts
  • web/packages/agenta-entities/src/session/index.ts
  • web/packages/agenta-entities/src/session/state/mounts.ts
💤 Files with no reviewable changes (3)
  • web/oss/src/components/Drives/FilesDrawerBody.tsx
  • web/oss/src/components/Drives/FilesWindow.tsx
  • web/oss/src/components/Drives/DriveDrawer.tsx

Comment thread api/oss/src/apis/fastapi/mounts/models.py Outdated
Comment thread api/oss/src/apis/fastapi/mounts/utils.py
Comment thread api/oss/src/core/mounts/service.py
Comment thread web/oss/src/components/Drives/driveFlatFiles.ts Outdated
Dead exports left after the drawer unification / lazy-loading rewrite:
- gridArrowKeyDown (VirtualTileGrid owns grid nav now)
- useAgentDrive (eager agent-drive fetch, obsolete after lazy loading)
- driveFiles / driveTotalSize wrappers (last caller removed)
Also orders the RuntimeLens drive imports (import/order lint).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
web/oss/src/components/AgentChatSlice/components/Inspector/lenses/RuntimeLens.tsx (1)

19-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move cross-module state to a shared store.

This component in AgentChatSlice is importing the filesDrawerOpenAtomFamily atom directly from a component file (SessionFilesDrawer) in the Drives module, which tightly couples the modules.

As per coding guidelines: "Keep module-specific code within its module; move functionality shared across modules to the appropriate root components, hooks, assets, or types location" and "Prefer Jotai atoms for shared state, keep UI-only state local, place module state in module stores, and place cross-module state in the root store". Please extract filesDrawerOpenAtomFamily into a shared root store (or shared state file) so it can be imported cleanly across modules.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f3a413e-6722-435a-a4e2-e37f5a620305

📥 Commits

Reviewing files that changed from the base of the PR and between 656c220 and 3ac6af2.

📒 Files selected for processing (4)
  • web/oss/src/components/AgentChatSlice/components/Inspector/lenses/RuntimeLens.tsx
  • web/oss/src/components/Drives/agentDrive.ts
  • web/oss/src/components/Drives/driveKeyboard.ts
  • web/oss/src/components/Drives/driveTree.ts
💤 Files with no reviewable changes (1)
  • web/oss/src/components/Drives/driveKeyboard.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/oss/src/components/Drives/agentDrive.ts

…ders, surface flat-page errors

- ArchiveMount.mount_id typed as UUID so a malformed id is rejected at the boundary (422) instead of raising an uncaught ValueError (500) inside the archive handler.
- Content-Disposition filenames (single-file download + archive) routed through one RFC 6266 helper that strips quotes/control chars and adds a percent-encoded filename*, closing a header-injection vector.
- Flat-view page-load errors no longer force hasMore=false: the cursor is preserved and the footer becomes a retry affordance, so a mid-list failure is visible and recoverable.
- Rename loop var shadowing re.sub; tighten chat file-link heuristic to a path-ish shape to cut guaranteed-404 probes.
…niteQuery

Addresses the review note that driveFlatFiles used raw axios + a hand-rolled useState/useEffect fetch loop, deviating from the web data-fetching guidelines.

- Regenerate the TypeScript Fern client from the local OpenAPI so the new mount routes are typed: adds mounts.getMountFilesPage + mounts.archiveMountFiles and their models (purely additive).
- Add queryMountFilePage to @agenta/entities/session (Fern client via getMountsClient + projectScopedRequest + zod validation), mirroring queryMountFiles/queryLatestMountFiles.
- Rewrite useFlatFilesInfinite on TanStack Query's useInfiniteQuery (shared QueryClient): the two folded mounts thread through one {srcIdx, cursor} page chain via getNextPageParam. Same public API and behavior; gains cache-backed instant reopen, native in-flight de-dup and retry, and removes the manual ref/effect state machine and raw axios.
…idth expand

UI simplification per design feedback — one view, less chrome:

- Remove the list/grid/flat view switcher and the flat view entirely; the drawer is now the tree navigator (tree pane + content pane) with a show/hide toggle for the tree pane (first toolbar slot, above the tree). The toggle animates: the Splitter stays mounted and the tree pane collapses to width 0 via a gated transition class (mirrors the playground splitter pattern; off during drag so resize stays 1:1).
- Fix QA's nested tree scrollbars: box-border on the tree container (preflight-off content-box was overflowing the overflow:auto Splitter panel).
- Add the app's full-width drawer-expand pattern (ArrowsOut/ArrowsIn in the header; 960px <-> clamp(960px,92vw,1800px), reset on close).
- Files-opener icon: ArrowSquareOut (read as 'open new tab') -> FolderOpen, in BOTH the config StorageFilesHeader and the chat ContextRail.

Dead code removed after dropping the flat view (FE + BE):
- FE: driveFlatFiles.ts, entity queryMountFilePage + schema + exports.
- BE: GET /mounts/{id}/files/page route + handler, list_files_page, list_objects_page, _first_ignored_ancestor_dir, MountFilePageResponse.
- Generated client: dropped getMountFilesPage + its request/response types (the /files/archive download-all route is untouched).
@mmabrouk

Copy link
Copy Markdown
Member

Hi @ardaerzin, following the backend review of this PR, I opened two small stacked PRs that implement the agreed pre-merge fixes so this can land on a sound base:

The intended order is #5411 into this branch, then #5412 into #5411, then this PR into main. Nothing here is merged; that stays your and Mahmoud's call. Happy to move any of it around or discuss the approach. Review and comments welcome on the two PRs.

mmabrouk added 2 commits July 20, 2026 17:16
…of a broken 200

The download-all archive resolved each mount lazily inside the streaming
generator, after the 200 headers were sent, so MountNotFound / MountStorageUnavailable
never reached @handle_mount_exceptions and a bad mount id yielded a corrupt zip.
Split iter_archive_members into build_archive_work_list (resolve + list, awaited
before the StreamingResponse is built) and iter_archive_members (prefetch/yield only),
so those errors surface as real 404/503 through the decorators.

Claude-Session: https://claude.ai/code/session_01PTkjdoAPbhSEFR3ubpgzBU
The raw (non-git-aware) count-only path ran a full uncapped list_objects_v2 and
hardcoded truncated=False, so a huge tree was enumerated whole. Page it bounded at
_COUNT_CAP and report total_capped=True when more remain, matching the git-aware
branch's bounded-count contract.

Re-adds the generic ObjectStore.list_objects_page pagination primitive (removed
alongside the flat Files view); this bounded count is now its sole consumer.

Claude-Session: https://claude.ai/code/session_01PTkjdoAPbhSEFR3ubpgzBU
@mmabrouk

Copy link
Copy Markdown
Member

Confirmed out-of-band: the flat/paged view is gone for good. I've rebased #5411 and #5412 onto 387c62ebef accordingly — dropped the pager fix as moot (its target code is removed) and kept the archive, count-cap, and test hardening on #5411, with #5412 (path-validation relax, archive hardening, export rename, depth/order guards) unaffected. Both are green on the rebased tip.

mmabrouk added 2 commits July 20, 2026 16:46
fix(api): relax drive path validation, harden archive, rename export route
fix(api): eager archive errors, capped count-only listing, rollup tests
@mmabrouk
mmabrouk changed the base branch from main to release/v0.105.7 July 20, 2026 15:48
Replace the antd Splitter tree/content split with a plain motion.div whose width animates 0<->width in one continuous pass, with the content pane flex-filling alongside. Fixes the double-resize: antd's Splitter re-derived panel flex-basis via its own ResizeObserver AFTER the CSS-transition window, snapping the width a second time (and re-flowing the grid twice). A motion tween has no such re-derivation.

- Tree width is draggable via a lightweight pointer handle (thin 1px line + wide hit area, accent on hover/drag), persisted across a hide/show, clamped 180-480px; drag follows the pointer 1:1 (tween off), the toggle animates.
- Grid keeps its robust CSS-only sizing (tiles resize via grid 1fr as the pane grows); no motion-layout on the tiles (it fought the CSS resize and janked). The column count snaps once when it crosses a threshold.
- Remove the now-dead ag-drive-tree-splitter globals.css rules.
…me-perfect tree drag

Give the tile grid's geometry a single owner — motion — so the column reflow finally animates without fighting CSS:

- VirtualTileGrid drops the CSS grid entirely: each visible tile is an absolutely positioned motion.div whose x/y/width the component computes from the measured pane width. Same column count → targets apply with duration 0 (1:1 tracking, like 1fr); a column flip → a short spring window (detected during render, so the first frame animates). Virtualization is unchanged — rows window exactly as before, fed by uniform tile heights (width x 3/4 + measured text-block constant).
- Anticipated reflow: on a tree-pane toggle, DriveExplorer announces the width shift, and the grid lays out ONCE for the final rest width and springs every tile straight to its final slot — one monotonic morph, no grow-then-shrink through the threshold. Chained announcements resolve rapid re-toggles; self-clears when the live width arrives (or 600ms safety).
- Two overdamped springs by cause: soft pane-matched (400/46) for anticipated toggles, tight (700/60) for live mid-drag flips so tiles stay on the pointer's heels. 12px hysteresis stops threshold flip-flop when parking the drag handle on a boundary.
- Tree drag rides a MotionValue pair (paneW/innerW): the pointer writes the DOM directly through motion's frame loop — zero React renders per move (state-per-move re-rendered the whole explorer per pointer event). Rest width commits once at drag end; the toggle animates the same value, so drag and toggle share one owner. Inner content reflows during a drag but clips during a collapse.
- overflow-x-hidden on the grid scroll container: during an anticipated widen, tiles already sit at their final (wider) slots — the new column slides in from the clipped edge instead of flashing a horizontal scrollbar.
@ardaerzin
ardaerzin merged commit 9e589fa into release/v0.105.7 Jul 20, 2026
37 of 40 checks passed
pull Bot pushed a commit to kp-forks/agenta that referenced this pull request Jul 22, 2026
What was tackled (persistence layer, cold-load request reduction, boot render
gates, session-UI render cost, request dedup, latest-revision persistence), what
was deliberately left alone and why (Phase 2 list persistence, the sidebar Menu,
deep config-panel work, org persistence, the billing 502, the serial boot gate
chain), and the learnings worth not relearning.

Explicitly scoped OUT: the drive/file/mount surfaces, which ship separately in
PR Agenta-AI#5400 that this branch is stacked on.
mmabrouk added a commit to mmabrouk/agenta that referenced this pull request Jul 27, 2026
Collects design and go-to-market material that was sitting uncommitted in the
working tree so it survives a workspace cleanup.

Design and research notes (docs/design/agent-workflows/):
- selfhost-hardening plan and the runner engine-quality roadmap
- research on client-tool delivery and concurrent human-in-the-loop approvals
- the desloppify round-two assessment
- root-cause writeups for warm-resume execution and frontend approval dispatch
- the PR Agenta-AI#5400 mounts review handoff, review report and fix brief
- the retheme hex audit and the stale positioning sweep

Go-to-market material (docs/design/gtm/), moved out of the repo root:
- the go-to-market wiki (was gtm.txt)
- the README rewrite draft (was readme.new.md)
- pricing page revision 3 (was website/new-pricing.json, unreferenced by the site)
- the sign-in redesign handoff with its logos and design tokens
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Frontend refactoring A code change that neither fixes a bug nor adds a feature size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants