Skip to content

Conversation

@rossmanko
Copy link
Contributor

@rossmanko rossmanko commented Oct 1, 2025

Summary by CodeRabbit

  • New Features

    • Dedicated file download action with Download button/icon and accessible labels.
    • Terminal file sharing UI with clear status (preparing, sharing, shared, failed) and file counts.
    • Upload progress/status banner shown in the chat message list.
    • New data controls UI: delete-all-chats and delete-terminal-sandbox actions plus a Settings tab entry.
  • Bug Fixes

    • Improved reliability and explicit error handling for downloads/uploads and sandbox file transfers.
    • Deletions now idempotent when referenced items are missing.
  • Documentation

    • Clarified guidance for sharing generated files and noted attachments path in the sandbox.

- Add missing serviceKey parameter in getMemoryById()
- Make deleteMemoryForBackend() and deleteUserMemory() idempotent
- Handle non-existent memories gracefully during deletion
- Prevents duplicate delete request errors
@vercel
Copy link

vercel bot commented Oct 1, 2025

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

Project Deployment Preview Comments Updated (UTC)
hackerai Ready Ready Preview Comment Oct 1, 2025 8:42pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 1, 2025

Walkthrough

Adds client-side file download handling and UI updates, registers a GetTerminalFilesHandler component and message branch, refactors sandbox uploader to fetch blobs via sandbox.downloadUrl with stricter error checks and token metadata, surfaces upload-status notifications in chat/messages, and updates system prompt with /home/user/upload guidance.

Changes

Cohort / File(s) Summary
UI — file download & memoization
app/components/FilePartRenderer.tsx
Replaced in-tab open with a fetch->Blob download flow (handleDownload), swapped Eye icon for Download, updated aria-labels to "Download …", added useCallback for handler and updated memoization dependency arrays.
UI — terminal files tool & message wiring
app/components/MessagePartHandler.tsx, app/components/tools/GetTerminalFilesHandler.tsx
Added tool-get_terminal_files branch in message rendering and new GetTerminalFilesHandler component rendering ToolBlock for states (input-streaming, input-available, output-available, output-error) with FileDown icon.
UI — upload status propagation
app/components/Messages.tsx, app/components/chat.tsx
Added uploadStatus prop to Messages and local uploadStatus state in Chat; onData now handles data-upload-status events and Messages renders a ShimmerText upload banner when uploading.
API — upload notifications
app/api/chat/route.ts
Emits two transient data-upload-status notifications (isUploading: true/false) immediately before and after uploadSandboxFiles, with a try/finally to ensure completion notification.
Sandbox uploader refactor
lib/ai/tools/utils/sandbox-file-uploader.ts
Removed internal toBlob; now calls sandbox.downloadUrl(path), fetches that URL to obtain a Blob, validates download and upload responses, uploads the Blob, and added tokens: number to UploadedFileInfo.
Tool descriptions / guidance
lib/ai/tools/get-terminal-files.ts, lib/ai/tools/run-terminal-cmd.ts
Expanded and reworded tool descriptions to clarify sharing/upload/download expectations and examples; no signature changes.
System prompt
lib/system-prompt.ts
Added note: user attachments are available at /home/user/upload; instruct to ask user to re-upload if a file is missing.
Convex memories idempotence & update handling
convex/memories.ts, lib/ai/tools/update-memory.ts, lib/db/actions.ts
Made deletion idempotent (return null when missing), adjusted update-memory to tolerate missing memory (try/catch, fallback text), and included serviceKey in getMemoryById payload.
New API route & data controls UI
app/api/delete-sandboxes/route.ts, app/components/DataControlsTab.tsx, app/components/SettingsDialog.tsx, app/components/AccountTab.tsx
Added POST /api/delete-sandboxes route with auth & subscription checks, new DataControlsTab component (delete chats, delete terminal sandbox), wired Data Controls tab into SettingsDialog; removed Delete All Chats UI from AccountTab.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as FilePartRenderer
  participant Net as fetch(url)
  participant Browser as Browser
  User->>UI: Click "Download"
  UI->>Net: GET file URL
  alt 200 OK
    Net-->>UI: Blob
    UI->>UI: Create blob URL
    UI->>Browser: Trigger download (anchor.click)
    UI->>UI: Revoke blob URL
  else Error
    Net-->>UI: Non-OK / throw
    UI->>Browser: window.open(url, "_blank")
  end
Loading
sequenceDiagram
  autonumber
  participant Msg as MessagePartHandler
  participant Comp as GetTerminalFilesHandler
  participant UI as ToolBlock
  Note over Msg: part.type === "tool-get_terminal_files"
  Msg->>Comp: Render({ part, status })
  alt input-streaming
    Comp->>UI: Show "Preparing…" (shimmer)
  else input-available
    Comp->>UI: Show "Sharing…" with file list
  else output-available
    Comp->>UI: Show "Shared" with file list/count
  else output-error
    Comp->>UI: Show "Failed to share"
  end
Loading
sequenceDiagram
  autonumber
  participant SFU as sandbox-file-uploader
  participant SB as sandbox
  participant Net as fetch
  participant Up as Upload API
  SFU->>SB: downloadUrl(path)
  SB-->>SFU: URL
  SFU->>Net: GET URL
  alt 200 OK
    Net-->>SFU: Blob
    SFU->>Up: POST Blob
    alt Upload OK
      Up-->>SFU: { id, tokens, ... }
      SFU->>SFU: Save metadata
    else Upload Error
      Up-->>SFU: Non-OK
      SFU->>SFU: Throw upload error
    end
  else Download Error
    Net-->>SFU: Non-OK
    SFU->>SFU: Throw download error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

I nibble at bytes and humbly share,
A blob becomes a carrot, light as air.
Fetch, click, and a download hop — hooray!
Sandboxed burrows send files my way.
/home/user/upload — tucked away 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title “Daily branch 2025 10 01” consists solely of the branch name and date and does not convey any of the substantive updates introduced in this PR, such as adding a client-side file download workflow, the GetTerminalFilesHandler component, or the new data-control features. As a result, it fails to summarize the primary purpose or scope of the changeset and offers no meaningful context for reviewers or future maintainers. Please rename the PR to explicitly state its main functionality, for example “Add client-side file download flow and GetTerminalFilesHandler component.” A descriptive title will help reviewers and maintainers quickly grasp the intent of the changes, and if this PR encompasses multiple distinct features, consider splitting it into focused PRs each with a clear, specific title.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch daily-branch-2025-10-01

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
app/components/tools/GetTerminalFilesHandler.tsx (1)

22-24: Add explicit return type annotation.

Per coding guidelines, functions should have explicit type annotations for better type safety.

-  const getFileNames = (paths: string[]) => {
+  const getFileNames = (paths: string[]): string => {
     return paths.map((path) => path.split("/").pop() || path).join(", ");
   };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8e92e7 and f635626.

📒 Files selected for processing (7)
  • app/components/FilePartRenderer.tsx (6 hunks)
  • app/components/MessagePartHandler.tsx (2 hunks)
  • app/components/tools/GetTerminalFilesHandler.tsx (1 hunks)
  • lib/ai/tools/get-terminal-files.ts (1 hunks)
  • lib/ai/tools/run-terminal-cmd.ts (1 hunks)
  • lib/ai/tools/utils/sandbox-file-uploader.ts (1 hunks)
  • lib/system-prompt.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)

**/*.{ts,tsx}: Use Id helper type from ./_generated/dataModel to type document IDs (e.g., Id<'users'>) instead of string
When defining Record types, specify key and value types matching validators (e.g., Record<Id<'users'>, string>)
Be strict with types for document IDs; prefer Id<'table'> over string in function args and variables
Use as const for string literals in discriminated unions
Declare arrays with explicit generic type: const arr: Array = [...]
Declare records with explicit generic types: const record: Record<KeyType, ValueType> = {...}

Files:

  • lib/ai/tools/get-terminal-files.ts
  • lib/ai/tools/utils/sandbox-file-uploader.ts
  • app/components/MessagePartHandler.tsx
  • app/components/tools/GetTerminalFilesHandler.tsx
  • lib/ai/tools/run-terminal-cmd.ts
  • lib/system-prompt.ts
  • app/components/FilePartRenderer.tsx
🧠 Learnings (1)
📚 Learning: 2025-08-29T13:33:09.937Z
Learnt from: CR
PR: hackerai-tech/hackerai#0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-08-29T13:33:09.937Z
Learning: Applies to convex/**/*.ts : Use ctx.storage.getUrl(fileId) to obtain signed URLs; it returns null if the file does not exist

Applied to files:

  • lib/ai/tools/utils/sandbox-file-uploader.ts
🧬 Code graph analysis (2)
app/components/MessagePartHandler.tsx (1)
app/components/tools/GetTerminalFilesHandler.tsx (1)
  • GetTerminalFilesHandler (11-74)
app/components/tools/GetTerminalFilesHandler.tsx (1)
types/chat.ts (1)
  • ChatStatus (60-60)
🪛 Biome (2.1.2)
app/components/tools/GetTerminalFilesHandler.tsx

[error] 49-49: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 50-50: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (15)
lib/ai/tools/run-terminal-cmd.ts (1)

30-30: LGTM! Clear guidance update.

The updated text better reflects the download-centric workflow introduced in this PR and provides clearer guidance to the AI on when and how to use the terminal files tool.

lib/system-prompt.ts (1)

140-140: LGTM! Helpful operational context.

This addition provides clear guidance to the AI about where user attachments are located and what to do if files are missing. The instruction aligns well with the broader attachment/download workflow introduced in this PR.

lib/ai/tools/get-terminal-files.ts (1)

10-17: LGTM! Enhanced tool description.

The expanded description provides much clearer guidance with concrete examples of file paths and usage scenarios. This will help the AI understand when and how to use the terminal files tool effectively.

lib/ai/tools/utils/sandbox-file-uploader.ts (3)

45-56: LGTM! Improved file retrieval with proper error handling.

The new approach using sandbox.downloadUrl followed by fetch provides better separation of concerns and includes appropriate error handling with descriptive error messages. The 30-second expiration is a reasonable security measure for temporary download URLs.


69-73: LGTM! Explicit upload error handling.

Good addition of explicit error handling for upload failures with descriptive error messages including status codes.


13-13: Ensure api.fileActions.saveFile returns the new tokens field and that every consumer of UploadedFileInfo handles it correctly.

app/components/FilePartRenderer.tsx (6)

2-2: LGTM! Appropriate import addition.

Adding useCallback is correct for the new handleDownload function to ensure stable function identity.


4-4: LGTM! Icon change aligns with download action.

Replacing Eye with Download accurately reflects the new download behavior instead of the previous open-in-view action.


18-36: Good implementation with appropriate fallback.

The client-side download flow is well-implemented:

  • Fetches the file and creates a blob
  • Uses programmatic download with proper cleanup
  • Falls back to opening in new tab on error
  • Logs errors for debugging

The approach works across modern browsers and handles edge cases appropriately.


68-81: LGTM! UI changes align with download semantics.

The changes correctly reflect the download action:

  • Download icon replaces the previous Eye icon
  • onClick handler invokes handleDownload instead of opening URL
  • aria-label updated to "Download {fileName}" for accessibility

99-99: LGTM! Correct memoization dependency.

Adding handleDownload to the FilePreviewCard memoization dependencies ensures the component updates when the handler changes.


201-201: LGTM! Proper dependency management.

Adding FilePreviewCard to the dependency array ensures correct memoization behavior when the memoized component changes.

app/components/MessagePartHandler.tsx (2)

8-8: LGTM!

The import follows the existing pattern and properly brings in the new GetTerminalFilesHandler component.


79-80: LGTM!

The new case handler follows the established pattern for tool rendering and correctly wires GetTerminalFilesHandler into the message part switch.

app/components/tools/GetTerminalFilesHandler.tsx (1)

26-73: Verify exhaustive handling of state. Confirm that the four cases (“input-streaming”, “input-available”, “output-available”, “output-error”) cover all possible values of part.state, or explicitly handle or document the intended fallback for any other states.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f635626 and 7b7ff9a.

📒 Files selected for processing (6)
  • app/api/chat/route.ts (1 hunks)
  • app/components/Messages.tsx (4 hunks)
  • app/components/chat.tsx (3 hunks)
  • convex/memories.ts (4 hunks)
  • lib/ai/tools/update-memory.ts (1 hunks)
  • lib/db/actions.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
convex/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)

convex/**/*.ts: Always use the new Convex function syntax (query/mutation/action objects with args/returns/handler) when defining Convex functions
When a function returns null, include returns: v.null() and return null explicitly
Use internalQuery/internalMutation/internalAction for private functions callable only by other Convex functions; do not expose sensitive logic via public query/mutation/action
Use query/mutation/action only for public API functions
Do not try to register functions via the api or internal objects
Always include argument and return validators for all Convex functions (query/internalQuery/mutation/internalMutation/action/internalAction)
In JS implementations, functions without an explicit return value implicitly return null
Use ctx.runQuery from queries/mutations/actions to call a query
Use ctx.runMutation from mutations/actions to call a mutation
Use ctx.runAction from actions to call an action
Only call an action from another action when crossing runtimes (e.g., V8 to Node); otherwise extract shared helper code
Minimize calls from actions to queries/mutations to avoid race conditions from splitting transactions
Pass FunctionReference values (from api/internal) to ctx.runQuery/ctx.runMutation/ctx.runAction; do not pass function implementations
When calling a function in the same file via ctx.run*, add an explicit return type annotation at the call site to avoid TS circularity
Use the generated api object for public functions and internal object for internal functions from convex/_generated/api.ts
Respect file-based routing for function references: e.g., convex/example.ts export f -> api.example.f; nested paths map to dot-separated namespaces
For paginated queries use paginationOptsValidator in args and .paginate(args.paginationOpts) on a query
v.bigint() is deprecated; use v.int64() for signed 64-bit integers
Use v.record(keys, values) for record-like data; v.map() and v.set() are not supported
For full-text search, use withSearchIndex("ind...

Files:

  • convex/memories.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)

**/*.{ts,tsx}: Use Id helper type from ./_generated/dataModel to type document IDs (e.g., Id<'users'>) instead of string
When defining Record types, specify key and value types matching validators (e.g., Record<Id<'users'>, string>)
Be strict with types for document IDs; prefer Id<'table'> over string in function args and variables
Use as const for string literals in discriminated unions
Declare arrays with explicit generic type: const arr: Array = [...]
Declare records with explicit generic types: const record: Record<KeyType, ValueType> = {...}

Files:

  • convex/memories.ts
  • app/components/chat.tsx
  • app/api/chat/route.ts
  • lib/db/actions.ts
  • lib/ai/tools/update-memory.ts
  • app/components/Messages.tsx
🧬 Code graph analysis (3)
app/api/chat/route.ts (1)
lib/utils/sandbox-file-utils.ts (1)
  • uploadSandboxFiles (54-70)
lib/ai/tools/update-memory.ts (1)
lib/db/actions.ts (1)
  • getMemoryById (284-297)
app/components/Messages.tsx (1)
app/components/ShimmerText.tsx (1)
  • ShimmerText (12-39)
🔇 Additional comments (6)
lib/db/actions.ts (1)

287-287: LGTM! Consistent service key authentication.

This change aligns getMemoryById with other backend calls in the file that already include serviceKey for proper authentication.

convex/memories.ts (2)

196-236: LGTM! Proper idempotent deletion.

Making the deletion operation idempotent is a solid improvement that aligns with REST semantics. The ownership verification at line 223 is still enforced when the memory exists, maintaining security.


307-348: LGTM! Consistent idempotent behavior.

The changes mirror the improvements in deleteMemoryForBackend, ensuring both frontend and backend deletion paths are idempotent while preserving ownership checks.

lib/ai/tools/update-memory.ts (1)

139-167: LGTM! Graceful handling of missing memory.

The try/catch around getMemoryById (lines 149-156) properly handles the case where memory doesn't exist, allowing the delete operation to proceed idempotently. The fallback message at line 165 ensures a meaningful response even when the memory is missing.

app/components/Messages.tsx (1)

404-411: LGTM! Clean upload status UI implementation.

The upload status banner is appropriately positioned and uses ShimmerText for a polished loading effect. The conditional rendering ensures it only appears when actively uploading.

app/components/chat.tsx (1)

170-178: All data-upload-status emissions match { message: string; isUploading: boolean }, so the payload assertion is safe.

…nagement

- Create new DataControlsTab component with database icon
- Add delete all chats functionality (moved from Account tab)
- Add delete terminal sandbox feature for pro/ultra users
- Create /api/delete-sandboxes endpoint with subscription validation
- Update SettingsDialog to include new Data controls tab
- Clean up AccountTab by removing delete chats section
@rossmanko rossmanko merged commit 16dde60 into main Oct 1, 2025
2 of 3 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
app/components/chat.tsx (2)

84-103: Clear uploadStatus when chat transitions occur.

The effect handles chat ID transitions but doesn't clear uploadStatus when switching between chats via route changes. Lines 98-101 set a new chat ID without clearing the upload banner, which could leave stale upload status visible.

Apply this diff to clear uploadStatus during route-based chat transitions:

   // If a chat id is present in the route, treat as existing chat
   if (routeChatId) {
     setChatId(routeChatId);
     setIsExistingChat(true);
+    setUploadStatus(null);
     return;
   }

231-243: Ensure reset runs on all chat initializations
Reset is only invoked via initializeNewChat (GlobalState.tsx:323–326), which your UI calls only on “New Chat” clicks (ChatHeader, SidebarHeader, ChatItem) and not when switching to an existing chat via initializeChat or activateChat. Invoke the reset function in those flows as well to clear uploadStatus and other local state.

🧹 Nitpick comments (2)
app/api/delete-sandboxes/route.ts (1)

11-16: Remove redundant authorization check.

The getUserIDAndPro helper (lib/auth/get-user-id.ts) throws a ChatSDKError("unauthorized:auth") if the session or user ID is missing. If it returns successfully, userId will always be truthy. This manual check is unreachable dead code.

Apply this diff to remove the redundant check:

     const { userId, subscription } = await getUserIDAndPro(req);
 
-    if (!userId) {
-      return new Response(JSON.stringify({ error: "Unauthorized" }), {
-        status: 401,
-        headers: { "Content-Type": "application/json" },
-      });
-    }
-
     // Only allow subscribed users to delete sandboxes
app/components/DataControlsTab.tsx (1)

29-41: Add user feedback for deletion errors.

When chat deletion fails, the user receives no feedback before being redirected to /. Consider showing an error toast and only redirecting on success.

Apply this diff to provide proper error feedback:

   const handleDeleteAllChats = async () => {
     if (isDeletingChats) return;
     setIsDeletingChats(true);
     try {
       await deleteAllChats();
+      setShowDeleteChats(false);
+      window.location.href = "/";
     } catch (error) {
       console.error("Failed to delete all chats:", error);
+      toast.error("Failed to delete chats");
     } finally {
-      setShowDeleteChats(false);
-      window.location.href = "/";
       setIsDeletingChats(false);
     }
   };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e96eeee and 7167c99.

📒 Files selected for processing (7)
  • app/api/chat/route.ts (1 hunks)
  • app/api/delete-sandboxes/route.ts (1 hunks)
  • app/components/AccountTab.tsx (0 hunks)
  • app/components/DataControlsTab.tsx (1 hunks)
  • app/components/SettingsDialog.tsx (3 hunks)
  • app/components/chat.tsx (5 hunks)
  • app/components/tools/GetTerminalFilesHandler.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • app/components/AccountTab.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/components/tools/GetTerminalFilesHandler.tsx
  • app/api/chat/route.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/convex_rules.mdc)

**/*.{ts,tsx}: Use Id helper type from ./_generated/dataModel to type document IDs (e.g., Id<'users'>) instead of string
When defining Record types, specify key and value types matching validators (e.g., Record<Id<'users'>, string>)
Be strict with types for document IDs; prefer Id<'table'> over string in function args and variables
Use as const for string literals in discriminated unions
Declare arrays with explicit generic type: const arr: Array = [...]
Declare records with explicit generic types: const record: Record<KeyType, ValueType> = {...}

Files:

  • app/components/DataControlsTab.tsx
  • app/components/SettingsDialog.tsx
  • app/components/chat.tsx
  • app/api/delete-sandboxes/route.ts
🧬 Code graph analysis (2)
app/components/DataControlsTab.tsx (2)
app/contexts/GlobalState.tsx (1)
  • useGlobalState (439-445)
convex/chats.ts (1)
  • deleteAllChats (456-524)
app/api/delete-sandboxes/route.ts (1)
lib/auth/get-user-id.ts (1)
  • getUserIDAndPro (41-88)
🔇 Additional comments (16)
app/components/chat.tsx (4)

43-46: LGTM! Clean type definition for upload status.

The discriminated union pattern with null for idle state is clear and type-safe.


170-176: LGTM! Upload status handler logic is correct.

The handler correctly updates uploadStatus from the data stream and clears it when isUploading is false.


215-215: LGTM! Upload status cleared on error.

Correctly addresses the past review comment by clearing uploadStatus in the error handler.


468-468: LGTM! Upload status correctly propagated to Messages.

The uploadStatus prop is correctly passed to the Messages component.

app/components/SettingsDialog.tsx (3)

4-4: LGTM!

The new imports for Database icon and DataControlsTab component are correctly added and align with their usage in the tab list and content rendering.

Also applies to: 11-11


28-28: LGTM!

The new "Data controls" tab entry follows the existing pattern and is correctly structured with matching id, label, and icon.


130-131: LGTM!

The conditional rendering of DataControlsTab follows the existing pattern used for other tabs.

app/api/delete-sandboxes/route.ts (4)

1-5: LGTM!

The imports are correct and maxDuration = 60 is appropriate for an operation that may need to kill multiple sandboxes.


18-24: LGTM!

The subscription check correctly restricts sandbox deletion to paid users (pro/ultra) and returns a clear 403 response for free users.


51-60: LGTM!

The error handling correctly logs the error for debugging and returns a generic 500 response to the client without leaking internal details.


26-45: Verify pagination: ensure all sandboxes are deleted. The code calls await paginator.nextItems() only once, so only the first page of sandboxes will be killed. Loop await paginator.nextItems() until it returns no items to delete all pages.

app/components/DataControlsTab.tsx (5)

1-27: LGTM!

The imports and component setup are correct. State management follows React patterns with clear separation between chat deletion and sandbox deletion states.


43-64: LGTM!

The sandbox deletion handler has proper error handling with user feedback via toasts and correctly manages loading state. The error message extraction from the response is well implemented.


66-83: LGTM!

The delete chats UI section is clear, accessible, and uses appropriate destructive styling.


85-105: LGTM!

The delete sandbox UI is correctly gated for paid users only and provides clear descriptive text explaining the action's impact.


107-163: LGTM!

Both confirmation dialogs follow consistent patterns with clear warnings, proper loading states, and accessible button labels.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants