Upload files#1
Conversation
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughIntroduces an upload-driven visualization flow: a new Upload component handles image selection, progress, and base64 output; Home routes completed uploads to a new dynamic Changes
Sequence DiagramsequenceDiagram
actor User
participant Home as Home Route
participant Upload as Upload Component
participant Visualizer as Visualizer Route
User->>Home: Open home page
Home->>Upload: Render with onComplete callback
User->>Upload: Selects or drops image
activate Upload
Upload->>Upload: Read file -> base64
Upload->>Upload: Increment progress until 100%
Upload->>Home: Call onComplete(base64)
deactivate Upload
Home->>Home: Create timestamp ID
Home->>Visualizer: Navigate to /visualizer/:id
User->>Visualizer: View visualizer page
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (7)
lib/constants.ts (3)
12-15:PROGRESS_INCREMENTvsPROGRESS_STEP— pick one.Both look like the per-tick progress bump but with different values (15 vs 5).
Upload.tsxonly usesPROGRESS_STEP. Either rename one to clarify intent or remove the unused constant before more code starts importing the wrong one.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/constants.ts` around lines 12 - 15, There are two similar constants PROGRESS_INCREMENT and PROGRESS_STEP—only PROGRESS_STEP is used by Upload.tsx—so remove the unused PROGRESS_INCREMENT (or, if both are intended, rename one to clarify distinct intent) and update any references; specifically, delete PROGRESS_INCREMENT from lib/constants.ts (or rename it to a descriptive name if used elsewhere) and ensure Upload.tsx continues to import and use PROGRESS_STEP consistently.
4-8: Derive sub-paths fromROOTto avoid drift.
SOURCESandRENDERSrepeat the literal"roomify". IfROOTever changes, the others must be updated in lock-step.♻️ Proposed refactor
-export const STORAGE_PATHS = { - ROOT: "roomify", - SOURCES: "roomify/sources", - RENDERS: "roomify/renders", -} as const; +const STORAGE_ROOT = "roomify"; +export const STORAGE_PATHS = { + ROOT: STORAGE_ROOT, + SOURCES: `${STORAGE_ROOT}/sources`, + RENDERS: `${STORAGE_ROOT}/renders`, +} as const;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/constants.ts` around lines 4 - 8, STORAGE_PATHS repeats the literal "roomify" causing drift; change SOURCES and RENDERS to derive from STORAGE_PATHS.ROOT (i.e., set SOURCES to `${STORAGE_PATHS.ROOT}/sources` and RENDERS to `${STORAGE_PATHS.ROOT}/renders`) so they automatically reflect any ROOT change while keeping the exported STORAGE_PATHS as const; update any consuming code if types change accordingly.
1-1: Silent empty-string fallback forPUTER_WORKER_URLwill produce confusing failures.If
VITE_PUTER_WORKER_URLis missing, every consumer will end up with an empty base URL, causing requests to resolve as relative paths against the app origin instead of failing fast. Consider throwing in development (or at least aconsole.warn) so misconfigured environments are obvious.const url = import.meta.env.VITE_PUTER_WORKER_URL; if (!url && import.meta.env.DEV) { console.warn("VITE_PUTER_WORKER_URL is not set; Puter API calls will fail."); } export const PUTER_WORKER_URL = url ?? "";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/constants.ts` at line 1, The current constant PUTER_WORKER_URL falls back silently to an empty string causing confusing runtime failures; update the module so it reads import.meta.env.VITE_PUTER_WORKER_URL into a local variable, and if it's falsy and import.meta.env.DEV is true emit a clear console.warn (or throw in dev) indicating VITE_PUTER_WORKER_URL is not set, then export PUTER_WORKER_URL from that validated variable instead of defaulting silently to ""; reference PUTER_WORKER_URL and import.meta.env.VITE_PUTER_WORKER_URL/import.meta.env.DEV to locate where to change behavior.components/Upload.tsx (2)
3-3: UnuseduseNavigateimport.
useNavigateis imported but never used — navigation is delegated to theonCompletecallback in the parent. Drop the import.-import { useOutletContext, useNavigate } from "react-router"; +import { useOutletContext } from "react-router";…and the corresponding
const navigate = useNavigate();at line 15.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Upload.tsx` at line 3, Remove the unused navigation hook: delete the `useNavigate` named import from the React Router import statement and remove the `const navigate = useNavigate();` invocation in this file; keep `useOutletContext` intact since it's used, and ensure no other references to `navigate` remain (update any usages to use the parent `onComplete` callback if present).
4-4:PROGRESS_STEPandPROGRESS_INCREMENTlook like duplicates.
lib/constants.tsexports bothPROGRESS_INCREMENT = 15andPROGRESS_STEP = 5. OnlyPROGRESS_STEPis used here. If they represent different concepts, name them accordingly (e.g.,UPLOAD_PROGRESS_STEPvs. something else); otherwise consolidate to one constant.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Upload.tsx` at line 4, PROGRESS_STEP and PROGRESS_INCREMENT appear duplicate; decide whether they represent distinct semantics or a single concept, then update the constants and usages accordingly: if they are the same, remove one (e.g., delete PROGRESS_INCREMENT) and export only PROGRESS_STEP, then update imports (e.g., in Upload.tsx) to import the single constant; if they are different, give them clear distinct names (e.g., UPLOAD_PROGRESS_STEP vs UPLOAD_PROGRESS_INCREMENT), update their definitions and all references (including the import in Upload.tsx) to use the new names, and ensure naming reflects their meanings.app/routes/visualizer.$id.tsx (1)
1-1: UnusedReactimport with the modern JSX transform.With the new JSX transform (default in React 19), the
Reactdefault import isn't needed unless you referenceReact.*APIs. Safe to drop to keep the file minimal.♻️ Proposed cleanup
-import React from "react"; - -const visualizer = () => { +const Visualizer = () => { return <div>visualizer</div>; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/routes/visualizer`.$id.tsx at line 1, Remove the unused default React import at the top of visualizer.$id.tsx (the line "import React from 'react'") since the modern JSX transform doesn't require it; if any React APIs are used via React.* replace with direct imports (e.g., useState, useEffect) or reference them explicitly, otherwise delete that import line to clean up the file.app/routes.ts (1)
3-6: Nit: inconsistent relative paths between the two route entries.
index("routes/home.tsx")androute("visualizer/:id", "./routes/visualizer.$id.tsx")both work, but pick one style for readability.♻️ Suggested tweak
export default [ - index("routes/home.tsx"), - route("visualizer/:id", "./routes/visualizer.$id.tsx"), + index("routes/home.tsx"), + route("visualizer/:id", "routes/visualizer.$id.tsx"), ] satisfies RouteConfig;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/routes.ts` around lines 3 - 6, The two route entries use inconsistent path styles: index("routes/home.tsx") vs route("visualizer/:id", "./routes/visualizer.$id.tsx"); pick one convention and make both consistent (either remove the leading "./" from the visualizer path or add "./" to the home path) so that the array of routes exported by default (the index and route entries) use the same relative-path style for readability and consistency.
🤖 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/routes/home.tsx`:
- Around line 18-24: handleUploadComplete currently drops the received
base64Image and navigates to `/visualizer/${newId}` with no way for the
visualizer to access the image; persist or forward the data before navigating
and remove unused async/return. Fix options: (A) store base64Image keyed by
newId (e.g., in-memory map, sessionStorage/IndexedDB, or upload to the worker
referenced in lib/constants.ts) then navigate to `/visualizer/${newId}` so the
visualizer can retrieve it by id; or (B) pass the image via navigation state
using navigate(`/visualizer/${newId}`, { state: { image: base64Image } }) if
same-tab consumption is acceptable. Also remove the unnecessary async keyword
and the `return true` since onComplete expects a void callback.
In `@app/routes/visualizer`.$id.tsx:
- Around line 3-7: The React component is named with a lowercase identifier
(visualizer) which can be treated as a DOM tag; rename the component to
PascalCase (e.g., Visualizer) and update the default export to export the
PascalCase identifier so the component name follows React conventions and avoids
rendering/devtools issues—update the function name visualizer to Visualizer and
change the export default visualizer to export default Visualizer (also search
for any local references and update them).
In `@components/Upload.tsx`:
- Around line 22-39: The FileReader lacks error handling so a failed read leaves
progress stuck; add a reader.onerror handler alongside reader.onload that clears
any running interval, sets an error state (or calls an existing error callback),
resets/updates setProgress so the UI is not stuck, and surfaces a retry path to
the user; reference the existing reader, reader.onload, setProgress, onComplete,
REDIRECT_DELAY_MS, PROGRESS_STEP, PROGRESS_INTERVAL_MS and file when
implementing the error branch so the read failure cleans up timers and provides
a clear error/retry flow.
- Around line 18-40: The setInterval started inside processFile is never cleaned
up on unmount; change the local const interval to be tracked in a ref (e.g.,
intervalRef) and assign the interval ID to intervalRef.current, clear
intervalRef.current when progress reaches 100 (where you currently call
clearInterval) and also add a useEffect cleanup that clears intervalRef.current
on unmount; ensure you also clear any previous interval before starting a new
one and keep existing behavior of calling onComplete with base64Data after
REDIRECT_DELAY_MS.
- Around line 51-68: Update file-type and size validation to use centralized
constants from lib/constants.ts (e.g., ALLOWED and MAX_BYTES) and enforce them
before any progress UI starts: in handleDrop and handleChange (or better, inside
processFile) check that file.type is in ALLOWED and file.size <= MAX_BYTES, and
if not, surface a user-visible error message and return without invoking the
progress/processing flow; ensure the MIME whitelist (ALLOWED) matches the UI
copy (e.g., "image/jpeg","image/png") and remove duplicate/contradictory checks
so both drag-and-drop (handleDrop) and input selection
(handleChange/processFile) behave identically.
- Around line 26-37: The updater passed to setProgress contains side effects
(clearInterval and setTimeout/onComplete) which can double-fire under Strict
Mode; remove those from the setProgress updater and instead detect completion in
a useEffect that watches the progress state (e.g., when progress === 100) to run
clearInterval and schedule onComplete after REDIRECT_DELAY_MS; ensure the
interval handle (created with PROGRESS_INTERVAL_MS/PROGRESS_STEP) is stored in a
ref or state so the effect can clear it and also clean up on unmount to avoid
leaks.
---
Nitpick comments:
In `@app/routes.ts`:
- Around line 3-6: The two route entries use inconsistent path styles:
index("routes/home.tsx") vs route("visualizer/:id",
"./routes/visualizer.$id.tsx"); pick one convention and make both consistent
(either remove the leading "./" from the visualizer path or add "./" to the home
path) so that the array of routes exported by default (the index and route
entries) use the same relative-path style for readability and consistency.
In `@app/routes/visualizer`.$id.tsx:
- Line 1: Remove the unused default React import at the top of
visualizer.$id.tsx (the line "import React from 'react'") since the modern JSX
transform doesn't require it; if any React APIs are used via React.* replace
with direct imports (e.g., useState, useEffect) or reference them explicitly,
otherwise delete that import line to clean up the file.
In `@components/Upload.tsx`:
- Line 3: Remove the unused navigation hook: delete the `useNavigate` named
import from the React Router import statement and remove the `const navigate =
useNavigate();` invocation in this file; keep `useOutletContext` intact since
it's used, and ensure no other references to `navigate` remain (update any
usages to use the parent `onComplete` callback if present).
- Line 4: PROGRESS_STEP and PROGRESS_INCREMENT appear duplicate; decide whether
they represent distinct semantics or a single concept, then update the constants
and usages accordingly: if they are the same, remove one (e.g., delete
PROGRESS_INCREMENT) and export only PROGRESS_STEP, then update imports (e.g., in
Upload.tsx) to import the single constant; if they are different, give them
clear distinct names (e.g., UPLOAD_PROGRESS_STEP vs UPLOAD_PROGRESS_INCREMENT),
update their definitions and all references (including the import in Upload.tsx)
to use the new names, and ensure naming reflects their meanings.
In `@lib/constants.ts`:
- Around line 12-15: There are two similar constants PROGRESS_INCREMENT and
PROGRESS_STEP—only PROGRESS_STEP is used by Upload.tsx—so remove the unused
PROGRESS_INCREMENT (or, if both are intended, rename one to clarify distinct
intent) and update any references; specifically, delete PROGRESS_INCREMENT from
lib/constants.ts (or rename it to a descriptive name if used elsewhere) and
ensure Upload.tsx continues to import and use PROGRESS_STEP consistently.
- Around line 4-8: STORAGE_PATHS repeats the literal "roomify" causing drift;
change SOURCES and RENDERS to derive from STORAGE_PATHS.ROOT (i.e., set SOURCES
to `${STORAGE_PATHS.ROOT}/sources` and RENDERS to
`${STORAGE_PATHS.ROOT}/renders`) so they automatically reflect any ROOT change
while keeping the exported STORAGE_PATHS as const; update any consuming code if
types change accordingly.
- Line 1: The current constant PUTER_WORKER_URL falls back silently to an empty
string causing confusing runtime failures; update the module so it reads
import.meta.env.VITE_PUTER_WORKER_URL into a local variable, and if it's falsy
and import.meta.env.DEV is true emit a clear console.warn (or throw in dev)
indicating VITE_PUTER_WORKER_URL is not set, then export PUTER_WORKER_URL from
that validated variable instead of defaulting silently to ""; reference
PUTER_WORKER_URL and import.meta.env.VITE_PUTER_WORKER_URL/import.meta.env.DEV
to locate where to change behavior.
🪄 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: 95cb3ddc-6a95-45e7-814d-d40732fff5d6
📒 Files selected for processing (5)
app/routes.tsapp/routes/home.tsxapp/routes/visualizer.$id.tsxcomponents/Upload.tsxlib/constants.ts
| const handleUploadComplete = async (base64Image: string) => { | ||
| const newId = Date.now().toString(); | ||
|
|
||
| navigate(`/visualizer/${newId}`); | ||
|
|
||
| return true; | ||
| }; |
There was a problem hiding this comment.
Uploaded image is dropped on the floor before navigation.
handleUploadComplete receives base64Image but never persists or forwards it. After navigate(/visualizer/${newId}), the new route has no way to obtain the uploaded data — :id is just Date.now() with no associated storage. Unless this is intentionally a stub for a follow-up PR, the visualizer route will render with nothing to visualize.
Consider one of:
- Persisting the base64 (e.g. into an in-memory store keyed by
newId, sessionStorage, IndexedDB, or uploading to the worker referenced inlib/constants.ts) before navigating. - Passing it via
navigate(..., { state: { image: base64Image } })if same-tab consumption is acceptable.
Also, the function is marked async and return trues, but neither awaits anything nor has its return value consumed by Upload (whose onComplete is typed (base64Data: string) => void). The async and return true can be removed.
♻️ Minimal cleanup (still missing data plumbing)
- const handleUploadComplete = async (base64Image: string) => {
- const newId = Date.now().toString();
-
- navigate(`/visualizer/${newId}`);
-
- return true;
- };
+ const handleUploadComplete = (base64Image: string) => {
+ const newId = Date.now().toString();
+ // TODO: persist `base64Image` so /visualizer/:id can retrieve it.
+ navigate(`/visualizer/${newId}`, { state: { image: base64Image } });
+ };📝 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 handleUploadComplete = async (base64Image: string) => { | |
| const newId = Date.now().toString(); | |
| navigate(`/visualizer/${newId}`); | |
| return true; | |
| }; | |
| const handleUploadComplete = (base64Image: string) => { | |
| const newId = Date.now().toString(); | |
| // TODO: persist `base64Image` so /visualizer/:id can retrieve it. | |
| navigate(`/visualizer/${newId}`, { state: { image: base64Image } }); | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/routes/home.tsx` around lines 18 - 24, handleUploadComplete currently
drops the received base64Image and navigates to `/visualizer/${newId}` with no
way for the visualizer to access the image; persist or forward the data before
navigating and remove unused async/return. Fix options: (A) store base64Image
keyed by newId (e.g., in-memory map, sessionStorage/IndexedDB, or upload to the
worker referenced in lib/constants.ts) then navigate to `/visualizer/${newId}`
so the visualizer can retrieve it by id; or (B) pass the image via navigation
state using navigate(`/visualizer/${newId}`, { state: { image: base64Image } })
if same-tab consumption is acceptable. Also remove the unnecessary async keyword
and the `return true` since onComplete expects a void callback.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
components/Upload.tsx (2)
38-40:⚠️ Potential issue | 🟡 MinorClear any existing upload interval before assigning a new one.
intervalRef.currentis replaced directly on Line 38. IfprocessFileis invoked again before completion, the previous timer can continue running.Proposed fix
- intervalRef.current = setInterval(() => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + intervalRef.current = setInterval(() => { setProgress((prev) => Math.min(prev + PROGRESS_STEP, 100)); }, PROGRESS_INTERVAL_MS);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Upload.tsx` around lines 38 - 40, Before assigning a new interval in processFile, clear any existing timer stored in intervalRef.current so the old interval doesn't keep running; call clearInterval(intervalRef.current) (after checking it's set) immediately before setting intervalRef.current = setInterval(...), ensuring you reference intervalRef.current, setInterval, PROGRESS_STEP and PROGRESS_INTERVAL_MS in the same block so repeated invocations safely replace the previous timer.
25-27:⚠️ Potential issue | 🟠 MajorEnforce file type/size in one place before starting upload flow.
Line 60 applies a broad drag-drop check (
image/*), while Line 68 allows any selected file path through, and there is still no max-size guard despite the UI claim on Line 122. This can cause inconsistent behavior and large-memory reads.Proposed fix
import { PROGRESS_INTERVAL_MS, PROGRESS_STEP, REDIRECT_DELAY_MS, + // add these in lib/constants.ts + // ALLOWED_UPLOAD_MIME_TYPES, + // MAX_UPLOAD_BYTES, } from "../lib/constants"; +// Temporary local fallback until centralized constants are added +const ALLOWED_UPLOAD_MIME_TYPES = ["image/jpeg", "image/png"]; +const MAX_UPLOAD_BYTES = 50 * 1024 * 1024; + const processFile = (file: File) => { if (!isSignedIn) return; + + if (!ALLOWED_UPLOAD_MIME_TYPES.includes(file.type)) { + return; + } + if (file.size > MAX_UPLOAD_BYTES) { + return; + } + setFile(file);Also applies to: 54-63, 65-71, 103-107, 122-122
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Upload.tsx` around lines 25 - 27, Centralize and enforce file type and size validation before any upload flow by adding a single validator (e.g. validateAndSetFile) and using it from processFile, the drag-drop handler (handleDrop/onDrop) and the file input handler (handleFileSelect) instead of directly calling setFile; the validator should check mime type against the existing allowed pattern (image/* or a stricter whitelist) and enforce the UI max-size constant (the claimed max on Line 122) and return/raise a user-facing error if invalid so no large or wrong-type files are ever passed to setFile or further processing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@components/Upload.tsx`:
- Around line 38-40: Before assigning a new interval in processFile, clear any
existing timer stored in intervalRef.current so the old interval doesn't keep
running; call clearInterval(intervalRef.current) (after checking it's set)
immediately before setting intervalRef.current = setInterval(...), ensuring you
reference intervalRef.current, setInterval, PROGRESS_STEP and
PROGRESS_INTERVAL_MS in the same block so repeated invocations safely replace
the previous timer.
- Around line 25-27: Centralize and enforce file type and size validation before
any upload flow by adding a single validator (e.g. validateAndSetFile) and using
it from processFile, the drag-drop handler (handleDrop/onDrop) and the file
input handler (handleFileSelect) instead of directly calling setFile; the
validator should check mime type against the existing allowed pattern (image/*
or a stricter whitelist) and enforce the UI max-size constant (the claimed max
on Line 122) and return/raise a user-facing error if invalid so no large or
wrong-type files are ever passed to setFile or further processing.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d4976ae8-a575-4914-9ea1-4873d93cf42f
📒 Files selected for processing (3)
app/routes/visualizer.$id.tsxcomponents/Upload.tsxlib/constants.ts
✅ Files skipped from review due to trivial changes (2)
- app/routes/visualizer.$id.tsx
- lib/constants.ts
Summary by CodeRabbit
New Features
Chores