Skip to content

Document Upload Progress Indicator & Upload Limit#109

Open
Ayush8923 wants to merge 9 commits intomainfrom
feat/upload-document-progress-indicator
Open

Document Upload Progress Indicator & Upload Limit#109
Ayush8923 wants to merge 9 commits intomainfrom
feat/upload-document-progress-indicator

Conversation

@Ayush8923
Copy link
Copy Markdown
Collaborator

@Ayush8923 Ayush8923 commented Apr 6, 2026

Issue: #106

Summary:

  • Add a streaming progress proxy in the API route that tracks real bytes consumed by the backend using a pull-based ReadableStream, sending progress events as NDJSON back to the browser.
  • Add uploadWithProgress client utility that reads the streaming response and reports two phases: uploading (0–100%) and processing (pulsing animation while backend processes the file)
  • Support upload cancellation via AbortController - closing the modal aborts the in-flight request
  • Refactor UploadDocumentModal to use the shared Modal component and extract accepted file types into ACCEPTED_DOCUMENT_TYPES constant
  • Added a document upload limit of up to 25 MB.

How it's works:

Browser --> fetch --> API route --> stream --> Backend (FastAPI)
                        ├- buffers file from browser (fast, same host)
                        ├- feeds 64KB chunks to FastAPI via pull-based stream
                        ├- sends `{ phase: "uploading", progress: N }` as each chunk is consumed
                        ├- sends `{ phase: "processing" }` after all bytes forwarded
                        ├- sends `{ done: true, status, data }` when response comes from backend

Summary by CodeRabbit

  • New Features

    • Real-time upload progress (percent + phase) with cancelable uploads and client-side max file size validation
    • Upload dialog now remains mounted and shows live progress/status; supports more document/image types
  • Bug Fixes

    • Document preview enforces full-height layout and improved download/preview behavior
  • Chores

    • UI polish: pointer cursors, updated icon artwork, and standardized modal/footer button styling
  • Removed

    • Removed a speech-to-text model comparison card component

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

Frontend upload flow now streams uploads via a new uploadWithProgress client helper, tracks upload progress/phase and supports aborting in the upload modal; the API POST handler buffers the request and streams newline-delimited JSON progress/phase events back to the client.

Changes

Cohort / File(s) Summary
Document Page
app/(main)/document/page.tsx
Switched upload to uploadWithProgress, added uploadProgress/uploadPhase state, saved abort ref, validated file.size against MAX_DOCUMENT_SIZE_BYTES, reset upload state on start/close, and always mount UploadDocumentModal with open prop.
Upload Modal UI
app/components/document/UploadDocumentModal.tsx
Replaced inline modal behavior with shared Modal (open/onClose), added open, uploadProgress?, uploadPhase? props, use ACCEPTED_DOCUMENT_TYPES and MAX_DOCUMENT_SIZE_MB, and render phase-aware progress UI and updated footer/button states.
API Upload Handler
app/api/document/route.ts
POST now buffers request body, validates non-empty payload, streams upload to backend in 64KB slices, emits newline-delimited JSON progress/phase and final done/error events, and returns a text/event-stream Response via a streaming writer.
API Client / Utilities
app/lib/apiClient.ts
Added UploadPhase type and uploadWithProgress that POSTs and parses NDJSON progress events, invoking a callback and returning { promise, abort }; tightened Content-Type handling for FormData.
Constants
app/lib/constants.ts
Added ACCEPTED_DOCUMENT_TYPES, MAX_DOCUMENT_SIZE_MB, and MAX_DOCUMENT_SIZE_BYTES.
Types
app/lib/types/document.ts
Added exported Document interface (id, fname, object_store_url, optional signed_url, file_size, inserted_at, updated_at).
Document Listing / Preview
app/components/document/DocumentListing.tsx, app/components/document/DocumentPreview.tsx
Switched Document type imports to @/app/lib/types/document, small styling and UI adjustments, refactored preview handling and download link behavior.
Knowledge Base Layout
app/(main)/knowledge-base/page.tsx
Removed commented wrapper and added h-full to preview container classes to enforce full-height behavior.
Modal / Icons / Misc
app/components/Modal.tsx, app/components/icons/document/DocumentFileIcon.tsx, app/components/speech-to-text/ModelComparisonCard.tsx
Minor UI tweaks: cursor on close button, SVG path updates for document icon, and deletion of ModelComparisonCard component (removed).

