fix: add word boundaries to code highlighting regex#490
fix: add word boundaries to code highlighting regex#490narutamaaurum wants to merge 198 commits into
Conversation
- Refactor TemplateFile, TemplateFolder, and TemplateItem types to use a unified BaseTemplateItem with an optional content property. - Fix Prisma InputJsonValue casting in playground actions. - Resolve missing properties and typing issues in EnvManager tests. - Fix useWebContainer hook signature in playground page. - Remove duplicate imports and fix WebContainer event listener typing. - Ensure all tests pass and tsc --noEmit reports zero errors.
Signed-off-by: Angelina <angelinachatterjee2206@gmail.com>
…st renders Signed-off-by: Angelina <angelinachatterjee2206@gmail.com>
…types chore(profile): improve type safety by replacing 'any' types
added moving popular templates section from right to left. Fixes piyushdotcomm#436
refactor(react): address missing hook dependencies to remove exhaustive-deps eslint disables
…e-icon-395 Fix piyushdotcomm#395: Correct theme toggle icon active state representation
…ate-limit fix(security): prevent IP Spoofing Rate Limit Bypass
…ependency fix: stabilize useEffect dependencies in webcontainer preview
…json-error fix: handle non-json responses gracefully in github import dialog
…tion-deployment fix: add DATABASE_URL validation for collaboration server
…-dashboard-sidebar-logout feat(dashboard): add mobile sidebar logout support
…console-logs-playground-actions fix(playground): remove leftover console.log statements in actions
…manager-aria-labels fix: added aria-labels to package manager icon buttons
…rate-limit fix(deploy): add rate limiting to Vercel deploy endpoint
Typescript : defined interface for collaborator
…ortcut-modal Added a Keyboard Shortcut Modal component
…ground-schema fix: correct typo myPlaground → myPlayground in Prisma schema
added feature: Export Chat Transcript (Download as Markdown / JSON)
* feat(playground): add ai-powered readme generator * fix(readme-generator): remove unused imports and consolidate useAI import * fix: apply AI review feedback from CodeRabbit * fix: apply AI review feedback from CodeRabbit * fix: apply AI review feedback from CodeRabbit 2 * fix: address PR review feedback on streaming UX and auth check order
…el-env-manager-buttons fix: add aria labels to env manager icon buttons
* fix: resolve nested button hydration error in LogoutButton * fix: resolve nested button hydration error in LogoutButton * chore: remove unused imports and improve typing
- Remove unnecessary 'highlighted' variable that was just an alias for 'escaped' - Directly use 'escaped' in the highlight function logic This addresses a CodeRabbit nitpick from PR piyushdotcomm#447.
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
👋 Thanks for opening a PR, @narutamaaurum!Your PR has entered the 🚦 PR Review Pipeline.
What happens next
A pipeline status comment will appear below and update automatically as your PR progresses. While you wait
This comment is posted only once. |
WalkthroughApp shell and landing-page structure were updated, PWA/service-worker support was added, API validation and rate limiting were tightened, and the playground gained encrypted secrets, split-view editing, Git/time-travel, README generation, and runtime activity reporting. ChangesIntegrated app, API, and playground changes
Sequence Diagram(s)sequenceDiagram
participant PlaygroundHeader
participant PlaygroundModals
participant EditorArea
participant PlaygroundTabBar
participant useGitHistory
participant useGit
participant ReadmeGeneratorDialog
participant EnvManager
PlaygroundHeader->>PlaygroundModals: open README / preferences / time travel UI
ReadmeGeneratorDialog->>PlaygroundModals: stream generated README.md content
EditorArea->>useGitHistory: load commits and travel back
PlaygroundTabBar->>EditorArea: switch pane/file selection
PlaygroundSidebar->>useGit: show staged and unstaged changes
EnvManager->>app/api/playgrounds/[id]/secrets/route.ts: fetch, save, delete encrypted secrets
Estimated code review effort🎯 5 (Critical) | ⏱️ ~90+ minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning |
There was a problem hiding this comment.
Actionable comments posted: 11
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
app/dashboard/profile/page.tsx (1)
79-87:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winRemove duplicated stale JSX block; it currently breaks compilation.
The block starting at Line 160 is a duplicate/stale fragment and causes parse errors. It also keeps obsolete logout composition (Line 79-Line 87) that no longer matches
LogoutButton’s API.Suggested fix
- <div className="flex gap-3"> - <LogoutButton> - <Button - variant="outline" - className="gap-2 text-red-500 border-red-500/20 hover:bg-red-500/10 hover:border-red-500/50 hover:text-red-600"> - <LogOut className="w-4 h-4" /> - Logout - </Button> - </LogoutButton> - <DeleteAccountButton /> - </div> + <div className="flex gap-3"> + <LogoutButton /> + <DeleteAccountButton /> + </div> @@ - </div> - - <div className="flex items-center gap-4"> - <HeaderNewProjectButton /> - ... - </div> - </header> - <main className="w-full max-w-7xl mx-auto ..."> - ... - </main> - </div> + </div> ); }Also applies to: 160-265
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/dashboard/profile/page.tsx` around lines 79 - 87, Remove the duplicated/stale JSX fragment that was accidentally left in the file (the large duplicate block later in the file) and update the remaining logout UI to the current LogoutButton API: delete the composed <LogoutButton><Button>...<LogOut/>...</Button></LogoutButton> pattern and replace it with the new supported usage of LogoutButton (e.g., call LogoutButton directly or pass its new props instead of nesting a Button and LogOut icon), leaving DeleteAccountButton as-is; search for usages of LogoutButton and DeleteAccountButton in this component and ensure only the single, up-to-date LogoutButton instance remains.Source: Linters/SAST tools
modules/home/header.tsx (1)
88-190:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftUnresolved JSX merge artifacts currently break compilation.
Headeris syntactically invalid in its current state.
- In Lines 88-190, duplicated/overlapping
<SheetContent>branches and stalesetOpen(...)calls remain.- In Lines 205-245,
NavLinkincludes conflicting duplicated<Link>markup and unbalanced tags.This aligns with the parser failures and blocks build/runtime entirely.
Also applies to: 205-245
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/home/header.tsx` around lines 88 - 190, The Header component contains leftover merge-artifact JSX: remove the duplicated/overlapping SheetContent block and any stale setOpen(...) calls inside the mobile menu, ensure only one SheetContent (the one with className "w-[85%] max-w-[320px] ...") remains and that its JSX is balanced; also fix NavLink by removing the duplicated/conflicting <Link> fragments so tags are balanced and only the intended Link/Dashboard/Get Started branches remain; verify the Header function's JSX returns a single well-formed tree and run the JSX parser to confirm compilation.Source: Linters/SAST tools
modules/dashboard/components/template-selecting-modal.tsx (1)
66-70:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win
getTemplateSummariesis used but not imported.This will fail type-check/build (
Cannot find name 'getTemplateSummaries') and breaks template loading.Suggested fix
import type { TemplateCategory } from "`@/lib/templates/types`"; +import { getTemplateSummaries } from "`@/lib/templates/actions`"; import type { TemplateKey } from "`@/lib/template`";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/dashboard/components/template-selecting-modal.tsx` around lines 66 - 70, The function loadTemplates calls getTemplateSummaries but that symbol is not imported; add an import for getTemplateSummaries at the top of the module (the same export used by your templates API/service) so the identifier resolves, e.g. import { getTemplateSummaries } from the module that exports template APIs; keep existing usage in loadTemplates, setIsLoadingTemplates and setTemplateError unchanged.modules/playground/actions/index.ts (2)
75-82:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winRequire authentication before querying user playgrounds.
Line 81 uses
userIdthat can beundefined; that can drop the filter and expose other users’ records. UserequireCurrentUserId()here (same pattern used elsewhere in this file) beforefindMany.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/actions/index.ts` around lines 75 - 82, The query uses a possibly-undefined userId from currentUser(), which can remove the user filter and leak other users' playgrounds; replace the currentUser()/user?.id pattern with a call to requireCurrentUserId() to obtain a guaranteed id before calling db.playground.findMany, pass that id into the where clause (remove optional chaining), and remove the now-unused user variable so the query always filters by the authenticated user's id.
259-261:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon’t swallow duplication failures.
duplicateProjectByIdlogs the error and returnsundefined, which makes failures silent for callers. Re-throw the error (or return a typed failure object) so the UI can handle it consistently.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/actions/index.ts` around lines 259 - 261, The catch block inside duplicateProjectById currently only logs the error and returns undefined, which hides failures from callers; update the catch to either re-throw the caught error (e.g., throw error) after logging or return a well-typed failure result (e.g., { success: false, error }) so callers/UI can handle it consistently; locate the catch in the duplicateProjectById function in modules/playground/actions/index.ts and ensure the function's return type/signature is updated accordingly if you choose the typed-failure approach.modules/playground/components/collaboration-avatars.tsx (1)
25-29:⚠️ Potential issue | 🟠 Major | ⚡ Quick winValidate awareness user payload before rendering.
Line 25 force-casts untrusted awareness state to
Collaborator. If a peer sends malformeduserdata,u.name.charAt(0)can throw at render time. Add a runtime type guard before storing in state.Also applies to: 59-60
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/components/collaboration-avatars.tsx` around lines 25 - 29, The code is force-casting awareness state to Collaborator which can crash on malformed payloads; add a runtime type guard (e.g., isCollaborator(obj): obj && typeof obj.name === 'string' && obj.name.length > 0 && typeof obj.color === 'string' (and any other required fields)) and use it when building activeUsers before deduplication and before the similar logic around lines 59-60; replace the direct cast in the activeUsers creation and the other occurrence by filtering with isCollaborator(...) so only validated collaborator objects are stored via setUsers and rendered.modules/playground/hooks/useFileExplorer.tsx (1)
416-419:⚠️ Potential issue | 🟠 MajorFix potential ID remapping desync when renaming before persisting.
generateFileIdderives the ID from the file’s folder path viafindFilePath(file, rootFolder)and falls back to an empty path when the file isn’t found. Inmodules/playground/hooks/useFileExplorer.tsx(handleRenameFile, around lines 416-419),newFileIdis computed fromnewFilewhiletemplateDatais still pre-rename, sofindFilePath(newFile, templateData)may not locate it and the ID can omit the folder path—leading to mismatches with IDs computed from the post-rename tree. ComputenewFileIdfrom the updated tree (after mutatingtemplateData) or construct it deterministically fromparentPath+newFilename/newExtension.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/hooks/useFileExplorer.tsx` around lines 416 - 419, handleRenameFile currently computes newFileId by calling generateFileId(newFile, templateData) before the tree is updated, which lets findFilePath(newFile, templateData) miss the new location and drop the folder path. Fix by deriving the new ID from the updated tree or from the known parent path: after mutating templateData (or after applying the rename to a copy of the tree), call generateFileId with the updated tree so findFilePath returns the correct folder path; alternatively build the ID deterministically using parentPath + newFilename + newExtension instead of relying on findFilePath. Ensure you update the code that sets newFileId in handleRenameFile to use the post-rename tree or the constructed parentPath-based id.
🟠 Major comments (28)
placeholder5-1-1 (1)
1-1:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove placeholder file.
This file serves no purpose and should be removed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@placeholder5` at line 1, Delete the placeholder5 file from the repository and remove any imports or references to it (search for "placeholder5" in the codebase) so there are no dangling dependencies; if this file was added to any build configs or package manifests, also remove those entries to avoid build errors.placeholder-1-1 (1)
1-1:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove placeholder file.
This file contains only placeholder text and serves no purpose in the codebase. It appears to be accidentally committed and should be removed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@placeholder` at line 1, Delete the superfluous placeholder file named "placeholder" from the repository; remove any references to it in build scripts, imports, or configuration files (search for "placeholder") and commit the deletion with a clear message (e.g., "chore: remove placeholder file"); ensure CI/build passes after removal and update any tests or documentation that erroneously referenced the file.placeholder6-1-1 (1)
1-1:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove placeholder file.
This is one of six placeholder files (
placeholderthroughplaceholder6) that serve no purpose. All six should be removed from this PR.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@placeholder6` at line 1, Delete this unused placeholder file (placeholder6) from the PR and also remove the other five placeholder files named placeholder, placeholder2, placeholder3, placeholder4, and placeholder5 so none of the six placeholder files remain in the commit; ensure no import references or CI entries point to these filenames (e.g., searches for "placeholder" in repo) before committing.placeholder4-1-1 (1)
1-1:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove placeholder file.
This file contains only placeholder text and should be removed from the repository.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@placeholder4` at line 1, Delete the placeholder file named "placeholder4" from the repository and remove any references or imports to it across the codebase (search for "placeholder4" in imports, tests, or build config) so no broken references remain; if the file was listed in any packaging, index, or manifest, remove that entry and run the test/build to confirm nothing depends on it.placeholder3-1-1 (1)
1-1:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove placeholder file.
This file contains only placeholder text and serves no purpose in the codebase. Please remove it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@placeholder3` at line 1, Delete the unused placeholder file "placeholder3" from the repository and remove any references to it (imports, exports, index entries, or build/config mappings) so it is not included in compilation or packaging; ensure related tests or CI configs do not reference "placeholder3" and run a quick build/test to confirm nothing breaks.placeholder2-1-1 (1)
1-1:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove placeholder file.
This file contains only placeholder text and serves no purpose. It should be removed along with the other placeholder files.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@placeholder2` at line 1, Delete the unused placeholder file named "placeholder2" from the repository and remove any references to it in build configs or imports; ensure no tests or scripts depend on it before committing the deletion.app/api/chat.test.ts-79-118 (1)
79-118:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMost new “validation” tests don’t validate
RequestBodySchema.Across these sections, many cases only assert local lengths/counts and never parse against
chatSchemas.RequestBodySchema, so they can pass even when endpoint validation is broken. Please convert these tosafeParseassertions (success === true/false) per scenario.Minimal pattern to apply
it("accepts part.text at max limit", () => { const text = "a".repeat(SIZE_LIMITS.MAX_PART_TEXT); const payload = { messages: [{ role: "user", parts: [{ type: "text", text }] }], provider: "gemini" }; - expect(text.length).toBe(SIZE_LIMITS.MAX_PART_TEXT); + const parsed = chatSchemas.RequestBodySchema.safeParse(payload); + expect(parsed.success).toBe(true); }); it("should reject part.text exceeding limit", () => { const text = "a".repeat(SIZE_LIMITS.MAX_PART_TEXT + 1); const payload = { messages: [{ role: "user", parts: [{ type: "text", text }] }], provider: "gemini" }; - expect(text.length).toBeGreaterThan(SIZE_LIMITS.MAX_PART_TEXT); + const parsed = chatSchemas.RequestBodySchema.safeParse(payload); + expect(parsed.success).toBe(false); });Also applies to: 120-169, 171-249
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/chat.test.ts` around lines 79 - 118, The tests in app/api/chat.test.ts are only asserting local string/size values instead of validating through chatSchemas.RequestBodySchema; update each case (including the shown tests and the other ranges 120-169 and 171-249) to build the payload and call chatSchemas.RequestBodySchema.safeParse(payload) and assert safeParseResult.success === true for valid payloads and === false for invalid ones (e.g., part.text exceeding SIZE_LIMITS.MAX_PART_TEXT or total payload > MAX_TOTAL_PAYLOAD); locate tests that reference SIZE_LIMITS and the payload objects and replace the existing expect(length/size) assertions with safeParse success checks to ensure endpoint validation is exercised.app/api/chat/route.ts-57-60 (1)
57-60:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse byte length (not string length) for payload cap enforcement.
JSON.stringify(body).lengthcounts UTF-16 code units, not bytes, so multibyte payloads can bypass the intended 1MB byte limit.Suggested fix
-const totalSize = JSON.stringify(body).length; +const totalSize = new TextEncoder().encode(JSON.stringify(body)).length; return totalSize <= SIZE_LIMITS.MAX_TOTAL_PAYLOAD;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/chat/route.ts` around lines 57 - 60, The payload size check currently uses JSON.stringify(body).length (UTF-16 code units) which can undercount bytes; change the calculation of totalSize in the body => { ... } block to measure actual UTF-8 byte length (e.g., use Buffer.byteLength(json, "utf8") or new TextEncoder().encode(json).length) on the JSON string and then compare that byte count against SIZE_LIMITS.MAX_TOTAL_PAYLOAD so totalSize and the comparison to SIZE_LIMITS.MAX_TOTAL_PAYLOAD reflect bytes not string length.app/api/chat/route.ts-205-208 (1)
205-208:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
contentcan be dropped whenpartsis an empty array.Because
[]is truthy,msg.parts || (...)keeps emptypartsand ignoresmsg.content. A message withcontentandparts: []becomes empty.Suggested fix
const sanitizedMessages = messages.map((msg) => ({ role: msg.role, - parts: msg.parts || (msg.content ? [{ type: "text" as const, text: msg.content }] : []), + parts: + msg.parts && msg.parts.length > 0 + ? msg.parts + : msg.content + ? [{ type: "text" as const, text: msg.content }] + : [], })) as Omit<UIMessage, 'id'>[];🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/chat/route.ts` around lines 205 - 208, The mapping in sanitizedMessages uses `msg.parts || (...)` which treats an empty array as truthy and drops `msg.content`; update the logic in the sanitizedMessages map (the mapping that constructs role and parts) to treat only non-empty arrays as valid (e.g., check Array.isArray(msg.parts) && msg.parts.length > 0) and otherwise fall back to creating parts from msg.content when present, preserving the Omit<UIMessage,'id'> type shape.app/api/playgrounds/[id]/secrets/route.ts-28-32 (1)
28-32:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid returning
200 []for secret-fetch failures.Swallowing DB/decryption failures as an empty list hides operational/auth issues and can cause clients to treat “failure” as “no secrets”.
Suggested fix
} catch (error) { console.error("Failed to fetch secrets:", error); - // If DB fails (e.g. no connection string), return empty array so UI doesn't crash - return NextResponse.json([]); + return NextResponse.json( + { error: "Failed to fetch secrets" }, + { status: 500 } + ); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/playgrounds/`[id]/secrets/route.ts around lines 28 - 32, The catch block in the secrets route currently swallows DB/decryption failures by logging and returning an empty array via NextResponse.json([]); update the error handling in the export default handler (the route function that calls your secrets fetch) to return an appropriate error response instead: log the full error (including error message/stack) and return NextResponse.json({ error: "Failed to fetch secrets" }, { status: 500 }) or similar non-200 status so clients can distinguish failures from “no secrets”; do not return an empty list on exceptions and ensure the response contains minimal error info for the client and full details in the server log.modules/dashboard/actions/index.ts-142-144 (1)
142-144:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
deleteProjectByIdswallows errors, causing false-success UX on failure.This catch block logs and resolves, so callers can show success even when deletion fails.
Suggested fix
} catch (error) { console.error(error); + throw error; } };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/dashboard/actions/index.ts` around lines 142 - 144, The catch in deleteProjectById currently only console.error()s and swallows the error, which allows callers to believe deletion succeeded; update the catch to preserve the log but rethrow the error (or return a rejected Promise) so callers receive the failure. Locate the catch block in deleteProjectById and after logging include a throw error (or return Promise.reject(error)) so the error propagates to the caller instead of resolving silently.lib/api-utils.ts-136-151 (1)
136-151:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
getClientIpcurrently trusts spoofable forwarding headers whenremoteAddris missing.
isTrustedProxy = !remoteAddr || ...means many runtimes will trustx-real-ip/x-forwarded-forby default. That weakens IP-based controls (rate limiting, abuse checks).Suggested fix
-const isTrustedProxy = !remoteAddr || TRUSTED_PROXIES.has(remoteAddr); +const isTrustedProxy = remoteAddr.length > 0 && TRUSTED_PROXIES.has(remoteAddr); if (isTrustedProxy) { const realIpHeader = request.headers.get("x-real-ip"); ... const forwardedHeader = request.headers.get("x-forwarded-for"); if (forwardedHeader) { - const forwarded = forwardedHeader.split(",")[0].trim(); + const parts = forwardedHeader.split(",").map((p: string) => p.trim()).filter(Boolean); + const forwarded = parts[parts.length - 1] || ""; if (forwarded.length > 0) return forwarded; } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/api-utils.ts` around lines 136 - 151, getClientIp currently treats missing remoteAddr as a trusted proxy (isTrustedProxy = !remoteAddr || TRUSTED_PROXIES.has(remoteAddr)), which allows spoofable headers to be trusted; change the logic so headers are only trusted when remoteAddr is present and known to be a trusted proxy (e.g., isTrustedProxy = remoteAddr && TRUSTED_PROXIES.has(remoteAddr)); update the branch that reads request.headers.get("x-real-ip") and "x-forwarded-for" to run only when that adjusted isTrustedProxy is true, and otherwise return remoteAddr (or a safe default) without consulting those headers to avoid trusting spoofed client-supplied values.app/api/upload-zip/route.ts-199-203 (1)
199-203:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid exposing raw internal error messages to clients.
Line 199-Line 203 can disclose backend internals through
error.message. Keep the detailed message in logs and return a generic 500 payload.Suggested fix
- const errorMessage = error instanceof Error ? error.message : "Failed to process ZIP file"; - - return NextResponse.json( - { error: errorMessage }, - { status: 500 } - ); + return NextResponse.json( + { error: "Failed to process ZIP file" }, + { status: 500 } + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/upload-zip/route.ts` around lines 199 - 203, The code currently returns raw error.message to clients via errorMessage and NextResponse.json; instead, change the handler in this route (the block that builds errorMessage and calls NextResponse.json) to log the full error detail using your logger (e.g., console.error or the existing request/logger) and return a generic JSON error payload (e.g., { error: "Internal server error" }) with status 500; keep the check for error instanceof Error only for logging, not for client output, and ensure NextResponse.json does not include internal error.message.modules/dashboard/components/dashboard-sidebar.tsx-199-207 (1)
199-207:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
LogoutButtonusage no longer matches its exported API.Line 202-Line 205 pass
className/children toLogoutButton, but the component now accepts no props. This is a compile-time contract break.Suggested fix
{isMobile && ( <SidebarMenuItem> - <SidebarMenuButton asChild tooltip="Logout" className="h-10 hover:bg-background/80 transition-all duration-200"> - <LogoutButton className="flex items-center gap-2 hover:text-red-500" > - <LogOut className="h-4 w-4" /> - <span className="font-medium">Logout</span> - </LogoutButton> - </SidebarMenuButton> + <LogoutButton /> </SidebarMenuItem> )}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/dashboard/components/dashboard-sidebar.tsx` around lines 199 - 207, The code passes className and children to LogoutButton which no longer accepts props; update the JSX so LogoutButton is used without props and move styling/children to the surrounding components: remove className and children from LogoutButton in the SidebarMenuButton block and instead apply the className to SidebarMenuButton or a new wrapper element (e.g., a span/div) and render the LogOut icon and "Logout" text outside or around the LogoutButton; ensure you only call <LogoutButton /> (no props/children) and keep the clickable area provided by SidebarMenuButton/SidebarMenuItem.auth.ts-33-40 (1)
33-40:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftFirst-login path is still race-prone and can intermittently reject valid sign-ins.
On Lines 33-40, two concurrent requests can both observe “no user” and race to create; one may fail and return
falseon Lines 104-113. This causes sporadic auth failures under load.Safer transactional pattern (upsert-based)
- let user = await tx.user.findUnique({ - where: { email }, - }); - - if (!user) { - user = await tx.user.create({ - data: { - email, - name, - image, - accounts: { - create: { ... } - }, - }, - }); - if (!user) throw new Error("Failed to create user"); - return; - } + const user = await tx.user.upsert({ + where: { email }, + update: { name, image }, + create: { email, name, image }, + }); + + const existingAccount = await tx.account.findUnique({ + where: { + provider_providerAccountId: { + provider: account.provider, + providerAccountId: account.providerAccountId, + }, + }, + }); + + if (!existingAccount) { + // Only auto-create account when this is the same newly-created user flow. + // Otherwise keep blocking auto-linking. + // (Implement explicit branch based on your trusted linking policy.) + }Also applies to: 104-113
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth.ts` around lines 33 - 40, The current find-then-create flow using tx.user.findUnique and tx.user.create is race-prone; replace it with a single atomic upsert (tx.user.upsert) or use the transaction's atomic create-or-get pattern so concurrent requests cannot both try to create the same user; specifically change the logic around tx.user.findUnique / tx.user.create (the first-login path) and the later create-retry branch (the code that returns false on the create failure) to use tx.user.upsert (providing where: { email }, update: {}, create: { ... }) or an equivalent DB-side upsert to ensure a safe, idempotent first-login path. Ensure any error handling still surfaces real errors but do not treat a unique-constraint create failure as an expected flow; use the upsert result as the canonical user object.lib/yjs.ts-72-78 (1)
72-78:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard browser-only event dispatch in the reconnect handler.
window.dispatchEvent(...)is unguarded here. In non-browser execution paths, this can throw and break collaboration recovery flow.Suggested fix
if (status === "connected") { console.log("[Yjs] Reconnected successfully"); // Force preview refresh after reconnect - window.dispatchEvent( - new CustomEvent("yjs-reconnected") - ); + if (typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("yjs-reconnected")); + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/yjs.ts` around lines 72 - 78, The reconnect handler currently calls window.dispatchEvent unguarded when status === "connected" (the block that logs "[Yjs] Reconnected successfully" and dispatches "yjs-reconnected"); wrap that dispatch in a browser-safe guard such as checking typeof window !== "undefined" and typeof window.dispatchEvent === "function" (and optionally typeof CustomEvent === "function") before calling dispatchEvent so non-browser runtimes won't throw and break recovery.modules/playground/components/playground-editor.tsx-489-494 (1)
489-494:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftAvoid disposing global Monaco models from each editor instance.
These blocks iterate
monaco.editor.getModels()and dispose models not owned by the current pane. In split view, one pane can dispose another pane’s active model, causing editor breakage.Also applies to: 534-543
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/components/playground-editor.tsx` around lines 489 - 494, The current unmount logic disposes every Monaco model via monacoRef.current.editor.getModels(), which can destroy models owned by other panes; change it to only dispose models owned by this editor instance by either: (a) tracking models you created in this component (e.g., createdModels set populated when you call monaco.editor.createModel or setModel) and only calling dispose() on those entries, or (b) checking the editor instance's active model via monacoRef.current.editor.getModel() and only disposing that model if it matches a model created by this component; update both occurrences (around monacoRef.current.editor.getModels() at the referenced blocks and the one at 534-543) to use this scoped disposal approach instead of iterating global monaco.editor.getModels().modules/playground/components/playground-editor.tsx-550-550 (1)
550-550:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove duplicate
heightprop onEditor.
heightis specified twice, which is an ESLint error (react/jsx-no-duplicate-props) and can fail CI.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/components/playground-editor.tsx` at line 550, The Editor JSX in PlaygroundEditor is passing the height prop twice, triggering react/jsx-no-duplicate-props; locate the <Editor ... /> element in playground-editor.tsx (the Editor component usage) and remove the duplicate height attribute (or consolidate into a single height value) so only one height prop is provided on the Editor element.Source: Linters/SAST tools
modules/playground/components/readme-generator-dialog.tsx-116-121 (1)
116-121:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid awaiting heavy side effects on every streamed chunk.
onReadmeGeneratedis awaited per chunk; with the current callback implementation this repeatedly saves/open-files during one generation. Move persistence callback to once after stream completion, and flush the decoder tail.Proposed fix
- while (true) { - const { done, value } = await reader.read(); - if (done) break; - accumulated += decoder.decode(value, { stream: true }); - await onReadmeGenerated(accumulated); - } + while (true) { + const { done, value } = await reader.read(); + if (done) break; + accumulated += decoder.decode(value, { stream: true }); + } + accumulated += decoder.decode(); const finalContent = accumulated.trim(); if (!finalContent) { throw new Error("Model returned an empty README. Try again."); } + await onReadmeGenerated(finalContent);Also applies to: 123-126
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/components/readme-generator-dialog.tsx` around lines 116 - 121, The loop currently awaits onReadmeGenerated for every streamed chunk causing heavy side-effects; instead, inside the reader.read() loop only accumulate decoded chunks using decoder.decode(value, {stream: true}) into accumulated (do not await onReadmeGenerated), then after the loop flush the decoder tail with decoder.decode() (no args) and append that to accumulated, and finally call/await onReadmeGenerated once with the complete accumulated string; update the code around the reader.read() loop, decoder.decode usage, and the single post-loop onReadmeGenerated call to implement this.modules/playground/components/git-diff-viewer.tsx-29-35 (1)
29-35:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle rejected diff fetches to avoid stuck loading state.
If
getFileDiff(filepath)rejects,loadingnever resets and the modal can remain indefinitely blocked.💡 Suggested fix
- getFileDiff(filepath).then((diff) => { - if (isMounted) { - setOriginal(diff.original || ""); - setModified(diff.modified || ""); - setLoading(false); - } - }); + (async () => { + try { + const diff = await getFileDiff(filepath); + if (!isMounted) return; + setOriginal(diff.original || ""); + setModified(diff.modified || ""); + } catch (error) { + if (!isMounted) return; + setOriginal(""); + setModified(""); + } finally { + if (isMounted) setLoading(false); + } + })();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/components/git-diff-viewer.tsx` around lines 29 - 35, The current getFileDiff(filepath) promise chain doesn't handle rejections so loading can stay true; update the call in git-diff-viewer to handle errors and always reset state when unmounted check permits: add a .catch handler (or use async/await with try/catch/finally) around getFileDiff to log or surface the error and in the .finally block (or after catch) call setLoading(false) only if isMounted is true, and optionally setOriginal/setModified to empty strings on error; modify the code around getFileDiff(filepath) and the setOriginal, setModified, setLoading calls to ensure setLoading(false) runs on both success and failure.modules/playground/hooks/useGitHistory.ts-37-42 (1)
37-42:⚠️ Potential issue | 🟠 MajorAwait git setup processes (
.exit) before running the initial commitIn
modules/playground/hooks/useGitHistory.ts(lines 37-42), thegit configandgit addcommands are spawned without waiting for their.exit, sogit commitcan start before staging/config completes.Proposed fix
- await instance.spawn("git", ["config", "user.name", "Editron User"]); - await instance.spawn("git", ["config", "user.email", "user@editron.dev"]); + const configNameProcess = await instance.spawn("git", ["config", "user.name", "Editron User"]); + await configNameProcess.exit; + const configEmailProcess = await instance.spawn("git", ["config", "user.email", "user@editron.dev"]); + await configEmailProcess.exit; // Create initial commit - await instance.spawn("git", ["add", "."]); + const addInitialProcess = await instance.spawn("git", ["add", "."]); + await addInitialProcess.exit;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/hooks/useGitHistory.ts` around lines 37 - 42, The git setup commands spawned in useGitHistory.ts are not being awaited for completion, so ensure you await the spawned processes' .exit before starting the commit: for each instance.spawn call that runs "git config" and "git add" wait for the returned process.exit, and likewise await commitProcess.exit after spawning the commit; update the code around the instance.spawn calls (the "git config", "git add", and commitProcess variables) to capture their process objects and await their .exit to guarantee ordering.modules/playground/hooks/useGitHistory.ts-56-63 (1)
56-63:⚠️ Potential issue | 🟠 MajorAwait
pipeTo(...)completion before parsing collected git output
currentHashProcess.output.pipeTo(...)(56-63) andlogProcess.output.pipeTo(...)(74-83) are not awaited; the code parsescurrentHash/logOutputafter only awaitingprocess.exit. WebContainer docs only guaranteeprocess.exitis a promise for the exit code and do not specify any ordering relative tooutput.pipeTocompletion, so parsing can race with output delivery.Proposed fix
- currentHashProcess.output.pipeTo( + const currentHashPipe = currentHashProcess.output.pipeTo( new WritableStream({ write(data) { currentHash += data; }, }) ); - await currentHashProcess.exit; + await Promise.all([currentHashProcess.exit, currentHashPipe]); @@ - logProcess.output.pipeTo( + const logPipe = logProcess.output.pipeTo( new WritableStream({ write(data) { logOutput += data; }, }) ); - - const exitCode = await logProcess.exit; + const [exitCode] = await Promise.all([logProcess.exit, logPipe]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/hooks/useGitHistory.ts` around lines 56 - 63, The code races parsing git output with stream delivery because currentHashProcess.output.pipeTo(...) and logProcess.output.pipeTo(...) are not awaited; update useGitHistory.ts to await the pipeTo promises (for currentHashProcess.output and for logProcess.output) before parsing currentHash and logOutput — reference the currentHashProcess, logProcess, currentHash and logOutput symbols and ensure you await the returned promises from their .output.pipeTo(...) calls (or store them and await them) prior to processing the collected data and/or awaiting parsing logic.modules/playground/lib/git-fs.ts-44-77 (1)
44-77:⚠️ Potential issue | 🟠 MajorPreserve
ENOENTerror code instat/lstat(modules/playground/lib/git-fs.ts lines 44-77, 79-113)isomorphic-git checks
err.code === 'ENOENT'to treat missing paths as non-fatal; these wrappers currently thrownew Error(...)without settingcode, so missing paths can be handled as unexpected failures.Proposed fix
- throw new Error(`ENOENT: no such file or directory, stat '${path}'`); + const err = new Error(`ENOENT: no such file or directory, stat '${path}'`) as Error & { code?: string }; + err.code = "ENOENT"; + throw err; @@ - throw new Error(`ENOENT: no such file or directory, lstat '${path}'`); + const err = new Error(`ENOENT: no such file or directory, lstat '${path}'`) as Error & { code?: string }; + err.code = "ENOENT"; + throw err;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/lib/git-fs.ts` around lines 44 - 77, The stat/lstat wrappers in git-fs.ts currently throw new Error(...) for missing paths which loses the original ENOENT error code; modify the catch path(s) in the async functions stat and lstat so that when path is not found you throw an Error object that includes code === 'ENOENT' (e.g., create the Error and set error.code = 'ENOENT' or rethrow the original error), preserving the ENOENT property expected by isomorphic-git; update both stat and lstat error branches that currently throw new Error(`ENOENT: no such file or directory, stat '${path}'`) to include the code property.modules/playground/hooks/useGit.ts-125-133 (1)
125-133:⚠️ Potential issue | 🟠 MajorAvoid
Bufferin browser-side blob decoding (useGit.ts). Decodegit.readBlob’sblob: Uint8ArraywithTextDecoderinstead ofBuffer.from(...)to prevent client runtime crashes (e.g.,Bufferundefined).Proposed fix
- original = Buffer.from(blob).toString("utf8"); + original = new TextDecoder().decode(blob);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/hooks/useGit.ts` around lines 125 - 133, The browser-side decode of git.readBlob currently uses Buffer.from(blob).toString("utf8") which fails in client runtimes; replace that decoding with a TextDecoder-based decode (e.g., new TextDecoder("utf-8").decode(blob)) where blob comes from the git.readBlob call so the variable original is assigned a string without relying on Buffer; update the code around currentCommit, the git.readBlob invocation, and the assignment to original in useGit.ts accordingly.modules/playground/lib/chat-export.ts-35-37 (1)
35-37:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSynthetic-message filter currently drops legitimate user messages.
Line 35 / Line 75 skip any user message with no
parts, even whenmsg.contentis present (which your own fallback reads). This can silently remove real user text from exports.Suggested fix
-// Skip synthetic messages -if (msg.role === "user" && textParts.length === 0) { +// Skip only truly empty synthetic user messages +if (msg.role === "user" && !textContent.trim()) { return; }Also applies to: 75-77
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/lib/chat-export.ts` around lines 35 - 37, The current synthetic-message filter in chat-export checks if msg.role === "user" && textParts.length === 0 and returns early, which drops legitimate user messages when msg.content exists; update the logic in the functions using that check (the blocks referencing msg.role, textParts, and msg.content) so you only skip the message when there are neither parts nor any fallback content—i.e., if textParts.length === 0 AND (!msg.content || msg.content.length === 0) then return; otherwise use msg.content as the fallback source for textParts/exports; apply the same change to both occurrences around the checks at the top-level export logic and the secondary block (the ones with msg.role, textParts, and msg.content).modules/playground/hooks/useYjsWebContainerSync.ts-52-85 (1)
52-85:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReinitialize and detach observers on every dependency change, not only unmount.
Line 83 only flips
disposed; it does not unobserve existing listeners or clear debounce timers for effect reruns. That leaves stale observers/timers alive acrossplaygroundId/templateData/writeFileSyncchanges, and existingfileKeyentries then block rebinding to the newY.Doc.Suggested fix
-const observersRef = useRef<Map<string, (event: Y.YTextEvent) => void>>(new Map()); +const observersRef = useRef< + Map<string, { yText: Y.Text; observer: (event: Y.YTextEvent) => void }> +>(new Map()); ... - yText.observe(observer); - observersRef.current.set(fileKey, observer); + yText.observe(observer); + observersRef.current.set(fileKey, { yText, observer }); ... - return () => { - disposed = true; - }; + return () => { + disposed = true; + observersRef.current.forEach(({ yText, observer }) => yText.unobserve(observer)); + observersRef.current.clear(); + debounceTimersRef.current.forEach((timer) => clearTimeout(timer)); + debounceTimersRef.current.clear(); + };Also applies to: 89-101
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/hooks/useYjsWebContainerSync.ts` around lines 52 - 85, The effect's teardown currently only sets disposed and leaves observers/timers attached; update the cleanup to iterate observersRef.current and call yText.unobserve(observer) (or appropriate unobserve API for the Y.Text instances tracked), clear and clearTimeout all timers from debounceTimersRef.current, and reset both maps so reruns can rebind; additionally ensure setupSync (and any async callbacks) checks the disposed flag before attaching new observers and that observers are also removed in the catch/failure paths to avoid leaking observers across playgroundId/templateData/writeFileSync changes.modules/webcontainers/hooks/useWebContainer.ts-146-150 (1)
146-150:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAuto-init effect can enter an unbounded retry loop on boot failures.
When initialization fails, state returns to
instance = nullandisLoading = false, which satisfies Line 147 again and immediately retries. This can hammer boot/logs indefinitely.Suggested fix
-import { useCallback, useEffect } from "react"; +import { useCallback, useEffect, useRef } from "react"; ... +const didAutoInitRef = useRef(false); useEffect(() => { - if (!instance && !isLoading) { - initialize(); + if (!didAutoInitRef.current && !instance && !isLoading && !error) { + didAutoInitRef.current = true; + void initialize(); } -}, [instance, isLoading, initialize]); +}, [instance, isLoading, error, initialize]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/webcontainers/hooks/useWebContainer.ts` around lines 146 - 150, The auto-init useEffect (watching instance, isLoading, initialize) can spin if initialize fails because instance stays null and isLoading false; modify the effect to avoid immediate infinite retries by tracking attempts and applying backoff: add a ref or state like initAttemptCount (or lastInitError timestamp) used inside the effect to only call initialize immediately on first mount (initAttemptCount === 0) and on subsequent failures schedule initialize with a setTimeout using exponential backoff (e.g., 1s * 2^attempts capped) and increment initAttemptCount in the catch path of initialize; reference the useEffect containing initialize, the initialize function itself, and the instance/isLoading variables and ensure you clear any pending timeout in the effect cleanup to avoid leaks.modules/playground/hooks/useYjsWebContainerSync.ts-46-50 (1)
46-50:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse a path-scoped fallback key to avoid cross-folder key collisions.
Line 46 falls back to
${filename}${ext}only. Files with the same name in different directories will share one Yjs text key whenidis missing, causing content to overwrite across files.Suggested fix
-const fileKey = fileId || `${file.filename}${ext}`; +const normalizedPath = path ? `${path}/${file.filename}${ext}` : `${file.filename}${ext}`; +const fileKey = fileId ?? normalizedPath;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@modules/playground/hooks/useYjsWebContainerSync.ts` around lines 46 - 50, The fallback key generation for Yjs uses fileKey = fileId || `${file.filename}${ext}` which causes collisions for same filenames in different folders; update the fallback to include the path (use the same logic as filePath) so the key becomes fileId || `${path ? `${path}/` : ''}${file.filename}${ext}` (or otherwise derive it from filePath) ensuring unique keys per folder; modify the logic where fileKey is computed in useYjsWebContainerSync.ts and keep filePath construction unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e1928f0e-a6ef-48bc-a33d-4dd0040b9b2a
⛔ Files ignored due to path filters (4)
package-lock.jsonis excluded by!**/package-lock.jsonpnpm-lock.yamlis excluded by!**/pnpm-lock.yamlpublic/icon-192x192.pngis excluded by!**/*.pngpublic/icon-512x512.pngis excluded by!**/*.png
📒 Files selected for processing (103)
.env.example.vscode/settings.jsonapp/(root)/page.tsxapp/api/chat.test.tsapp/api/chat/route.tsapp/api/completion/route.tsapp/api/deploy/netlify/route.tsapp/api/deploy/vercel/route.tsapp/api/playgrounds/[id]/secrets/route.tsapp/api/projects/[id]/download/route.tsapp/api/readme/route.tsapp/api/upload-zip/route.tsapp/dashboard/error.tsxapp/dashboard/profile/page.tsxapp/globals.cssapp/icon.tsxapp/layout.tsxapp/playground/[id]/page.tsxapp/sw.tsauth.config.tsauth.tscomponents/CodeTimeTravel.tsxcomponents/ShortcutModal.tsxcomponents/marketing/home-page-client.tsxcomponents/marketing/template-card.tsxcomponents/ui/animated-shader-background.tsxcomponents/ui/button.tsxcomponents/ui/commits-grid.tsxcomponents/ui/glowing-effect.tsxcomponents/ui/toggle-theme.tsxeditron-starters/bolt-qwik/src/components/starter/hero/hero.tsxenv.tslib/api-utils.tslib/encryption.tslib/user-data.tslib/yjs.tsmodules/auth/components/logout-button.tsxmodules/auth/types.tsmodules/dashboard/actions/index.tsmodules/dashboard/components/dashboard-sidebar.tsxmodules/dashboard/components/empty-state.tsxmodules/dashboard/components/github-import-dialog.tsxmodules/dashboard/components/project-table.tsxmodules/dashboard/components/template-selecting-modal.tsxmodules/home/code-line.tsxmodules/home/features.tsxmodules/home/header.tsxmodules/home/hero-code.tsxmodules/playground/actions/index.tsmodules/playground/components/ai-chat-panel.tsxmodules/playground/components/breadcrumbs.tsxmodules/playground/components/collaboration-avatars.tsxmodules/playground/components/editor-area.tsxmodules/playground/components/env-manager.tsxmodules/playground/components/git-diff-viewer.tsxmodules/playground/components/git-source-control.tsxmodules/playground/components/package-manager.tsxmodules/playground/components/playground-editor.tsxmodules/playground/components/playground-header.tsxmodules/playground/components/playground-modals.tsxmodules/playground/components/playground-sidebar.tsxmodules/playground/components/playground-tab-bar.tsxmodules/playground/components/preferences-dialog.tsxmodules/playground/components/readme-generator-dialog.tsxmodules/playground/components/status-bar.tsxmodules/playground/contexts/playground-context.tsxmodules/playground/hooks/useAI.test.tsmodules/playground/hooks/useAI.tsmodules/playground/hooks/useFileExplorer.tsxmodules/playground/hooks/useGit.tsmodules/playground/hooks/useGitHistory.tsmodules/playground/hooks/usePlaygroundUI.tsmodules/playground/hooks/usePreferences.tsmodules/playground/hooks/useYjsWebContainerSync.tsmodules/playground/lib/chat-export.tsmodules/playground/lib/git-fs.tsmodules/playground/lib/path-to-json.tsmodules/profile/actions.tsmodules/profile/components/AIInsights.tsxmodules/profile/components/CompactProjectTable.tsxmodules/profile/components/ContributionHeatmap.tsxmodules/profile/components/KPIStats.tsxmodules/profile/components/RunningEnvironments.tsxmodules/profile/components/UsageAnalytics.tsxmodules/webcontainers/components/runtime-activity-panel.tsxmodules/webcontainers/components/terminal.tsxmodules/webcontainers/components/webcontainer-preview.tsxmodules/webcontainers/hooks/useWebContainer.tsnext.config.tspackage.jsonplaceholderplaceholder2placeholder3placeholder4placeholder5placeholder6placeholder7placeholder9pnpm-workspace.yamlprisma/schema.prismapublic/manifest.jsonserver/collab.tstests/env-manager.test.tsx
💤 Files with no reviewable changes (4)
- tests/env-manager.test.tsx
- .env.example
- modules/home/hero-code.tsx
- modules/playground/lib/path-to-json.ts
| export default function Home() { | ||
| const [isLoading, setIsLoading] = useState(true); | ||
| export default async function Home() { | ||
| const popularTemplates = await getPopularTemplateSummaries(4); | ||
|
|
||
| // Schema Markup for AI SEO (Organization & SoftwareApplication) | ||
| const schemaMarkup = { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Default export declarations:"
rg -n '^\s*export\s+default' 'app/(root)/page.tsx'
echo
echo "Hook usage + client directive presence:"
rg -n 'useState|useEffect|^"use client";' 'app/(root)/page.tsx'
echo
echo "Tail of file to confirm orphaned JSX:"
nl -ba 'app/(root)/page.tsx' | sed -n '108,132p'Repository: piyushdotcomm/Editron
Length of output: 439
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE='app/(root)/page.tsx'
echo "=== File head (1-80) with line numbers ==="
awk 'NR>=1 && NR<=80 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== File middle (80-140) with line numbers ==="
awk 'NR>=80 && NR<=160 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== Presence of use client directive ==="
rg -n '["'\'']use client["'\''];|^"use client"$' "$FILE" || true
echo
echo "=== Quick syntax hints: default exports + JSX end ==="
rg -n '^\s*export\s+default' "$FILE"Repository: piyushdotcomm/Editron
Length of output: 4598
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE='app/(root)/page.tsx'
echo "=== File head (1-80) with line numbers ==="
awk 'NR>=1 && NR<=80 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== File middle (80-140) with line numbers ==="
awk 'NR>=80 && NR<=160 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== Presence of use client directive ==="
rg -n '["'\'']use client["'\''];|^"use client"$' "$FILE" || true
echo
echo "=== Quick syntax hints: default exports + JSX end ==="
rg -n '^\s*export\s+default' "$FILE"Repository: piyushdotcomm/Editron
Length of output: 4598
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE='app/(root)/page.tsx'
echo "=== File head (1-80) with line numbers ==="
awk 'NR>=1 && NR<=80 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== File middle (80-140) with line numbers ==="
awk 'NR>=80 && NR<=160 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== Presence of use client directive ==="
rg -n '["'\'']use client["'\''];|^"use client"$' "$FILE" || true
echo
echo "=== Quick syntax hints: default exports + JSX end ==="
rg -n '^\s*export\s+default' "$FILE"Repository: piyushdotcomm/Editron
Length of output: 4598
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE='app/(root)/page.tsx'
echo "=== File head (1-80) with line numbers ==="
awk 'NR>=1 && NR<=80 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== File middle (80-160) with line numbers ==="
awk 'NR>=80 && NR<=160 {printf "%5d:%s\n", NR, $0}' "$FILE"
echo
echo "=== Presence of use client directive ==="
rg -n '["'\'']use client["'\''];|^"use client"$' "$FILE" || true
echo
echo "=== Default export declarations ==="
rg -n '^\s*export\s+default' "$FILE"Repository: piyushdotcomm/Editron
Length of output: 4580
Resolve merged Home conflict (this file is currently non-compilable).
app/(root)/page.tsxhas twoexport default Homedeclarations (export default function Home()at line 18 andexport default async function Home()at line 20).- There’s stray/out-of-scope JSX after the function closes (
<HomePageClient popularTemplates={popularTemplates} />around lines 120-123), leaving mismatched closing braces/tags. - The file uses
useState/useEffectbut contains no"use client"directive; keep hooks insideHomePageClient(or make a proper client component) and keep the servergetPopularTemplateSummariesfetch in the serverHome.
🧰 Tools
🪛 Biome (2.4.16)
[error] 18-123: Illegal duplicate default export declarations
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`(root)/page.tsx around lines 18 - 23, You have two conflicting Home
exports and client hooks in a server component; keep only the async server
component Home that calls getPopularTemplateSummaries(4) and returns the JSX
which renders the client component HomePageClient, then move any
useState/useEffect logic into the HomePageClient component (or make
HomePageClient a separate file) and add the "use client" directive at the top of
that client component; remove the duplicate export default function Home() and
stray/mismatched JSX so Home returns a single valid element <HomePageClient
popularTemplates={popularTemplates} /> and ensure HomePageClient accepts
popularTemplates prop and contains the hooks and client-only logic.
| const session = await auth(); | ||
| // In a real app we'd check if the user is the owner of the playground | ||
|
|
||
| // Attempt to fetch secrets | ||
| const secrets = await prisma.secret.findMany({ | ||
| where: { playgroundId: params.id }, | ||
| }); |
There was a problem hiding this comment.
Enforce authentication and ownership checks before any secret operation.
session is fetched but never validated, and none of the handlers verify that params.id belongs to session.user.id. Any caller who knows a playground ID can read, upsert, or delete that playground’s secrets.
Suggested fix
+async function requirePlaygroundAccess(playgroundId: string) {
+ const session = await auth();
+ const userId = session?.user?.id;
+ if (!userId) {
+ return { error: NextResponse.json({ error: "Unauthorized" }, { status: 401 }) };
+ }
+
+ const owned = await prisma.playground.findFirst({
+ where: { id: playgroundId, userId },
+ select: { id: true },
+ });
+ if (!owned) {
+ return { error: NextResponse.json({ error: "Forbidden" }, { status: 403 }) };
+ }
+ return { userId };
+}-const session = await auth();
+const access = await requirePlaygroundAccess(params.id);
+if ("error" in access) return access.error;Also applies to: 40-43, 85-99
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/api/playgrounds/`[id]/secrets/route.ts around lines 11 - 17, Ensure
authentication and ownership are validated before any secret operations: after
calling auth() verify session and session.user.id exists, then fetch the
playground and confirm ownership (e.g., query prisma.playground.findUnique({
where: { id: params.id } }) and check playground.ownerId === session.user.id).
Return appropriate 401/403 responses if unauthenticated or not the owner, and
perform this same check in each handler that calls prisma.secret.findMany,
prisma.secret.upsert (or create/update), and prisma.secret.delete to prevent
unauthorized access.
| // The ENCRYPTION_KEY should be exactly 32 bytes long. | ||
| // If it's not set, we'll fall back to a dummy key for local development to prevent crashes, | ||
| // but in production this MUST be set. | ||
| const envKey = process.env.ENCRYPTION_KEY || "fallback_dummy_key_for_dev_only_123!"; | ||
| let secretKey: Buffer; |
There was a problem hiding this comment.
Hardcoded fallback encryption key is a production secret-compromise risk.
If ENCRYPTION_KEY is missing, encryption uses a known constant, making stored secrets recoverable by anyone with source access.
Suggested fix
-const envKey = process.env.ENCRYPTION_KEY || "fallback_dummy_key_for_dev_only_123!";
+const envKey = process.env.ENCRYPTION_KEY;
+if (!envKey && process.env.NODE_ENV === "production") {
+ throw new Error("ENCRYPTION_KEY must be set in production");
+}
+const effectiveKey = envKey ?? "local-dev-only-key-change-me";-if (Buffer.byteLength(envKey) === KEY_LENGTH) {
- secretKey = Buffer.from(envKey);
+if (Buffer.byteLength(effectiveKey) === KEY_LENGTH) {
+ secretKey = Buffer.from(effectiveKey);
} else {
- secretKey = crypto.createHash('sha256').update(String(envKey)).digest();
+ secretKey = crypto.createHash("sha256").update(String(effectiveKey)).digest();
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/encryption.ts` around lines 9 - 13, Replace the hardcoded fallback key
usage: do not set envKey to a constant when process.env.ENCRYPTION_KEY is
missing; instead make initialization of secretKey fail loudly in non-development
environments and only allow a securely generated ephemeral key in dev/test.
Locate the envKey and secretKey initialization (symbols: envKey, secretKey,
ENCRYPTION_KEY) and change the logic to throw an error or exit if ENCRYPTION_KEY
is unset in production, while for local/dev generate a secure random 32-byte key
at runtime (do not hardcode) and ensure the code enforces the 32-byte length
requirement before using the key.
| import { StatusBar } from "@/modules/playground/components/status-bar"; | ||
| import { WelcomeScreen } from "@/modules/playground/components/welcome-screen"; | ||
| import WebContainerPreview from "@/modules/webcontainers/components/webcontainer-preview"; | ||
| import { CodeTimeTravel } from "@/frontend/src/components/CodeTimeTravel"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -i "CodeTimeTravel.tsx"
rg -n --fixed-strings 'CodeTimeTravel' modules/playground/components/editor-area.tsxRepository: piyushdotcomm/Editron
Length of output: 219
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Find all CodeTimeTravel component files
fd -i "CodeTimeTravel\.(t|j)sx?$" . || true
fd -i "CodeTimeTravel" . -g'*.ts' -g'*.tsx' || true
# 2) Check whether the imported target path exists in the filesystem
ls -la frontend/src/components 2>/dev/null || true
ls -la frontend/src/components/CodeTimeTravel* 2>/dev/null || true
ls -la frontend/components 2>/dev/null || true
ls -la frontend/components/CodeTimeTravel* 2>/dev/null || true
# 3) Inspect path alias configuration for "@"
rg -n "paths\\s*:\\s*\\{|\"@\"|\"`@/`|baseUrl\\s*:" -S tsconfig*.json **/tsconfig*.json || true
rg -n "alias\\s*:\\s*\\{|@\\s*:\\s*[" -S **/*vite*.config.* **/*webpack*.config.* **/*next*.config.* **/*.config.* 2>/dev/null || true
# 4) Confirm whether this exact import path is used elsewhere
rg -n --fixed-strings '`@/frontend/src/components/CodeTimeTravel`' . || true
# 5) Show the current import statement in the target file
sed -n '1,60p' modules/playground/components/editor-area.tsxRepository: piyushdotcomm/Editron
Length of output: 2831
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== tsconfig.json paths/baseUrl =="
cat -n tsconfig.json | sed -n '1,120p'
echo "----"
rg -n '"`@/`|@\\*"' tsconfig.json || true
echo "----"
echo "== filesystem checks =="
ls -la frontend/src/components 2>/dev/null || true
ls -la frontend/src/components/CodeTimeTravel* 2>/dev/null || true
ls -la components/CodeTimeTravel* 2>/dev/null || true
echo "== import usage checks =="
rg -n --fixed-strings "`@/frontend/src/components/CodeTimeTravel`" . || true
rg -n --fixed-strings "`@/components/CodeTimeTravel`" . || true
rg -n --fixed-strings "CodeTimeTravel" modules/playground/components/editor-area.tsx components/CodeTimeTravel.tsx || trueRepository: piyushdotcomm/Editron
Length of output: 1919
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the relevant parts around closeFileForPane and activeFileId
rg -n "closeFileForPane|activeFileId" modules/playground/components/editor-area.tsx
echo "---- closeFileForPane context ----"
rg -n "closeFileForPane" -n modules/playground/components/editor-area.tsx -n --context 5
echo "---- activeFileId context ----"
rg -n "activeFileId" modules/playground/components/editor-area.tsx -n --context 8Repository: piyushdotcomm/Editron
Length of output: 5686
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate globalCloseFile/globalSetActiveFileId definitions/usages
rg -n "globalCloseFile|globalSetActiveFileId|closeFile\\s*=|setActiveFileId\\s*=|updateFileContentForPane" modules/playground/components/editor-area.tsx
# Dump the surrounding code blocks for these functions (small, targeted ranges)
# (Use sed with line numbers from rg context)
sed -n '1,240p' modules/playground/components/editor-area.tsx | rg -n "globalCloseFile|globalSetActiveFileId|setEditorPanes|updateFileContentForPane|closeFileForPane|setActiveFileIdForPane"
# Also print the full definitions of updateFileContentForPane and closeFileForPane regions
rg -n "const updateFileContentForPane|const closeFileForPane" modules/playground/components/editor-area.tsx
awk 'NR>=130 && NR<=190 {print}' modules/playground/components/editor-area.tsx
awk 'NR>=190 && NR<=280 {print}' modules/playground/components/editor-area.tsxRepository: piyushdotcomm/Editron
Length of output: 7957
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== frontend folder checks =="
ls -la frontend 2>/dev/null || echo "no ./frontend dir"
ls -la frontend/src 2>/dev/null || true
ls -la frontend/src/components 2>/dev/null || true
ls -la frontend/src/components/CodeTimeTravel* 2>/dev/null || true
echo "== resolve other similar imports =="
rg -n --fixed-strings "`@/components/CodeTimeTravel`" . || true
rg -n --fixed-strings "`@/frontend/src/components/CodeTimeTravel`" . || true
echo "== locate globalCloseFile/globalSetActiveFileId definitions =="
rg -n "globalCloseFile\\b|globalSetActiveFileId\\b" modules/playground -S
rg -n "function\\s+globalCloseFile|const\\s+globalCloseFile|closeFile\\s*:\\s*globalCloseFile" modules/playground -S || true
rg -n "function\\s+globalSetActiveFileId|const\\s+globalSetActiveFileId|setActiveFileId\\s*:\\s*globalSetActiveFileId" modules/playground -S || trueRepository: piyushdotcomm/Editron
Length of output: 849
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Print top of editor-area.tsx to see where global* functions are imported from
sed -n '1,120p' modules/playground/components/editor-area.tsx
# 2) Find the identifier names for the source store/context (globalCloseFile/globalUpdateFileContent/globalSetActiveFileId)
rg -n "globalCloseFile|globalSetActiveFileId|globalUpdateFileContent" modules/playground/components/editor-area.tsx
# 3) Trace imports/definitions for these globals within modules/playground
rg -n "globalCloseFile\\b|globalSetActiveFileId\\b|globalUpdateFileContent\\b" modules/playground -S
# 4) If they're from a store file, inspect that file for behavior on closeFile/openFiles/activeFileId
# We'll try to narrow by grabbing the line where globalCloseFile is defined in any file.
rg -n "globalCloseFile\\b" modules/playground -S --context 5 || true
rg -n "closeFile\\b|setActiveFileId\\b|updateFileContent\\b" modules/playground -S | head -n 80Repository: piyushdotcomm/Editron
Length of output: 10006
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate CodeTimeTravel across repo =="
fd -i "CodeTimeTravel\.(t|j)sx?$" . || true
fd -i "CodeTimeTravel" . -a || true
echo "== check whether ./frontend exists anywhere =="
ls -la frontend 2>/dev/null || echo "no root ./frontend dir"
fd -i "frontend" . -t d -d 3 2>/dev/null || true
echo "== inspect components/CodeTimeTravel.tsx exports =="
sed -n '1,120p' components/CodeTimeTravel.tsx
echo "== inspect useFileExplorer closeFile implementation =="
sed -n '1,260p' modules/playground/hooks/useFileExplorer.tsx
rg -n "closeFile\\s*:\\s*\\(|closeFile\\s*\\(" modules/playground/hooks/useFileExplorer.tsx
sed -n '160,260p' modules/playground/hooks/useFileExplorer.tsx
echo "== inspect usePlaygroundContext/editorPanes reconciliation =="
rg -n "editorPanes|setEditorPanes|activeFileId" modules/playground/contexts/playground-context.tsx modules/playground/hooks/usePlaygroundActions.ts modules/playground/hooks/useFileExplorer.tsx || trueRepository: piyushdotcomm/Editron
Length of output: 18359
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' modules/playground/contexts/playground-context.tsx
echo "----"
rg -n "focusedPane|setFocusedPane|activePaneId|editorPanes|primary|secondary" modules/playground/contexts/playground-context.tsx
echo "----"
rg -n "setFocusedPane\\(|focusedPane" modules/playground -S | head -n 80Repository: piyushdotcomm/Editron
Length of output: 3973
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "setFocusedPane\\b|focusedPane\\b" modules/playground -S
rg -n "activePaneId\\b|setActivePaneId\\b" modules/playground -S
# Also inspect editor-area for usage of setActivePaneId beyond click handler
rg -n "setActivePaneId\\(" modules/playground/components/editor-area.tsx -n --context 5 || true
# Inspect playground-sidebar which uses focusedPane to choose which open file is active
sed -n '1,260p' modules/playground/components/playground-sidebar.tsxRepository: piyushdotcomm/Editron
Length of output: 9398
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== setFocusedPane usage =="
rg -n "setFocusedPane\\b" modules/playground -S || true
echo "== inspect playground-tab-bar for closeFile/setActiveFileId wiring =="
sed -n '1,260p' modules/playground/components/playground-tab-bar.tsxRepository: piyushdotcomm/Editron
Length of output: 10028
Fix broken CodeTimeTravel import path (and split-pane close state mismatch)
modules/playground/components/editor-area.tsximports@/frontend/src/components/CodeTimeTravel, buttsconfig.jsonmaps@/*->./*and./frontend/doesn’t exist; the component lives at./components/CodeTimeTravel.tsx(exportCodeTimeTravel). Update the import to@/components/CodeTimeTravel.closeFileForPaneclears only the initiatingeditorPanesentry, then callsglobalCloseFile(fileId)(noprimary/secondaryargument).useFileExplorer.closeFileusespane ?? focusedPane, so the wrong pane’s file list/active id can be updated in split view; pass the correct pane tocloseFile(or redesign socloseFileForPaneupdates both editor-panes and the matchinguseFileExplorerpane consistently).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/playground/components/editor-area.tsx` at line 18, Update the broken
import and fix the split-pane close-state mismatch: change the CodeTimeTravel
import in editor-area.tsx from "`@/frontend/src/components/CodeTimeTravel`" to
"`@/components/CodeTimeTravel`" (the component is exported as CodeTimeTravel), and
modify the closeFileForPane logic so it passes the correct pane when calling
useFileExplorer.closeFile/globalCloseFile instead of relying on the default
focusedPane; ensure closeFileForPane updates the matching editorPanes entry
(editorPanes[pane]) and calls globalCloseFile/closeFile with the same pane
identifier so the file list and activeId stay in sync for primary/secondary
panes.
| Secrets Manager | ||
| </div> | ||
| <Button size="icon" variant="ghost" className="h-5 w-5" onClick={handleAddVar} title="Add Variable"> | ||
| <Button size="icon" variant="ghost" className="h-5 w-5" onClick={handleAddVar} title="Add Variable" aria-label="Add environment variable"> |
There was a problem hiding this comment.
Resolve leftover env-vars code path causing TSX parse/build failure.
Line 123 calls handleAddVar, and Lines 163-217 still render legacy envVars/handleUpdateVar/handleRemoveVar/isValidKey logic. That mixed migration leaves undefined symbols and broken JSX, so this file won’t compile.
💡 Suggested fix (replace stale env-vars block with secrets rendering)
- <Button size="icon" variant="ghost" className="h-5 w-5" onClick={handleAddVar} title="Add Variable" aria-label="Add environment variable">
+ <Button size="icon" variant="ghost" className="h-5 w-5" type="button" title="Add Secret" aria-label="Add secret">
<Plus className="h-3.5 w-3.5" />
</Button>
@@
- {envVars.map((v, idx) => {
- const keyTrimmed = v.key.trim();
- const isDup = duplicateKeys.has(keyTrimmed);
- const isMalformed = keyTrimmed !== "" && !isValidKey(keyTrimmed);
- const itemHasError = isDup || isMalformed;
-
- return (
- <div
- key={idx}
- className={`flex items-center gap-1.5 border p-1.5 rounded bg-muted/20 ${itemHasError ? "border-destructive/40 bg-destructive/5" : "border-border"
- }`}
- >
- <div className="flex flex-col flex-1 gap-1">
- <Input
- value={v.key}
- onChange={(e) => handleUpdateVar(idx, "key", e.target.value)}
- placeholder="API_KEY"
- className={`h-6 text-[10px] font-mono rounded-sm bg-background shadow-none border ${itemHasError
- ? "border-destructive/60 text-destructive focus-visible:ring-destructive"
- : "border-transparent focus-visible:ring-ring"
- }`}
- />
- {isMalformed && (
- <span className="text-[8px] text-destructive leading-tight px-1 font-sans">
- A-Z, 0-9, _ only, must start with letter/_
- </span>
- )}
- {isDup && (
- <span className="text-[8px] text-destructive leading-tight px-1 font-sans">
- Duplicate key name
- </span>
- )}
- <Input
- value={v.value}
- onChange={(e) => handleUpdateVar(idx, "value", e.target.value)}
- placeholder="Value..."
- type="password"
- className="h-6 text-[10px] font-mono rounded-sm border border-transparent focus-visible:border-input focus-visible:ring-ring bg-background shadow-none"
- />
- </div>
- <Button
- size="icon"
- variant="ghost"
- className="h-8 w-8 text-muted-foreground hover:text-red-500 shrink-0"
- onClick={() => handleRemoveVar(idx)}
- aria-label="Delete environment variable"
- >
- <Trash2 className="h-3.5 w-3.5" />
- </Button>
- </div>
- <div className="font-mono text-[10px] text-muted-foreground truncate bg-background p-1 rounded border">
- {showValues[secret.key] ? secret.value : "••••••••••••••••"}
- </div>
- </div>
- ))}
+ {secrets.map((secret) => (
+ <div key={secret.id ?? secret.key} className="space-y-1">
+ <div className="flex items-center gap-2 border p-2 rounded bg-muted/20">
+ <KeyRound className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
+ <span className="text-[10px] font-mono truncate flex-1">{secret.key}</span>
+ <Button
+ size="icon"
+ variant="ghost"
+ className="h-6 w-6"
+ type="button"
+ onClick={() => toggleShowValue(secret.key)}
+ aria-label={`Toggle value visibility for ${secret.key}`}
+ >
+ {showValues[secret.key] ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
+ </Button>
+ <Button
+ size="icon"
+ variant="ghost"
+ className="h-6 w-6 text-muted-foreground hover:text-red-500"
+ type="button"
+ onClick={() => handleDeleteSecret(secret.key)}
+ aria-label={`Delete secret ${secret.key}`}
+ >
+ <Trash2 className="h-3.5 w-3.5" />
+ </Button>
+ </div>
+ <div className="font-mono text-[10px] text-muted-foreground truncate bg-background p-1 rounded border">
+ {showValues[secret.key] ? secret.value : "••••••••••••••••"}
+ </div>
+ </div>
+ ))}Also applies to: 163-217
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/playground/components/env-manager.tsx` at line 123, The file still
references legacy env-vars symbols (envVars, handleUpdateVar, handleRemoveVar,
isValidKey) in the JSX while the Add button uses handleAddVar, causing undefined
identifiers and a TSX parse error; remove or replace the stale block that
renders envVars (lines around the current env-vars map) and instead render the
new secrets UI using the same patterns as the surrounding component (use
handleAddVar and the secrets state/handlers), or wire the old handlers into the
new secrets data if intended; ensure you eliminate references to envVars,
handleUpdateVar, handleRemoveVar, and isValidKey from the JSX so the component
compiles.
Source: Linters/SAST tools
| feat/split-view-editor | ||
|
|
||
| if (playgroundId) { | ||
| destroyYDoc(playgroundId); | ||
| } |
There was a problem hiding this comment.
Remove merge artifacts and fix missing symbol in cleanup block.
The raw branch labels on Lines 483/495 are invalid syntax, and destroyYDoc is called on Line 486 without an import. This block won’t compile as-is.
Also applies to: 495-495
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/playground/components/playground-editor.tsx` around lines 483 - 487,
Remove the stray merge-artifact token "feat/split-view-editor" from the cleanup
block and ensure the cleanup calls the correct symbol: import destroyYDoc at top
(the module that provides Y doc cleanup) and then call destroyYDoc(playgroundId)
only when playgroundId is truthy; if destroyYDoc lives on a different export
name, update the call to the correct export. Also ensure the cleanup is inside
the effect's return function (the same block where playgroundId is referenced)
so the component compiles.
| feat/split-view-editor | ||
| import { usePlaygroundContext } from "@/modules/playground/contexts/playground-context"; | ||
|
|
||
| type OpenFile = ReturnType<typeof useFileExplorer.getState>["openFiles"][number]; | ||
| import { Button } from "@/components/ui/button"; | ||
| develop |
There was a problem hiding this comment.
Resolve merge-conflict artifacts before merge.
This file still includes unresolved conflict text and duplicated JSX/typing blocks, which makes it unparsable and blocks compilation.
Suggested cleanup direction
- feat/split-view-editor
- develop
+// Keep a single, reconciled PlaygroundTabBar implementation only.
+// Remove all conflict marker remnants and duplicate interface/component blocks.Also applies to: 17-24, 123-245
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/playground/components/playground-tab-bar.tsx` around lines 6 - 10,
The file playground-tab-bar.tsx contains unresolved merge-conflict artifacts and
duplicated imports/JSX (e.g., duplicate import lines for usePlaygroundContext
and Button and duplicated component/typing blocks around the top and in the
123-245 region); open the component (playground-tab-bar.tsx), remove all
conflict markers (<<<<<<, ======, >>>>>>), delete duplicated import/JSX/typing
blocks so there is a single import of usePlaygroundContext and Button and only
one implementation of the PlaygroundTabBar component and its props, ensure the
remaining code compiles and exports correctly, and run the build/tests to verify
the file is parsable.
Source: Linters/SAST tools
|
|
||
| interface CompactProjectTableProps { | ||
| projects: Project[] | ||
| projects: CompactProjectRow[] |
There was a problem hiding this comment.
Define or import CompactProjectRow before using it.
CompactProjectRow is referenced at Line 43, Line 62, Line 66, and Line 72, but there is no declaration/import in this module. This will break type-checking.
💡 Suggested fix
import type { Playground } from "`@prisma/client`"
+type CompactProjectRow = Playground & {
+ Starmark?: Array<{ isMarked: boolean }>
+}Also applies to: 62-62, 66-67, 72-73
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/profile/components/CompactProjectTable.tsx` at line 43, The code
references the type CompactProjectRow in the CompactProjectTable component props
and several places (projects: CompactProjectRow[], map/usage at render) but the
type is not declared or imported; fix by either importing CompactProjectRow from
its source (e.g., export in modules/profile/types or wherever project types
live) or declare a local interface/type with the required fields and export it;
update the top of the file to add the import or type declaration and ensure
every use inside CompactProjectTable (props, map callbacks, and any helper
functions) refers to that same CompactProjectRow type.
| interface StatCardProps { | ||
| icon: LucideIcon; | ||
| label: string; | ||
| value: string | number; | ||
| color: string; | ||
| delay: number; |
There was a problem hiding this comment.
KpiColor and colorStyles are missing declarations.
At Line 38/45/67, these identifiers are used but not defined in this module, which breaks compilation.
💡 Suggested fix
interface KPIStatsProps {
@@
}
+type KpiColor = "blue" | "amber" | "orange";
+
+const colorStyles: Record<KpiColor, { text: string; bg: string; ring: string }> = {
+ blue: {
+ text: "text-blue-500",
+ bg: "bg-blue-500/10",
+ ring: "ring-blue-500/20",
+ },
+ amber: {
+ text: "text-amber-500",
+ bg: "bg-amber-500/10",
+ ring: "ring-amber-500/20",
+ },
+ orange: {
+ text: "text-orange-500",
+ bg: "bg-orange-500/10",
+ ring: "ring-orange-500/20",
+ },
+};
+
interface StatCardProps {
icon: LucideIcon;
label: string;
value: string | number;
- color: string;
+ color: KpiColor;
delay: number;
}Also applies to: 38-38, 45-45, 63-68
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/profile/components/KPIStats.tsx` around lines 20 - 25, The file uses
undefined identifiers KpiColor and colorStyles in the StatCard component and
related code (e.g., StatCardProps, places at lines referenced) which breaks
compilation; fix by either importing them from the module that defines them or
by adding local declarations: declare a KpiColor union/enum type listing the
allowed color keys used by StatCardProps and create a colorStyles object that
maps those keys to the expected style strings/classes used by the component;
update StatCardProps.color to use KpiColor and ensure all usages of
colorStyles[...] reference valid keys so the component compiles.
| feat/runtime-activity-panel | ||
| <RefreshCw | ||
| size={13} | ||
| className={ | ||
| !isSetupComplete ? "animate-spin text-primary" : "" | ||
| } | ||
| /> | ||
|
|
||
| <RefreshCw | ||
| size={13} | ||
| className={ | ||
| !isSetupComplete ? "animate-spin text-primary" : "" | ||
| isLoading ? "animate-spin text-primary" : "" | ||
| } | ||
| /> | ||
| develop | ||
| </Button> |
There was a problem hiding this comment.
Unresolved merge-conflict artifacts break TSX parsing.
Line 998 and Line 1012 include branch-marker text in JSX, and this block also renders duplicate refresh icons. This is a compile-time blocker.
Suggested fix
- feat/runtime-activity-panel
- <RefreshCw
- size={13}
- className={
- !isSetupComplete ? "animate-spin text-primary" : ""
- }
- />
-
- <RefreshCw
- size={13}
- className={
- isLoading ? "animate-spin text-primary" : ""
- }
- />
- develop
+ <RefreshCw
+ size={13}
+ className={isLoading ? "animate-spin text-primary" : ""}
+ />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/webcontainers/components/webcontainer-preview.tsx` around lines 998 -
1013, Remove the merge markers and duplicate icon render: delete the stray
branch text ("feat/runtime-activity-panel", "develop") and the extra RefreshCw
element, and render a single RefreshCw inside the Button that uses a combined
condition for its className (referencing RefreshCw, Button, isSetupComplete,
isLoading) e.g. set className to the spinner style when either !isSetupComplete
or isLoading is true so only one icon is rendered and TSX parses correctly.
Addresses CodeRabbit review comments on #481.
Changes:
The escapeHtml function already handles XSS protection by escaping HTML entities before highlighting.
Summary by CodeRabbit
New Features
Improvements