Skip to content

feat(frontend): @agenta/entity-ui/drive — the drive explorer, tree and file surfaces leave the app - #5877

Draft
ardaerzin wants to merge 1 commit into
pkg/entities-drivefrom
pkg/entity-ui-drive
Draft

feat(frontend): @agenta/entity-ui/drive — the drive explorer, tree and file surfaces leave the app#5877
ardaerzin wants to merge 1 commit into
pkg/entities-drivefrom
pkg/entity-ui-drive

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

The rendered half of the drive: the explorer, the tree and the file surfaces, on top of the
headless layer from the lane below.
Not run in a browser — static gates only (pnpm lint-fix 24/24, tsc --noEmit clean
for @agenta/shared, ui, entities, entity-ui, settings-ui, oss, ee, mobile).

Stacked on pkg/entities-drive; review only this lane's diff.

@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error Aug 11, 2026 5:10am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 7416fd1c-e520-40a6-a7e1-23775bb4bfa2

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
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added responsive, virtualized file browsing with keyboard navigation, infinite scrolling, and autofocus support.
    • Added customizable Markdown and code-block rendering for file previews.
    • Added session-aware file and artifact context for drive views.
    • Added streamlined single-file downloads with loading, success, and error notifications.
    • Added improved loading placeholders for drive explorers and storage sections.
  • UI Improvements

    • Updated drive menus, tooltips, buttons, labels, previews, and animations for a more consistent interface.

Walkthrough

The PR centralizes drive components under shared Agenta packages. It adds session and renderer APIs, updates storage and messaging flows, introduces VirtualTileGrid, and migrates OSS drive consumers.

Changes

Drive UI centralization

Layer / File(s) Summary
Shared drive APIs and exports
web/packages/agenta-entity-ui/src/drive/DriveBreadcrumb.tsx, driveMarkdown.tsx, driveSessionContext.tsx, quickLook.tsx, index.ts, useDriveFileDownload.ts
Adds shared drive context, renderer registration, quick-look state, download handling, barrel exports, and entity-backed types.
Drive component migration
web/packages/agenta-entity-ui/src/drive/ContextRail.tsx, DriveExplorer.tsx, DriveHeader.tsx, DriveItemContextMenu.tsx, DriveFile*.tsx, DriveTree*.tsx, Folder*.tsx, OriginTag.tsx
Replaces local drive and Ant Design dependencies with shared drive entities and Agenta UI components.
Session storage and file rendering
web/packages/agenta-entity-ui/src/drive/Storage*.tsx, renderers.tsx, driveFileSource.tsx, useDrive*.ts, useLazyDriveTree.tsx, useUploadReveal.ts, tests/unit/useUploadReveal.test.ts
Adds session-scoped storage inputs, shared loading states, registered renderers, centralized messaging, and updated test imports.
Virtualized tile grid
web/packages/agenta-entity-ui/src/drive/VirtualTileGrid.tsx
Adds responsive tile virtualization, animated reflow, infinite scrolling, autofocus, and keyboard navigation.
OSS drive integration
web/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsx, web/oss/src/components/Drives/*, web/oss/src/components/pages/overview/agent/AgentFilesCard.tsx
Migrates OSS consumers to shared drive packages and adds scoped chat session resolution.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the frontend drive explorer, tree, and file surfaces moved into the shared entity-ui package.
Description check ✅ Passed The description accurately summarizes the rendered drive surfaces, validation status, browser-testing limitation, and stacked PR context.
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.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pkg/entity-ui-drive

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.

@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/packages/agenta-entity-ui/src/drive/StorageSection.tsx (1)

87-106: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Isolate config Files drawer state by session.

AgentOperationsSections forwards the same StorageSection element for revisionId across agents, but that component now selects a different sessionId drive via useConfigDrive. Its drawer atom is still keyed only to revisionId, so staging and initialPath can persist when the passed session changes. Include the active session in the drawer state or reset the drawer when sessionId changes.

🧹 Nitpick comments (4)
web/packages/agenta-entity-ui/src/drive/VirtualTileGrid.tsx (3)

1-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Trim the comment volume to match the repository comment rule.

The guidelines allow long comments only for genuinely surprising constraints. Several blocks here explain routine layout math and rejected alternatives (for example lines 18-19 and 43-48). Keep the setState-in-render and spring-window notes, which are genuine ordering constraints. Move the design rationale and history to the PR description or an ADR, and reduce the rest to one short line each.

As per coding guidelines: "Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements."

Source: Coding guidelines


277-293: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard the focus retry loop against overlap and unmount.

Every focusCell call starts an independent requestAnimationFrame chain. A held arrow key starts several chains that compete for focus, and the last one to resolve wins. No chain is cancelled when the component unmounts.

Store the frame id in a ref, cancel the previous chain before starting a new one, and cancel on unmount.

♻️ Proposed refactor
+    const focusFrame = useRef<number | null>(null)
+    useEffect(() => () => {
+        if (focusFrame.current !== null) cancelAnimationFrame(focusFrame.current)
+    }, [])
     const focusCell = (index: number) => {
         if (!items.length) return
         const target = Math.min(Math.max(index, 0), items.length - 1)
         virtualizer.scrollToIndex(Math.floor(target / cols), {align: "auto"})
+        if (focusFrame.current !== null) cancelAnimationFrame(focusFrame.current)
         let tries = 0
         const run = () => {
             const cell = parentRef.current?.querySelector<HTMLElement>(
                 `[data-grid-cell="${target}"]`,
             )
             const focusable = cell?.querySelector<HTMLElement>(
                 "button, [href], [tabindex]:not([tabindex='-1'])",
             )
             if (focusable) focusable.focus()
-            else if (tries++ < 4) requestAnimationFrame(run)
+            else if (tries++ < 4) focusFrame.current = requestAnimationFrame(run)
         }
-        requestAnimationFrame(run)
+        focusFrame.current = requestAnimationFrame(run)
     }

355-406: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider exposing grid semantics to assistive technology.

The container is focusable and implements 2D roving focus, but the DOM carries no grid semantics. data-grid-cell is a query hook only. Screen readers announce a plain scrollable region, so the row and column position is not available.

Add role="grid" on the scroll container, and role="gridcell" plus aria-colindex and aria-rowindex on each motion.div. Also set aria-rowcount={rowCount} and aria-colcount={cols}. Verify with a screen reader before merge, because the absolutely positioned tiles have no intermediate role="row" element and some readers require one.

web/packages/agenta-entity-ui/src/drive/ContextRail.tsx (1)

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

Extract the shared secondary-text component.

Text has the same implementation in three drive modules. Move it to one shared drive UI module and import it at each site.

  • web/packages/agenta-entity-ui/src/drive/ContextRail.tsx#L35-L45: replace the local Text declaration with the shared component.
  • web/packages/agenta-entity-ui/src/drive/DriveFileCard.tsx#L23-L33: replace the local Text declaration with the shared component.
  • web/packages/agenta-entity-ui/src/drive/DriveTreeList.tsx#L19-L29: replace the local Text declaration with the shared component.

As per coding guidelines, “Extract reusable components only when used in three or more places.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 3dc61029-fd15-46ef-b950-9814652ca9b2

📥 Commits

Reviewing files that changed from the base of the PR and between e2d62a8 and 892272f.

📒 Files selected for processing (44)
  • web/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsx
  • web/oss/src/components/Drives/chatFileRefs.tsx
  • web/oss/src/components/Drives/useChatScopeSessionId.ts
  • web/oss/src/components/pages/overview/agent/AgentFilesCard.tsx
  • web/packages/agenta-entity-ui/src/drive/ContextRail.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveBreadcrumb.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveExplorer.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveExplorerSkeleton.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveExplorerStates.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFileCard.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFileContentViewer.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFilePreview.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFileRow.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveHeader.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveItemContextMenu.tsx
  • web/packages/agenta-entity-ui/src/drive/DrivePendingTiles.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveToolbar.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveTreeList.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveTreePane.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveTreeRow.tsx
  • web/packages/agenta-entity-ui/src/drive/FileThumb.tsx
  • web/packages/agenta-entity-ui/src/drive/FilesDrawer.tsx
  • web/packages/agenta-entity-ui/src/drive/FolderTile.tsx
  • web/packages/agenta-entity-ui/src/drive/FolderView.tsx
  • web/packages/agenta-entity-ui/src/drive/OriginTag.tsx
  • web/packages/agenta-entity-ui/src/drive/SessionFilesDrawer.tsx
  • web/packages/agenta-entity-ui/src/drive/StorageFilesHeader.tsx
  • web/packages/agenta-entity-ui/src/drive/StorageSection.tsx
  • web/packages/agenta-entity-ui/src/drive/VirtualTileGrid.tsx
  • web/packages/agenta-entity-ui/src/drive/driveFileSource.tsx
  • web/packages/agenta-entity-ui/src/drive/driveIcons.tsx
  • web/packages/agenta-entity-ui/src/drive/driveMarkdown.tsx
  • web/packages/agenta-entity-ui/src/drive/driveSessionContext.tsx
  • web/packages/agenta-entity-ui/src/drive/fileMeta.tsx
  • web/packages/agenta-entity-ui/src/drive/index.ts
  • web/packages/agenta-entity-ui/src/drive/quickLook.tsx
  • web/packages/agenta-entity-ui/src/drive/renderers.tsx
  • web/packages/agenta-entity-ui/src/drive/repoMeta.tsx
  • web/packages/agenta-entity-ui/src/drive/useDriveDownloadAll.ts
  • web/packages/agenta-entity-ui/src/drive/useDriveFileDownload.ts
  • web/packages/agenta-entity-ui/src/drive/useDriveTreeData.ts
  • web/packages/agenta-entity-ui/src/drive/useLazyDriveTree.tsx
  • web/packages/agenta-entity-ui/src/drive/useUploadReveal.ts
  • web/packages/agenta-entity-ui/tests/unit/useUploadReveal.test.ts

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/packages/agenta-entity-ui/src/drive/StorageSection.tsx (1)

87-106: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Isolate config Files drawer state by session.

AgentOperationsSections forwards the same StorageSection element for revisionId across agents, but that component now selects a different sessionId drive via useConfigDrive. Its drawer atom is still keyed only to revisionId, so staging and initialPath can persist when the passed session changes. Include the active session in the drawer state or reset the drawer when sessionId changes.

🧹 Nitpick comments (4)
web/packages/agenta-entity-ui/src/drive/VirtualTileGrid.tsx (3)

1-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Trim the comment volume to match the repository comment rule.

The guidelines allow long comments only for genuinely surprising constraints. Several blocks here explain routine layout math and rejected alternatives (for example lines 18-19 and 43-48). Keep the setState-in-render and spring-window notes, which are genuine ordering constraints. Move the design rationale and history to the PR description or an ADR, and reduce the rest to one short line each.

As per coding guidelines: "Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements."

Source: Coding guidelines


277-293: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard the focus retry loop against overlap and unmount.

Every focusCell call starts an independent requestAnimationFrame chain. A held arrow key starts several chains that compete for focus, and the last one to resolve wins. No chain is cancelled when the component unmounts.

Store the frame id in a ref, cancel the previous chain before starting a new one, and cancel on unmount.

♻️ Proposed refactor
+    const focusFrame = useRef<number | null>(null)
+    useEffect(() => () => {
+        if (focusFrame.current !== null) cancelAnimationFrame(focusFrame.current)
+    }, [])
     const focusCell = (index: number) => {
         if (!items.length) return
         const target = Math.min(Math.max(index, 0), items.length - 1)
         virtualizer.scrollToIndex(Math.floor(target / cols), {align: "auto"})
+        if (focusFrame.current !== null) cancelAnimationFrame(focusFrame.current)
         let tries = 0
         const run = () => {
             const cell = parentRef.current?.querySelector<HTMLElement>(
                 `[data-grid-cell="${target}"]`,
             )
             const focusable = cell?.querySelector<HTMLElement>(
                 "button, [href], [tabindex]:not([tabindex='-1'])",
             )
             if (focusable) focusable.focus()
-            else if (tries++ < 4) requestAnimationFrame(run)
+            else if (tries++ < 4) focusFrame.current = requestAnimationFrame(run)
         }
-        requestAnimationFrame(run)
+        focusFrame.current = requestAnimationFrame(run)
     }

355-406: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider exposing grid semantics to assistive technology.

The container is focusable and implements 2D roving focus, but the DOM carries no grid semantics. data-grid-cell is a query hook only. Screen readers announce a plain scrollable region, so the row and column position is not available.

Add role="grid" on the scroll container, and role="gridcell" plus aria-colindex and aria-rowindex on each motion.div. Also set aria-rowcount={rowCount} and aria-colcount={cols}. Verify with a screen reader before merge, because the absolutely positioned tiles have no intermediate role="row" element and some readers require one.

web/packages/agenta-entity-ui/src/drive/ContextRail.tsx (1)

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

Extract the shared secondary-text component.

Text has the same implementation in three drive modules. Move it to one shared drive UI module and import it at each site.

  • web/packages/agenta-entity-ui/src/drive/ContextRail.tsx#L35-L45: replace the local Text declaration with the shared component.
  • web/packages/agenta-entity-ui/src/drive/DriveFileCard.tsx#L23-L33: replace the local Text declaration with the shared component.
  • web/packages/agenta-entity-ui/src/drive/DriveTreeList.tsx#L19-L29: replace the local Text declaration with the shared component.

As per coding guidelines, “Extract reusable components only when used in three or more places.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 3dc61029-fd15-46ef-b950-9814652ca9b2

📥 Commits

Reviewing files that changed from the base of the PR and between e2d62a8 and 892272f.

📒 Files selected for processing (44)
  • web/oss/src/components/AgentChatSlice/components/AttachmentViewerDrawer.tsx
  • web/oss/src/components/Drives/chatFileRefs.tsx
  • web/oss/src/components/Drives/useChatScopeSessionId.ts
  • web/oss/src/components/pages/overview/agent/AgentFilesCard.tsx
  • web/packages/agenta-entity-ui/src/drive/ContextRail.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveBreadcrumb.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveExplorer.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveExplorerSkeleton.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveExplorerStates.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFileCard.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFileContentViewer.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFilePreview.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveFileRow.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveHeader.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveItemContextMenu.tsx
  • web/packages/agenta-entity-ui/src/drive/DrivePendingTiles.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveToolbar.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveTreeList.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveTreePane.tsx
  • web/packages/agenta-entity-ui/src/drive/DriveTreeRow.tsx
  • web/packages/agenta-entity-ui/src/drive/FileThumb.tsx
  • web/packages/agenta-entity-ui/src/drive/FilesDrawer.tsx
  • web/packages/agenta-entity-ui/src/drive/FolderTile.tsx
  • web/packages/agenta-entity-ui/src/drive/FolderView.tsx
  • web/packages/agenta-entity-ui/src/drive/OriginTag.tsx
  • web/packages/agenta-entity-ui/src/drive/SessionFilesDrawer.tsx
  • web/packages/agenta-entity-ui/src/drive/StorageFilesHeader.tsx
  • web/packages/agenta-entity-ui/src/drive/StorageSection.tsx
  • web/packages/agenta-entity-ui/src/drive/VirtualTileGrid.tsx
  • web/packages/agenta-entity-ui/src/drive/driveFileSource.tsx
  • web/packages/agenta-entity-ui/src/drive/driveIcons.tsx
  • web/packages/agenta-entity-ui/src/drive/driveMarkdown.tsx
  • web/packages/agenta-entity-ui/src/drive/driveSessionContext.tsx
  • web/packages/agenta-entity-ui/src/drive/fileMeta.tsx
  • web/packages/agenta-entity-ui/src/drive/index.ts
  • web/packages/agenta-entity-ui/src/drive/quickLook.tsx
  • web/packages/agenta-entity-ui/src/drive/renderers.tsx
  • web/packages/agenta-entity-ui/src/drive/repoMeta.tsx
  • web/packages/agenta-entity-ui/src/drive/useDriveDownloadAll.ts
  • web/packages/agenta-entity-ui/src/drive/useDriveFileDownload.ts
  • web/packages/agenta-entity-ui/src/drive/useDriveTreeData.ts
  • web/packages/agenta-entity-ui/src/drive/useLazyDriveTree.tsx
  • web/packages/agenta-entity-ui/src/drive/useUploadReveal.ts
  • web/packages/agenta-entity-ui/tests/unit/useUploadReveal.test.ts
🛑 Comments failed to post (2)
web/packages/agenta-entity-ui/src/drive/VirtualTileGrid.tsx (2)

153-159: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

An announced shift can be dropped silently.

Two paths advance shiftSeq without freezing the layout:

  • If width is still 0 (first render, before measurement), line 157 skips the freeze. The announcement is consumed and never applied.
  • shiftSeq initializes from anticipateShift?.seq ?? 0. If the host mounts the grid with anticipateShift === null and then announces {delta, seq: 0}, the comparison at line 155 sees equal values and ignores the shift.

In both cases the grid derives columns from the sweeping live width, which is the behavior the prop exists to prevent. Seed the sequence with a sentinel, and defer the pending shift until a width exists.

🐛 Proposed fix
 const [frozenWidth, setFrozenWidth] = useState<number | null>(null)
-const [shiftSeq, setShiftSeq] = useState(anticipateShift?.seq ?? 0)
-if (anticipateShift && anticipateShift.seq !== shiftSeq) {
+const [shiftSeq, setShiftSeq] = useState<number | null>(null)
+if (anticipateShift && anticipateShift.seq !== shiftSeq && width > 0) {
     setShiftSeq(anticipateShift.seq)
-    if (width > 0) setFrozenWidth((frozenWidth ?? width) + anticipateShift.delta)
+    setFrozenWidth((frozenWidth ?? width) + anticipateShift.delta)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    const [frozenWidth, setFrozenWidth] = useState<number | null>(null)
    const [shiftSeq, setShiftSeq] = useState<number | null>(null)
    if (anticipateShift && anticipateShift.seq !== shiftSeq && width > 0) {
        setShiftSeq(anticipateShift.seq)
        setFrozenWidth((frozenWidth ?? width) + anticipateShift.delta)
    }
    const arrived = frozenWidth !== null && width > 0 && Math.abs(width - frozenWidth) < 2

308-343: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The handler steals arrow keys, Home, and End from nested text fields.

handleKeyDown sits on the scroll container, so it receives bubbled events from every descendant. If a tile renders an input or textarea (inline rename, in-tile filter), pressing ArrowLeft, ArrowRight, Home, or End moves grid focus and e.preventDefault() at line 328 blocks caret movement. The same applies to Cmd/Ctrl+ArrowDown at line 310.

The autoFocus effect already bails on an active text field at line 303. Apply the same guard here.

🐛 Proposed fix
     const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
+        // Never hijack keys from an editable descendant (inline rename, in-tile search).
+        const t = e.target as HTMLElement | null
+        if (t && (/^(input|textarea|select)$/i.test(t.tagName) || t.isContentEditable)) return
         // Finder-style step in/out (Cmd on macOS, Ctrl elsewhere).
         if ((e.metaKey || e.ctrlKey) && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
        // Never hijack keys from an editable descendant.
        const t = e.target as HTMLElement | null
        if (t && (/^(input|textarea|select)$/i.test(t.tagName) || t.isContentEditable)) return
        // Finder-style step in/out (Cmd on macOS, Ctrl elsewhere).
        if ((e.metaKey || e.ctrlKey) && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
            if (e.key === "ArrowDown") {
                const cur = focusedIndex()
                if (cur >= 0 && onMetaActivate) {
                    e.preventDefault()
                    onMetaActivate(items[cur], cur)
                }
            } else if (onMetaBack) {
                e.preventDefault()
                onMetaBack()
            }
            return
        }
        if (!NAV_KEYS.includes(e.key)) {
            onKeyDown?.(e)
            return
        }
        const cur = focusedIndex()
        e.preventDefault()
        switch (e.key) {
            case "Home":
                return focusCell(0)
            case "End":
                return focusCell(items.length - 1)
            case "ArrowLeft":
                return focusCell(cur < 0 ? 0 : cur - 1)
            case "ArrowRight":
                return focusCell(cur < 0 ? 0 : cur + 1)
            case "ArrowUp":
                return focusCell(cur < 0 ? 0 : cur - cols)
            case "ArrowDown":
                return focusCell(cur < 0 ? 0 : cur + cols)
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant