Conversation
Feate/create project
Feat/generate/3d
Completed project
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughThis pull request introduces comprehensive project management and 3D visualization features to the ArqiTech application. It adds backend API endpoints for saving and retrieving projects, implements a full-featured visualizer page with before/after image comparison, integrates Sonner for toast notifications, and rebrands storage paths from "roomify" to "arqitech". Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Upload as Upload Component
participant AI as AI Service
participant Server as Puter Server
participant KV as KV Store
participant Storage as Image Storage
User->>Upload: Upload image file
Upload->>Upload: Convert to base64
Upload->>Storage: Upload sourceImage
Storage-->>Upload: Image URL
Upload->>AI: generate3DView(sourceImage)
AI->>AI: Validate & prepare payload
AI->>Server: Request 3D render (txt2img)
Server->>Server: Generate 3D visualization
Server-->>AI: Return rendered image URL
AI->>AI: Fetch rendered image as data URL
AI-->>Upload: Return renderedImage & path
Upload->>KV: createProject(item, visibility)
KV->>KV: Store project metadata
KV-->>Upload: Project saved
Upload->>Upload: setProjects([newItem, ...])
Upload->>User: Navigate to visualizer
sequenceDiagram
participant User as User
participant Viz as VisualizerPage
participant Session as SessionStorage
participant Server as Puter Server
participant KV as KV Store
participant Comparison as Comparison Slider
User->>Viz: Navigate to /visualizer/[id]
Viz->>Session: Load project from sessionStorage
Session-->>Viz: Project data (if cached)
Viz->>Server: getProjectById(id)
Server->>KV: Fetch by project ID
KV-->>Server: Project metadata & images
Server-->>Viz: Project details
Viz->>Viz: Update state with fetched data
Viz->>User: Display source image & header
User->>Viz: Click Generate
Viz->>Viz: trigger handleGenerate
Viz->>Server: generate3DView(sourceImage)
loop Progress Update
Viz->>Viz: Update progress ticker
Viz->>User: Show progress bar
end
Server-->>Viz: renderedImage URL
Viz->>Session: Store results in sessionStorage
Viz->>Comparison: Display before/after slider
Viz->>User: Show rendered result & export button
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
Pull request overview
This PR evolves the web app toward an “ArqiTech” experience by introducing project persistence via a Puter worker, a substantially expanded Visualizer page (AI render + before/after comparison), and new UI dependencies/utilities to support the workflow.
Changes:
- Add project save/list/get flows backed by a new Puter worker endpoint and client-side action helpers.
- Implement a full Visualizer page with AI rendering, progress UI, and image comparison.
- Introduce new UI dependencies (sonner toaster, react-compare-slider, next-themes) and update navigation/layout wiring.
Reviewed changes
Copilot reviewed 23 out of 24 changed files in this pull request and generated 18 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks new dependencies (sonner, react-compare-slider, next-themes) and related graph changes. |
| package.json | Adds eslint/prettier integration packages at the repo root. |
| packages/ui/package.json | Adds next-themes and sonner to the shared UI package deps. |
| packages/ui/components/ui/sonner.tsx | New shared Toaster wrapper for sonner with lucide icons and CSS vars. |
| apps/web/package.json | Adds sonner and react-compare-slider to the web app. |
| apps/web/next.config.ts | Allows Next Image to load from puter.com / puter.site hosts. |
| apps/web/src/utils/puter.ts | Renames hosting config key and slug prefix to “arqitech”. |
| apps/web/src/types/puter.types.ts | Adds DesignItem/DesignConfig/AppStatus/CreateProjectParams types. |
| apps/web/src/types/project-card.types.ts | Expands ProjectCard props (adds id; time becomes broader type). |
| apps/web/src/types/ai.types.ts | Adds Generate3DViewParams type. |
| apps/web/src/lib/constants.ts | Rebrands storage paths and sets a Puter worker base URL. |
| apps/web/src/lib/puter.hosting.ts | Persists hosting config after creation and supports hosting uploads. |
| apps/web/src/lib/puter.action.ts | Adds createProject/getProjects/getProjectById using the worker + hosting uploads. |
| apps/web/src/lib/puter.worker.js | New worker routes for saving/listing/fetching projects from KV. |
| apps/web/src/lib/ai.actions.ts | New helper to fetch images as data URLs and generate AI renders. |
| apps/web/src/data/navbar.data.ts | Changes navbar links to placeholder hashes. |
| apps/web/src/components/home/Hero.tsx | Plumbs project state through Hero into Upload. |
| apps/web/src/components/home/Upload.tsx | Saves new uploads as projects and navigates to the visualizer. |
| apps/web/src/components/home/ProjectSection.tsx | Fetches and displays project history. |
| apps/web/src/components/home/ProjectCard.tsx | Card now links to project, renders image via Next Image, formats time. |
| apps/web/src/components/Navbar.tsx | Makes the logo link back to home. |
| apps/web/src/app/page.tsx | Hoists projects state to the home page and passes it down. |
| apps/web/src/app/layout.tsx | Updates metadata and mounts the new Toaster component. |
| apps/web/src/app/visualizer/[id]/page.tsx | Full client-side visualizer UI: render generation, compare slider, export. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "dependencies": { | ||
| "eslint-plugin-prettier": "^5.5.5", | ||
| "eslint-prettier-config": "^1.0.1" |
There was a problem hiding this comment.
These are ESLint/Prettier integration packages and should generally be in devDependencies, not shipped as runtime dependencies. Also, eslint-prettier-config appears unrelated to the common eslint-config-prettier package and pulls very old eslint/prettier versions per the lockfile; confirm this is intentional or switch to eslint-config-prettier.
| hasInitialGenerated.current = true; | ||
| } | ||
| } | ||
|
|
||
| setIsProjectLoading(false); | ||
| // Only reset if we don't already have a rendered image | ||
| if (!hasInitialGenerated.current) { | ||
| hasInitialGenerated.current = false; | ||
| } |
There was a problem hiding this comment.
This block is a no-op: hasInitialGenerated.current is only ever set to false inside the if (!hasInitialGenerated.current) branch, where it is already false. This looks like leftover logic and can be removed (or replaced with the intended reset behavior).
| hasInitialGenerated.current = true; | |
| } | |
| } | |
| setIsProjectLoading(false); | |
| // Only reset if we don't already have a rendered image | |
| if (!hasInitialGenerated.current) { | |
| hasInitialGenerated.current = false; | |
| } | |
| } | |
| // Track whether an initial render has been generated for this project | |
| hasInitialGenerated.current = !!fetchedProject.renderedImage; | |
| } | |
| setIsProjectLoading(false); |
| if (!userId) return jsonError(401, 'Authentication failed'); | ||
|
|
||
| const projects = (await userPuter.kv.list(PROJECT_PREFIX, true)) | ||
| .map(({value}) => ({ ...value, isPublic: true })) |
There was a problem hiding this comment.
/api/projects/list forces isPublic: true for every project, which overrides any stored visibility and makes the field unreliable. Return the persisted value (or default to false) instead of hardcoding it here.
| .map(({value}) => ({ ...value, isPublic: true })) | |
| .map(({ value }) => ({ ...value, isPublic: value?.isPublic ?? false })) |
| style={ | ||
| { | ||
| '--normal-bg': 'var(--popover)', | ||
| '--normal-text': 'var(--popover-foreground)', | ||
| '--normal-border': 'var(--border)', | ||
| '--border-radius': 'var(--radius)', | ||
| } as React.CSSProperties | ||
| } |
There was a problem hiding this comment.
This component uses React.CSSProperties but React (or the relevant type) isn’t imported, which will cause a TS compile error. Import type CSSProperties from react (or import type React from 'react') and cast to that instead of React.CSSProperties.
| const [progress, setProgress] = useState(0); | ||
| const [error, setError] = useState<string | null>(null); | ||
| const [currentImage, setCurrentImage] = useState<string | null>(null); | ||
|
|
There was a problem hiding this comment.
error state is set in handleGenerate, but it’s never rendered to the UI. As-is, failures will be silent for users; display the error message (and ideally provide a retry action) when status === 'error'.
| // Surface errors to the user so failures are not silent | |
| useEffect(() => { | |
| if (status === 'error') { | |
| const message = | |
| error || 'An unexpected error occurred while generating the 3D view.'; | |
| // Using alert here avoids changing JSX structure while still providing UI feedback | |
| // and satisfies the requirement that errors are visible to users. | |
| window.alert(message); | |
| } | |
| }, [status, error]); |
| import { | ||
| CircleCheckIcon, | ||
| InfoIcon, | ||
| Loader2Icon, | ||
| OctagonXIcon, | ||
| TriangleAlertIcon, | ||
| } from 'lucide-react'; | ||
| import { Toaster as Sonner, type ToasterProps } from 'sonner'; | ||
|
|
||
| const Toaster = ({ ...props }: ToasterProps) => { | ||
| return ( | ||
| <Sonner |
There was a problem hiding this comment.
sonner’s toaster relies on client-side React APIs; this file should be marked as a Client Component. Add 'use client'; at the top so it can be safely imported into Next.js layouts/pages without Server Component hook errors.
| export type HeroProps = { | ||
| projects: DesignItem[]; | ||
| setProjects: React.Dispatch<React.SetStateAction<DesignItem[]>>; | ||
| }; |
There was a problem hiding this comment.
HeroProps references React.Dispatch / React.SetStateAction, but React isn’t imported in this file. This will fail type-checking unless the React namespace is globally available; import the needed types from react (or import type React from 'react').
| export const PUTER_WORKER_URL = 'https://sensible-pen-2991.puter.work'; | ||
|
|
There was a problem hiding this comment.
PUTER_WORKER_URL is now hard-coded to a single production URL. This makes local/dev/staging deployments difficult and risks accidentally shipping the wrong endpoint; prefer reading from an environment variable (e.g., process.env.NEXT_PUBLIC_...) with a safe fallback.
| export const PUTER_WORKER_URL = 'https://sensible-pen-2991.puter.work'; | |
| const DEFAULT_PUTER_WORKER_URL = 'https://sensible-pen-2991.puter.work'; | |
| export const PUTER_WORKER_URL = | |
| typeof process !== 'undefined' && | |
| process.env && | |
| process.env.NEXT_PUBLIC_PUTER_WORKER_URL | |
| ? process.env.NEXT_PUBLIC_PUTER_WORKER_URL | |
| : DEFAULT_PUTER_WORKER_URL; |
| console.log('Fetching project with ID:', id); | ||
|
|
||
| try { | ||
| const response = await puter.workers.exec( | ||
| `${PUTER_WORKER_URL}/api/projects/get?id=${encodeURIComponent(id)}`, | ||
| { method: 'GET' } | ||
| ); | ||
|
|
||
| console.log('Fetch project response:', response); | ||
|
|
||
| if (!response.ok) { | ||
| console.error('Failed to fetch project:', await response.text()); | ||
| return null; | ||
| } | ||
|
|
||
| const data = (await response.json()) as { | ||
| project?: DesignItem | null; | ||
| }; | ||
|
|
||
| console.log('Fetched project data:', data); | ||
|
|
There was a problem hiding this comment.
getProjectById includes multiple console.log debug statements that will run in production and may leak project IDs / response details to user consoles. Remove these logs or gate them behind a debug flag.
|
|
||
| return data?.project ?? null; | ||
| } catch (error) { | ||
| console.log('Failed to save Project', error); |
There was a problem hiding this comment.
This catch block logs with console.log, while other error paths use console.error. For consistency (and to avoid missing errors in production logging), log failures with console.error and a clear message.
| console.log('Failed to save Project', error); | |
| console.error('Failed to save project:', error); |
Summary by CodeRabbit
Release Notes
New Features
Improvements