feat(website): add cancel functionality for pack requests#864
feat(website): add cancel functionality for pack requests#864
Conversation
- Add onAbort callback to distinguish timeouts from errors - Implement errorType system to differentiate warnings from errors - Update TryItResultErrorContent to show warning styling for timeouts - Change timeout message to suggest using Include/Ignore Patterns - Timeout now displays with yellow warning icon instead of red error
Implement cancel button for pack requests with improved user experience. The button now shows "Cancel" on hover during processing and allows users to abort ongoing requests. Also improved error messages for better clarity. - Add cancel functionality to PackButton with hover state - Implement request cancellation using AbortController - Improve error messages for remote repository processing failures - Update timeout handling and server configuration - Add proper event handling to prevent form submission conflicts
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughImplements client-side cancelation for pack requests, propagates cancel events through UI components, adds abort handling and error typing (error vs warning) in composable and request utilities, updates server timeouts and messages, and adjusts Cloud Run CPU and deploy timeout. No structural server logic changes; primarily UI/composable wiring and error/abort messaging. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant PB as PackButton.vue
participant T as TryIt.vue
participant PR as usePackRequest (composable)
participant RH as requestHandlers.ts
participant API as Server API
U->>PB: Click "Pack"
PB-->>T: @submit (when not loading)
T->>PR: submit()
PR->>RH: handlePackRequest({ onAbort, signal })
RH->>API: POST /pack (fetch with AbortSignal)
alt Success
API-->>RH: 200 OK
RH-->>PR: resolve
PR-->>T: success state
else Error
API-->>RH: 4xx/5xx or throw
RH-->>PR: onError(errorMessage)
PR-->>T: error (errorType='error')
end
U-->>PB: Click while loading
PB-->>T: @cancel
T->>PR: cancelRequest() (AbortController.abort('cancel'))
RH-->>PR: onAbort("Request was canceled")
PR-->>T: error (errorType='warning')
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @yamadashy, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant user experience improvement by allowing users to cancel ongoing pack requests directly from the website. It refines the client-side interaction with dynamic button states and robust request cancellation logic, while also bolstering the server's capacity and providing more informative error feedback to the user. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Pull Request Overview
This PR implements cancel functionality for pack requests to improve user experience on the website. Users can now cancel ongoing requests by hovering over the pack button during processing, which changes to show "Cancel" with a red background.
- Cancel Button: Pack button shows "Cancel" on hover during processing with red styling
- Request Handling: Added AbortController integration with proper error handling for timeouts and cancellations
- UI Improvements: Enhanced error messages and visual feedback for different error types
Reviewed Changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| website/client/components/Home/PackButton.vue | Implements dual-text display and cancel functionality with hover states |
| website/client/composables/usePackRequest.ts | Adds abort handling and error type differentiation |
| website/client/components/utils/requestHandlers.ts | Enhanced abort/timeout detection and error categorization |
| website/client/components/Home/TryItResultErrorContent.vue | Added warning state styling and error type support |
| website/server/src/domains/pack/remoteRepo.ts | Improved error messages for remote repository processing |
| website/server/src/index.ts | Increased API timeout from 30s to 35s |
| website/server/cloudbuild.yaml | Updated server resources and timeout configuration |
There was a problem hiding this comment.
Code Review
This pull request effectively implements the cancellation functionality for pack requests, which is a great enhancement to the user experience. The use of AbortController is well-executed, and the updated error handling and UI feedback for cancellation and timeouts are clear and user-friendly. The related server-side configuration changes for timeouts and resources are also sensible. I have a few suggestions to further improve the code quality and maintainability, mainly regarding stricter type safety in a Vue component and a more robust check for cancellation reasons. Overall, this is a solid contribution.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #864 +/- ##
=======================================
Coverage 88.85% 88.85%
=======================================
Files 109 109
Lines 7575 7575
Branches 1423 1423
=======================================
Hits 6731 6731
Misses 844 844 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Deploying repomix with
|
| Latest commit: |
f1d8f97
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://48db5503.repomix.pages.dev |
| Branch Preview URL: | https://fix-website-timeout.repomix.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (9)
website/server/src/domains/pack/remoteRepo.ts (2)
106-106: Log with context (and prefer shared logger for consistency).Include repoUrl/format in the error log (and consider switching to the shared logger to keep structured logs consistent with the rest of the codebase).
Apply this diff to enrich the log:
- console.error('Error in remote repository processing:', error); + console.error('Error in remote repository processing:', { repository: repoUrl, format }, error);
108-116: Consider mapping status codes by failure cause instead of always 500.Timeouts, auth, and not-found can return 504/403/404 for clearer UX and better client retry behavior.
Example helper (place in a shared util and use here):
function mapRemoteErrorToStatus(e: unknown): number { const err = e as { code?: string; message?: string; status?: number }; if (typeof err?.status === 'number') return err.status; if (err?.code === 'ETIMEDOUT') return 504; if (err?.code === 'ECONNREFUSED' || err?.code === 'ENOTFOUND') return 502; if (/\b(unauthorized|forbidden|permission)\b/i.test(err?.message ?? '')) return 403; if (/\b(not\s*found|404)\b/i.test(err?.message ?? '')) return 404; return 500; }Then:
const status = mapRemoteErrorToStatus(error); throw new AppError('Remote repository processing failed.\nThe repository may not be public or there may be an issue with Repomix.', status);website/server/cloudbuild.yaml (2)
41-41: Parameterize CPU to avoid hardcoding across environments.Swap to a substitution and set it in
substitutions:.- - '2' + - '$_CPU'Add (or update) in
substitutions::substitutions: _REGION: us-central1 _SERVICE_NAME: repomix-server-us _CPU: '2'Operational: Confirm you actually benefit from 2 vCPU (e.g., worker threads/subprocesses). Otherwise this may increase cost without throughput gains.
47-47: Single source of truth for request timeout; align with app-level and keep cushion.Parameterize timeout and ensure the app-level timeout is slightly lower (e.g., 34s) than Cloud Run (e.g., 35s).
- - '35s' + - '$_REQUEST_TIMEOUT'Add (or update) in
substitutions::substitutions: _REGION: us-central1 _SERVICE_NAME: repomix-server-us _REQUEST_TIMEOUT: '35s'website/server/src/middlewares/rateLimit.ts (1)
11-16: IncludeRetry-Afterheader and fix singular/plural while keeping multiline message.Expose standard 429 metadata and improve copy polish.
if (!rateLimiter.isAllowed(clientInfo.ip)) { const remainingTime = Math.ceil(rateLimiter.getRemainingTime(clientInfo.ip) / 1000); - const message = `Rate limit exceeded.\nPlease try again in ${remainingTime} seconds.`; + const unit = remainingTime === 1 ? 'second' : 'seconds'; + const message = `Rate limit exceeded.\nPlease try again in ${remainingTime} ${unit}.`; const requestId = c.get('requestId'); - return c.json(createErrorResponse(message, requestId), 429); + c.header('Retry-After', String(remainingTime)); + return c.json(createErrorResponse(message, requestId), 429); }website/client/composables/usePackRequest.ts (1)
24-25: Abort semantics and errorType handling — solid; consider small tweaks.
- Good:
errorTypestate with reset and abort-path downgrade to warning.- Good: Timeout via AbortController, cancel reason strings wired end-to-end.
- Nice-to-have: Make
TIMEOUT_MSconfigurable (env or prop) to align with server timeouts and future tuning.Example tweak:
-const TIMEOUT_MS = 30_000; +const TIMEOUT_MS = Number(import.meta.env?.VITE_PACK_TIMEOUT_MS ?? 30_000);Ensure PackButton only emits
cancelwhileloadingto avoid callingcancelRequest()when no controller is active.Also applies to: 56-56, 72-72, 76-82, 95-99, 143-147
website/client/components/Home/TryItResultErrorContent.vue (1)
29-34: Add ARIA for better announcement of errors/warnings.Make the container an alert so screen readers announce status changes.
- <div :class="errorType === 'warning' ? 'warning' : 'error'"> + <div :class="errorType === 'warning' ? 'warning' : 'error'" role="alert" aria-live="polite">website/client/components/Home/PackButton.vue (2)
4-9: Harden cancel vs submit: bind button type to stateGreat UX wiring. To further prevent accidental form submits while loading, bind type dynamically.
- type="submit" + :type="loading ? 'button' : 'submit'"
63-65: Add keyboard accessibility: mirror hover behavior on focus-visibleHover-only label/background changes aren’t announced for keyboard users. Mirror the hover state when the button is focused.
.pack-button--loading:hover { background: var(--vp-c-danger-1); } + +/* Keyboard users should get the same visual affordance as hover */ +.pack-button--loading:focus-visible { + background: var(--vp-c-danger-1); +} .pack-button--loading:hover .pack-button__text--normal { opacity: 0; } .pack-button--loading:hover .pack-button__text--hover { opacity: 1; } + +/* Also swap texts on keyboard focus */ +.pack-button--loading:focus-visible .pack-button__text--normal { + opacity: 0; +} +.pack-button--loading:focus-visible .pack-button__text--hover { + opacity: 1; +}Also applies to: 72-89
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
website/client/components/Home/PackButton.vue(2 hunks)website/client/components/Home/TryIt.vue(6 hunks)website/client/components/Home/TryItFileUpload.vue(2 hunks)website/client/components/Home/TryItFolderUpload.vue(2 hunks)website/client/components/Home/TryItResult.vue(2 hunks)website/client/components/Home/TryItResultErrorContent.vue(4 hunks)website/client/components/Home/TryItUrlInput.vue(2 hunks)website/client/components/utils/requestHandlers.ts(3 hunks)website/client/composables/usePackRequest.ts(6 hunks)website/server/cloudbuild.yaml(1 hunks)website/server/src/domains/pack/remoteRepo.ts(1 hunks)website/server/src/index.ts(1 hunks)website/server/src/middlewares/rateLimit.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
website/client/components/utils/requestHandlers.ts (1)
website/client/components/utils/analytics.ts (1)
analyticsUtils(63-129)
website/server/src/domains/pack/remoteRepo.ts (2)
src/shared/logger.ts (1)
error(34-38)website/server/src/utils/errorHandler.ts (1)
AppError(3-11)
🔇 Additional comments (16)
website/server/src/domains/pack/remoteRepo.ts (1)
108-111: Do not expose raw error.message to clientsLog the original error server-side and throw a generic AppError to avoid leaking internals.
File: website/server/src/domains/pack/remoteRepo.ts (≈lines 108–111)
- throw new AppError( - `Remote repository processing failed.\nThe repository may not be public or there may be an issue with Repomix.\n\n${error.message}`, - 500, - ); + throw new AppError( + 'Remote repository processing failed.\nThe repository may not be public or there may be an issue with Repomix.', + 500, + );Repo search returned no matches for error handlers or AppError usage — verify locally that your error-handling middleware does not serialize AppError.message to HTTP responses.
website/server/src/index.ts (1)
34-34: Timeout should cover/api/*, avoid the magic number, and return a structured JSON on timeout.
- Use wildcard so the middleware applies to subpaths (e.g.,
/api/pack) if Hono requires/*.- Replace the magic number with a named/env-configurable constant and keep a small cushion vs Cloud Run’s 35s to fail gracefully in-app.
- Emit a JSON error with
requestIdfor consistency.Apply this diff to Line 34:
-app.use('/api', timeout(35_000)); +app.use('/api/*', timeout(API_TIMEOUT_MS, { + onTimeout: (c) => { + const requestId = c.get('requestId'); + return c.json(createErrorResponse('Request timed out. Please try again later.', requestId), 504); + }, +}));Add these near the imports/top of the file:
import { createErrorResponse } from './utils/http.js'; const API_TIMEOUT_MS = Number.parseInt(process.env.API_TIMEOUT_MS ?? '34000', 10);Note: Please verify Hono’s path matching. If
/apialready matches subpaths, keep it; otherwise prefer/api/*.website/client/components/Home/TryItUrlInput.vue (1)
13-18: Cancel event passthrough added — LGTM.New
cancelemit is correctly declared alongside existing events.website/client/components/Home/TryItResult.vue (1)
14-15: errorType prop propagation — LGTM.Optional prop with safe passthrough to ErrorContent; backward compatible.
Also applies to: 54-56
website/client/components/Home/TryItFolderUpload.vue (2)
14-15: Public cancel event added — LGTM.API surface matches other TryIt components.
114-115: Cancel passthrough to parent — LGTM.Matches new PackButton behavior.
website/client/components/Home/TryItFileUpload.vue (2)
14-15: Public cancel event added — LGTM.
104-105: Cancel passthrough to parent — LGTM.website/client/components/utils/requestHandlers.ts (1)
52-65: Use strict equality for AbortSignal.reason and simplify unknown case message.Avoid
String(signal.reason)casting; compare directly and provide a user-friendly fallback.- const isTimeout = String(signal?.reason) === 'timeout'; + const isTimeout = signal?.reason === 'timeout'; if (isTimeout) { onAbort?.('Request timed out.\nPlease consider using Include Patterns or Ignore Patterns to reduce the scope.'); return; } - const isCancelled = String(signal?.reason) === 'cancel'; + const isCancelled = signal?.reason === 'cancel'; if (isCancelled) { onAbort?.('Request was cancelled.'); return; } - onAbort?.('Request was cancelled with an unknown reason.'); + onAbort?.('Request was cancelled.');website/client/components/Home/PackButton.vue (2)
23-26: Type the component (props/emit/handler) for safety and consistencyConsider switching this script to TypeScript and adding types for props, emits, and the event parameter. Matches the pattern used in TryIt.vue.
-const props = defineProps({ - loading: Boolean, - isValid: Boolean, -}); +const props = defineProps<{ + loading?: boolean; + isValid?: boolean; +}>(); -const emit = defineEmits(['cancel']); +const emit = defineEmits<{ + (e: 'cancel'): void; +}>(); -function handleClick(event) { +function handleClick(event: MouseEvent) { if (props.loading) { event.preventDefault(); event.stopPropagation(); emit('cancel'); } }Additionally, change the script tag header to:
<script setup lang="ts">Also applies to: 28-36
56-56: LGTM: positioning enables layered hover/focus textposition: relative here correctly anchors the absolute text layers.
website/client/components/Home/TryIt.vue (5)
56-57: Good: wire PackButton cancel to composableThe cancel path is correctly routed via @cancel → handleCancel → cancelRequest.
96-96: Propagate error type to result viewPassing :error-type improves clarity of failure modes.
134-135: Expose errorType and cancelRequestCorrectly destructured from usePackRequest; aligns with the new flow.
Also applies to: 147-148
186-191: Remove redundant preventDefault (form already uses @submit.prevent)@submit.prevent on the form guarantees default prevention; this extra preventDefault is unnecessary and duplicates PackButton’s click guard.
- if (loading.value) { - event?.preventDefault(); - return; - } + if (loading.value) { + return; + }
226-229: LGTM: cancellation handlerhandleCancel cleanly forwards to cancelRequest.
…e quality Improve type safety and code quality based on PR review comments: - Convert PackButton.vue to TypeScript with proper type definitions - Fix AbortSignal.reason comparison using strict equality instead of String() - Extract magic number constant for API timeout in server configuration These changes enhance type safety, improve code robustness, and increase maintainability as suggested by automated code reviewers.
Implements cancel button functionality for pack requests with improved user experience.
Summary
This PR adds the ability to cancel ongoing pack requests on the website. The pack button now shows "Cancel" on hover during processing and allows users to abort requests in progress. Additionally, error messages have been improved for better user understanding.
Key Changes
Technical Implementation
Checklist
npm run testnpm run lint