Sequence Diagram(s)

sequenceDiagram
    participant User as User / Browser
    participant Modal as UploadDocumentModal
    participant Page as Document Page
    participant Client as uploadWithProgress
    participant API as API Handler (POST /api/document)
    participant Backend as Backend Service

    User->>Modal: select file & start upload
    Modal->>Page: submit file -> handleUpload
    Page->>Page: set uploadProgress=0, uploadPhase="uploading"
    Page->>Client: uploadWithProgress(url, apiKey, formData, onProgress)
    Client->>API: POST (duplex: "half") streaming body
    API->>Backend: forward buffered 64KB slices

    loop streaming upload progress
        API->>Client: { "phase":"uploading","progress": X } (NDJSON)
        Client->>Page: onProgress(X,"uploading")
        Page->>Modal: update progress bar (X%)
    end

    Backend->>API: processing complete
    API->>Client: { "phase":"processing" } then { "done":true, "status":..., "data":... }
    Client->>Page: onProgress(100,"processing") then resolve
    Page->>Modal: set uploadPhase="done" / show completion
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • Documents: UI for upload #20: Previously modified app/api/document/route.ts upload handling; closely related to the POST streaming rewrite here.

Suggested reviewers

  • Prajna1999
  • vprashrex
  • nishika26

Poem

🐰 I hopped a file into a modal bright,
Chunks streamed out beneath the moonlight,
Bars that climb and phases hum,
An abort if things go numb,
Then a beep — the upload’s done!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: adding a document upload progress indicator and implementing an upload size limit (25 MB).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/upload-document-progress-indicator

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 and usage tips.

@Ayush8923 Ayush8923 self-assigned this Apr 6, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
app/(main)/document/page.tsx (1)

110-114: ⚠️ Potential issue | 🟡 Minor

Skip the failure toast on intentional cancellation.

Closing the modal aborts the request at Lines 225-226, so this catch block currently reports every user-cancelled upload as an error. Return early for AbortError and let finally handle the cleanup.

Suggested change
     } catch (error) {
+      if (error instanceof Error && error.name === "AbortError") {
+        return;
+      }
       console.error("Upload error:", error);
       toast.error(
         `Failed to upload document: ${error instanceof Error ? error.message : "Unknown error"}`,
       );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(main)/document/page.tsx around lines 110 - 114, The catch block that
currently logs and toasts all errors should skip intentional cancellations:
detect an AbortError (e.g., check error.name === "AbortError" or error
instanceof DOMException with name "AbortError") and return early before calling
console.error or toast.error so the finally cleanup still runs; leave the
existing toast.error and console.error for non-abort errors (reference the catch
block handling error, the toast.error call, and the finally cleanup).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/document/route.ts`:
- Around line 82-84: The current code emits the { phase: "processing" } event
after awaiting apiClient(...), so clients learn "processing" only after the
backend finished; move the writer.write(encoder.encode(JSON.stringify({ phase:
"processing" }) + "\n")) call so it is executed immediately after the last chunk
is pulled from uploadBody (i.e., right after the loop that reads uploadBody
completes) and before calling await apiClient(...), and keep the final { done:
true, ... } event to be written only when the apiClient response is received;
update references to writer.write, encoder.encode, uploadBody, and apiClient
accordingly.
- Around line 32-33: The code currently calls await request.arrayBuffer()
(creating fileBuffer and totalSize) which buffers the entire upload; change the
proxying logic in app/api/document/route.ts to stream request.body directly to
the upstream (or to the file sink) and track bytes forwarded by reading chunks
from the ReadableStream, incrementing a counter instead of using
fileBuffer/totalSize, so memory usage stays constant and backpressure is
preserved; replace usages of fileBuffer and totalSize with the streamed byte
counter and forward each chunk to the destination write stream or fetch body.
- Around line 74-80: The upstream POST via apiClient is not receiving the
browser abort signal, so forward the incoming request's AbortSignal to the
upstream call by passing signal: request.signal (or request.body?.signal if
wrapped) into the apiClient fetch/init options; update the call that constructs
the POST to include the signal alongside method, body, headers, and duplex so
the upstream request is aborted when the browser aborts (refer to apiClient and
the request/uploadBody variables to locate the call).

In `@app/lib/apiClient.ts`:
- Around line 56-63: Before starting the NDJSON line-reader, check the HTTP
response status and content-type on the Fetch response (res) and handle
non-stream error payloads by reading and parsing them as JSON/text; if res.ok is
false or content-type is not an NDJSON/stream type, await res.text() or
res.json() and throw a new Error that includes the server message so the real
error is surfaced instead of falling through to the NDJSON reader. Update the
code around the fetch response check (the res variable and the NDJSON parsing
block referenced in lines ~70-107) to gate entering the line-reader only when
the status and content-type indicate a streaming NDJSON response. Ensure the
controller.signal handling remains the same and surface the parsed server error
in the thrown exception.

---

Outside diff comments:
In `@app/`(main)/document/page.tsx:
- Around line 110-114: The catch block that currently logs and toasts all errors
should skip intentional cancellations: detect an AbortError (e.g., check
error.name === "AbortError" or error instanceof DOMException with name
"AbortError") and return early before calling console.error or toast.error so
the finally cleanup still runs; leave the existing toast.error and console.error
for non-abort errors (reference the catch block handling error, the toast.error
call, and the finally cleanup).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ed9e38f-8efe-4a6f-93ab-d83800aad5e0

📥 Commits

Reviewing files that changed from the base of the PR and between 5ba8a7c and 32e054d.

📒 Files selected for processing (6)
  • app/(main)/document/page.tsx
  • app/(main)/knowledge-base/page.tsx
  • app/api/document/route.ts
  • app/components/document/UploadDocumentModal.tsx
  • app/lib/apiClient.ts
  • app/lib/constants.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
app/api/document/route.ts (2)

32-33: ⚠️ Potential issue | 🟠 Major

Stream the inbound request instead of buffering it.

await request.arrayBuffer() makes memory usage proportional to the full multipart payload and removes most of the backpressure benefit of this proxy. It also prevents the route from sending any progress until the browser → Next.js hop has already finished. Read from request.body directly and update the forwarded-byte counter as chunks are enqueued upstream.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/document/route.ts` around lines 32 - 33, Replace the full-buffering
logic that uses await request.arrayBuffer() and the fileBuffer/totalSize
variables with streaming reads from request.body (the incoming ReadableStream);
read chunks from request.body, increment the forwarded-byte counter as each
chunk is enqueued to the upstream request, and forward chunks directly to the
outbound fetch/stream instead of assembling a single Uint8Array. Update any
logic that used totalSize to account for streamed progress updates, and remove
the await request.arrayBuffer() and fileBuffer creation to avoid buffering the
whole payload.

83-85: ⚠️ Potential issue | 🟠 Major

Emit the processing phase before awaiting the backend response.

app/lib/apiClient.ts:71-101 only switches the UI to "processing" when this event arrives. Because it is written after await apiClient(...), the client effectively jumps from uploading to done. Emit "processing" when uploadBody closes, then keep { done: true, status, data } as the terminal event.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/document/route.ts` around lines 83 - 85, The code currently writes
the {"phase":"processing"} event after awaiting the backend response so the
client transitions from "uploading" to "done"; move the writer.write call that
emits the processing phase to immediately after uploadBody closes (i.e., before
calling apiClient) so the client receives the processing event before the
backend roundtrip; keep the final terminal event shape ({ done: true, status,
data }) as the last write after apiClient returns; look for the writer.write
call in app/api/document/route.ts and the apiClient invocation in
app/lib/apiClient.ts to implement this ordering change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/api/document/route.ts`:
- Around line 32-33: Replace the full-buffering logic that uses await
request.arrayBuffer() and the fileBuffer/totalSize variables with streaming
reads from request.body (the incoming ReadableStream); read chunks from
request.body, increment the forwarded-byte counter as each chunk is enqueued to
the upstream request, and forward chunks directly to the outbound fetch/stream
instead of assembling a single Uint8Array. Update any logic that used totalSize
to account for streamed progress updates, and remove the await
request.arrayBuffer() and fileBuffer creation to avoid buffering the whole
payload.
- Around line 83-85: The code currently writes the {"phase":"processing"} event
after awaiting the backend response so the client transitions from "uploading"
to "done"; move the writer.write call that emits the processing phase to
immediately after uploadBody closes (i.e., before calling apiClient) so the
client receives the processing event before the backend roundtrip; keep the
final terminal event shape ({ done: true, status, data }) as the last write
after apiClient returns; look for the writer.write call in
app/api/document/route.ts and the apiClient invocation in app/lib/apiClient.ts
to implement this ordering change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6668df59-c02a-4c69-ba74-3ad0cbe2ff56

📥 Commits

Reviewing files that changed from the base of the PR and between 32e054d and e9ac327.

📒 Files selected for processing (1)
  • app/api/document/route.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/document/UploadDocumentModal.tsx`:
- Around line 106-127: The UI doesn't handle the "done" UploadPhase so users
briefly see "Uploading... 100%" when onProgress(100, "done") is emitted; update
the conditional rendering in UploadDocumentModal (look for isUploading,
uploadPhase, uploadProgress) to explicitly handle uploadPhase === "done"
(separate from "processing" and "uploading") by showing a completion message
like "Done" or "Upload complete", rendering the progress bar at 100% without the
uploading transition/animation (no animate-pulse or transition) and optionally
keep the percentage as 100% or hide it; ensure this branch is used when
uploadPhase === "done" so the UI reflects completion until the parent clears
isUploading.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 218e1fa3-3153-4f2f-b0ca-3e432d941503

📥 Commits

Reviewing files that changed from the base of the PR and between e9ac327 and 8e59f25.

📒 Files selected for processing (6)
  • app/(main)/document/page.tsx
  • app/components/document/DocumentListing.tsx
  • app/components/document/DocumentPreview.tsx
  • app/components/document/UploadDocumentModal.tsx
  • app/lib/constants.ts
  • app/lib/types/document.ts
✅ Files skipped from review due to trivial changes (2)
  • app/lib/types/document.ts
  • app/lib/constants.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/(main)/document/page.tsx

@Ayush8923 Ayush8923 changed the title Document Upload Progress Indicator Document Upload Progress Indicator & Upload Limit Apr 7, 2026
@Ayush8923 Ayush8923 requested review from nishika26 and vprashrex April 7, 2026 09:29
@Ayush8923 Ayush8923 requested a review from Prajna1999 April 7, 2026 09:29
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
app/components/document/DocumentListing.tsx (1)

125-133: Add an explicit accessible name to the icon-only delete button.

title is not a reliable accessible name across assistive tech. Add aria-label for consistent screen-reader support.

♿ Suggested tweak
                   <button
                     onClick={(e) => {
                       e.stopPropagation();
                       onDelete(doc.id);
                     }}
                     className="p-1.5 rounded-md transition-colors shrink-0 border border-[hsl(8,86%,80%)] bg-[hsl(0,0%,100%)] text-[hsl(8,86%,40%)] hover:bg-[hsl(8,86%,95%)] cursor-pointer"
+                    aria-label={`Delete ${doc.fname}`}
                     title="Delete Document"
                   >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/document/DocumentListing.tsx` around lines 125 - 133, The
delete button in DocumentListing (the button wrapping <TrashIcon /> and invoking
onDelete(doc.id)) lacks an explicit accessible name; add an aria-label attribute
to that button (e.g., aria-label="Delete document" or aria-label={`Delete
${doc.name}`} with a sensible fallback) so screen readers get a reliable name
instead of relying on title; keep the existing onClick/onDelete and visual title
if desired but ensure aria-label is present on the button element.
app/components/document/DocumentPreview.tsx (2)

108-128: Consider collapsing duplicated download link branches.

Both branches render the same anchor with only download behavior differing; this is a good candidate for a small cleanup.

♻️ Simplify duplicated markup
-          {(document.signed_url || document.object_store_url) &&
-            (isPreviewable(document.fname) ? (
-              <a
-                href={document.signed_url || document.object_store_url}
-                target="_blank"
-                rel="noopener noreferrer"
-                className="shrink-0 px-4 py-1.5 rounded-md text-sm font-medium transition-colors bg-[`#171717`] text-white hover:bg-accent-hover"
-              >
-                Download
-              </a>
-            ) : (
-              <a
-                href={document.signed_url || document.object_store_url}
-                download={document.fname}
-                target="_blank"
-                rel="noopener noreferrer"
-                className="shrink-0 px-4 py-1.5 rounded-md text-sm font-medium transition-colors bg-[`#171717`] text-white hover:bg-accent-hover"
-              >
-                Download
-              </a>
-            ))}
+          {(document.signed_url || document.object_store_url) && (
+            <a
+              href={document.signed_url || document.object_store_url}
+              download={isPreviewable(document.fname) ? undefined : document.fname}
+              target="_blank"
+              rel="noopener noreferrer"
+              className="shrink-0 px-4 py-1.5 rounded-md text-sm font-medium transition-colors bg-[`#171717`] text-white hover:bg-accent-hover"
+            >
+              Download
+            </a>
+          )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/document/DocumentPreview.tsx` around lines 108 - 128, The JSX
in DocumentPreview duplicates the same <a> element in both branches; consolidate
by computing href = document.signed_url || document.object_store_url and a
conditional downloadProp = isPreviewable(document.fname) ? undefined :
document.fname, then render one anchor with href, target, rel, className and
download={downloadProp}; update references to isPreviewable, document.fname,
document.signed_url/document.object_store_url and keep the existing attributes
and text.

152-157: Replace raw <img> with next/image for better optimization.

This aligns with the @next/next/no-img-element lint rule from your Next.js ESLint config. However, implementing this requires configuring the signed URL domain in next.config.ts via remotePatterns first, since Next.js Image requires explicit remote domain allowlisting. Consider the trade-off: signed URLs are typically ephemeral, so Next.image's image optimization benefits may be limited. If you proceed, ensure proper sizing constraints are set to avoid layout shifts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/document/DocumentPreview.tsx` around lines 152 - 157, Replace
the raw <img> in DocumentPreview with Next.js' Image component: import Image
from 'next/image', use <Image ...> in the DocumentPreview render (keep using
document.signed_url and document.fname), provide explicit sizing (width/height)
or use layout="fill" with a positioned container to prevent layout shifts, and
wire Image's onError to call setImageLoadError(true) (or use onLoadingComplete
fallback) so existing error handling remains. Also add the signed URL host to
next.config.ts via images.remotePatterns (allowlist the domain/path pattern used
by document.signed_url) so Next can load remote images, and adjust CSS/className
for Image wrapper since Image doesn't accept all <img> props.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/document/DocumentPreview.tsx`:
- Around line 45-47: The formatFileSize function treats zero as missing because
it uses a falsy check; change the existence check from if (!size) to a
null/undefined-only check (e.g., if (size == null) or typeof size ===
'undefined') so that 0 is handled and returns "0 B", keeping the remaining logic
(size < 1024, etc.) unchanged.
- Around line 169-171: The message in DocumentPreview.tsx uses the ext variable
inside the <p> element and renders "Preview is not available for .{ext} files",
which produces incorrect output when ext is empty; update the JSX in the
DocumentPreview component to check ext and render a fallback when it's falsy
(for example "Preview is not available for this file" or "Preview is not
available for files without an extension") and keep the original ".{ext} files"
text only when ext is non-empty.

---

Nitpick comments:
In `@app/components/document/DocumentListing.tsx`:
- Around line 125-133: The delete button in DocumentListing (the button wrapping
<TrashIcon /> and invoking onDelete(doc.id)) lacks an explicit accessible name;
add an aria-label attribute to that button (e.g., aria-label="Delete document"
or aria-label={`Delete ${doc.name}`} with a sensible fallback) so screen readers
get a reliable name instead of relying on title; keep the existing
onClick/onDelete and visual title if desired but ensure aria-label is present on
the button element.

In `@app/components/document/DocumentPreview.tsx`:
- Around line 108-128: The JSX in DocumentPreview duplicates the same <a>
element in both branches; consolidate by computing href = document.signed_url ||
document.object_store_url and a conditional downloadProp =
isPreviewable(document.fname) ? undefined : document.fname, then render one
anchor with href, target, rel, className and download={downloadProp}; update
references to isPreviewable, document.fname,
document.signed_url/document.object_store_url and keep the existing attributes
and text.
- Around line 152-157: Replace the raw <img> in DocumentPreview with Next.js'
Image component: import Image from 'next/image', use <Image ...> in the
DocumentPreview render (keep using document.signed_url and document.fname),
provide explicit sizing (width/height) or use layout="fill" with a positioned
container to prevent layout shifts, and wire Image's onError to call
setImageLoadError(true) (or use onLoadingComplete fallback) so existing error
handling remains. Also add the signed URL host to next.config.ts via
images.remotePatterns (allowlist the domain/path pattern used by
document.signed_url) so Next can load remote images, and adjust CSS/className
for Image wrapper since Image doesn't accept all <img> props.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59bb784e-78ce-4eb5-9d4e-ca617d3ae0f4

📥 Commits

Reviewing files that changed from the base of the PR and between 8e59f25 and 9f12829.

📒 Files selected for processing (6)
  • app/components/Modal.tsx
  • app/components/document/DocumentListing.tsx
  • app/components/document/DocumentPreview.tsx
  • app/components/document/UploadDocumentModal.tsx
  • app/components/icons/document/DocumentFileIcon.tsx
  • app/components/speech-to-text/ModelComparisonCard.tsx
💤 Files with no reviewable changes (1)
  • app/components/speech-to-text/ModelComparisonCard.tsx
✅ Files skipped from review due to trivial changes (2)
  • app/components/Modal.tsx
  • app/components/icons/document/DocumentFileIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/document/UploadDocumentModal.tsx

Comment on lines +45 to +47
const formatFileSize = (size?: number) => {
if (!size) return "N/A";
if (size < 1024) return `${size} B`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

formatFileSize mislabels zero-byte files.

if (!size) treats 0 as missing and returns "N/A" instead of "0 B".

✅ Minimal fix
   const formatFileSize = (size?: number) => {
-    if (!size) return "N/A";
+    if (size == null) return "N/A";
     if (size < 1024) return `${size} B`;
📝 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.

Suggested change
const formatFileSize = (size?: number) => {
if (!size) return "N/A";
if (size < 1024) return `${size} B`;
const formatFileSize = (size?: number) => {
if (size == null) return "N/A";
if (size < 1024) return `${size} B`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/document/DocumentPreview.tsx` around lines 45 - 47, The
formatFileSize function treats zero as missing because it uses a falsy check;
change the existence check from if (!size) to a null/undefined-only check (e.g.,
if (size == null) or typeof size === 'undefined') so that 0 is handled and
returns "0 B", keeping the remaining logic (size < 1024, etc.) unchanged.

Comment on lines +169 to +171
<p className="text-sm font-medium text-text-primary mb-1">
Preview is not available for .{ext} files
</p>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle extension-less filenames in unsupported preview text.

For files without an extension, this currently renders ". files".

💬 Suggested fallback text
-                  <p className="text-sm font-medium text-text-primary mb-1">
-                    Preview is not available for .{ext} files
-                  </p>
+                  <p className="text-sm font-medium text-text-primary mb-1">
+                    {ext
+                      ? `Preview is not available for .${ext} files`
+                      : "Preview is not available for this file type"}
+                  </p>
📝 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.

Suggested change
<p className="text-sm font-medium text-text-primary mb-1">
Preview is not available for .{ext} files
</p>
<p className="text-sm font-medium text-text-primary mb-1">
{ext
? `Preview is not available for .${ext} files`
: "Preview is not available for this file type"}
</p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/document/DocumentPreview.tsx` around lines 169 - 171, The
message in DocumentPreview.tsx uses the ext variable inside the <p> element and
renders "Preview is not available for .{ext} files", which produces incorrect
output when ext is empty; update the JSX in the DocumentPreview component to
check ext and render a fallback when it's falsy (for example "Preview is not
available for this file" or "Preview is not available for files without an
extension") and keep the original ".{ext} files" text only when ext is
non-empty.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant