feat(website): add file selection checkboxes for selective re-packing#791
Conversation
|
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. 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 WalkthroughIntroduces a file selection UI and repack flow. Adds FileInfo/allFiles types to client/server, emits a repack event from TryItResult, handles it in TryIt.vue via a new repackWithSelectedFiles composable function, and updates server to return allFiles and token counts. Adjusts client API base URL env logic and tsconfig includes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant TFS as TryItFileSelection.vue
participant TR as TryItResult.vue
participant TV as TryIt.vue
participant CPR as usePackRequest()
participant S as Server (remoteRepo)
U->>TFS: Select files + click "Re-pack Selected"
TFS-->>TR: emit repack(selectedFiles)
TR-->>TV: emit repack(selectedFiles)
TV->>CPR: repackWithSelectedFiles(selectedFiles)
activate CPR
CPR->>S: handlePackRequest({ includePatterns from selected paths, ignorePatterns cleared })
activate S
S-->>CPR: PackResult { metadata: allFiles, topFiles, ... }
deactivate S
CPR-->>TV: updated result (mark selected)
deactivate CPR
TV-->>TR: pass updated result
TR-->>TFS: render with new allFiles/selection
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Summary of Changes
Hello @spandan-kumar, 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 new feature to the Repomix website, enabling users to precisely control the content of their packed repository output. After an initial repository processing, users can now interactively select or deselect individual files via a new UI, and then re-pack the repository to generate output containing only the chosen files. This enhances customization and provides greater flexibility in managing packed code.
Highlights
- Interactive File Selection UI: A new TryItFileSelection.vue component provides a user-friendly interface to view all processed files, complete with checkboxes for individual selection, bulk select/deselect options, and real-time statistics on selected files and token counts.
- Dynamic Re-packing Functionality: Users can now trigger a "Re-pack Selected" action, which sends only the paths of the chosen files to the backend, allowing for on-demand regeneration of the packed output based on the refined selection.
- Extended API and Data Models: The backend API response (PackResult) has been enhanced to include comprehensive metadata for all processed files (allFiles array), leveraging a new FileInfo interface that tracks file path, character count, token count, and selection state.
- Improved Loading State: The loading indicator for repository processing has been updated for better user experience.
Using Gemini Code Assist
The 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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.
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
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Code Review
This pull request introduces a great new feature allowing users to select specific files for re-packing. The implementation is comprehensive, touching both frontend and backend. The new Vue components are well-structured. I've identified a critical bug in how props are passed to the error component, which will prevent error messages from displaying correctly. I've also noted some opportunities for improvement regarding Vue best practices, such as avoiding direct prop mutation, and some minor fixes for robustness and consistency. Overall, a solid feature addition with a few areas to refine.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (14)
website/client/tsconfig.json (1)
19-29: Include declaration files for new directories (optional).The expanded include globs look correct. To ensure ambient types and module augmentations in those folders are picked up, consider including .d.ts files as well.
"include": [ ".vitepress/**/*.ts", ".vitepress/**/*.d.ts", ".vitepress/**/*.tsx", ".vitepress/**/*.vue", "components/**/*.ts", "components/**/*.vue", + "components/**/*.d.ts", "composables/**/*.ts", + "composables/**/*.d.ts", "utils/**/*.ts", + "utils/**/*.d.ts", "constants/**/*.ts" + ,"constants/**/*.d.ts" ],website/server/src/types.ts (1)
19-24: Type shape for FileInfo matches client; consider exporting for reuse.The server-side FileInfo mirrors the client shape well. Exporting it (and/or unifying overlapping fields with TopFile) can reduce drift between types over time.
-interface FileInfo { +export interface FileInfo { path: string; charCount: number; tokenCount: number; selected?: boolean; }Optional DRY improvement:
interface TopFile { - path: string; - charCount: number; - tokenCount: number; + path: string; + charCount: number; + tokenCount: number; } -interface FileInfo { - path: string; - charCount: number; - tokenCount: number; - selected?: boolean; -} +export interface FileInfo extends TopFile { + selected?: boolean; +}website/client/components/Home/TryIt.vue (1)
199-201: Make handleRepack async and await the composable (optional).Awaiting the async composable here enables local try/catch or UI-side debouncing if needed and avoids overlapping calls.
-function handleRepack(selectedFiles: FileInfo[]) { - repackWithSelectedFiles(selectedFiles); -} +async function handleRepack(selectedFiles: FileInfo[]) { + await repackWithSelectedFiles(selectedFiles); +}website/server/src/remoteRepo.ts (1)
106-114: Coerce to numbers and consider response size for allFiles.
- Prefer
Number(...)to ensure numeric JSON values.- Exposing all files for large repos can bloat responses and cache entries. Consider capping, paging, or deferring details behind an additional endpoint/flag if you notice size/perf issues.
- allFiles: Object.entries(packResult.fileCharCounts) - .map(([path, charCount]) => ({ - path, - charCount: charCount as number, - tokenCount: packResult.fileTokenCounts[path] || 0, - selected: true, // Default to selected for initial packing - })) + allFiles: Object.entries(packResult.fileCharCounts) + .map(([path, charCount]) => ({ + path, + charCount: Number(charCount), + tokenCount: Number(packResult.fileTokenCounts[path] ?? 0), + selected: true, // Default to selected for initial packing + })) .sort((a, b) => b.charCount - a.charCount),If you decide to cap:
- Add a server-side limit (e.g., first 10k entries) and include a truncated flag in metadata.
- Or, provide a separate route to fetch allFiles with pagination.
website/client/composables/usePackRequest.ts (4)
102-104: Early-return on empty selection is fine; consider UX feedback (optional).If the UI ever triggers repack with zero selections, setting a friendly error (or toasting) would be clearer than noop.
- async function repackWithSelectedFiles(selectedFiles: FileInfo[]) { - if (!result.value || selectedFiles.length === 0) return; + async function repackWithSelectedFiles(selectedFiles: FileInfo[]) { + if (!result.value) return; + if (selectedFiles.length === 0) { + // Optional: provide UX feedback + // error.value = 'Select at least one file to re-pack.'; + return; + }
115-117: Edge case: file paths containing commas.Joining by comma mirrors the existing includePatterns semantics, but paths with commas will be ambiguous. If Repomix supports alternative separators or array payloads, consider that in the future.
131-153: Optimize selection marking from O(n²) to O(n).Use a Set for membership checks when marking selected files in the response.
- onSuccess: (response) => { - // Update file selection state in the new result - if (response.metadata?.allFiles) { - response.metadata.allFiles.forEach(file => { - file.selected = selectedPaths.includes(file.path); - }); - } - result.value = response; - }, + onSuccess: (response) => { + // Update file selection state in the new result + if (response.metadata?.allFiles) { + const selectedSet = new Set(selectedPaths); + response.metadata.allFiles.forEach((file) => { + file.selected = selectedSet.has(file.path); + }); + } + result.value = response; + },
149-151: Pass file only when needed (optional).For URL-based repacks,
fileis unused. Passing it conditionally can make intent clearer.- file: mode.value === 'file' || mode.value === 'folder' ? uploadedFile.value || undefined : undefined, + file: + mode.value === 'file' || mode.value === 'folder' + ? uploadedFile.value || undefined + : undefined,(Functional no-op; purely readability.)
website/client/components/api/client.ts (2)
59-59: Allow overriding API base URL via env for preview/staging buildsRelying solely on
import.meta.env.DEVcan make preview deployments target production API unintentionally. Add an explicitVITE_API_BASE_URLoverride.Apply this diff:
-const API_BASE_URL = import.meta.env.DEV ? 'http://localhost:8080' : 'https://api.repomix.com'; +const API_BASE_URL = + (import.meta.env.VITE_API_BASE_URL as string | undefined) ?? + (import.meta.env.DEV ? 'http://localhost:8080' : 'https://api.repomix.com');
61-85: Harden error handling for non-JSON or network failuresIf the server returns non-JSON on error (e.g., HTML error page)
response.json()will throw before you surface anApiError. Consider parsing the response as text first, then conditionally JSON-parse for robust errors, and wrap fetch in try/catch for network failures.You can refactor
packRepositorylike this (outside the selected lines):export async function packRepository(request: PackRequest): Promise<PackResult> { const formData = new FormData(); if (request.file) { formData.append('file', request.file); } else { formData.append('url', request.url); } formData.append('format', request.format); formData.append('options', JSON.stringify(request.options)); let response: Response; try { response = await fetch(`${API_BASE_URL}/api/pack`, { method: 'POST', body: formData, signal: request.signal, headers: { Accept: 'application/json', }, }); } catch (err) { throw new ApiError(`Network error: ${(err as Error)?.message ?? String(err)}`); } const text = await response.text(); if (!response.ok) { try { const parsed = JSON.parse(text) as ErrorResponse; throw new ApiError(parsed?.error ?? `Request failed with ${response.status}`); } catch { throw new ApiError(text || `Request failed with ${response.status}`); } } try { return JSON.parse(text) as PackResult; } catch { throw new ApiError('Invalid JSON in API response'); } }website/client/components/Home/TryItResult.vue (1)
37-41: Do not show selection UI when an error is presentGuard the file selection against error state to avoid rendering it alongside errors.
Apply this diff:
- <TryItFileSelection - v-if="result?.metadata?.allFiles && result.metadata.allFiles.length > 0" + <TryItFileSelection + v-if="!error && result?.metadata?.allFiles && result.metadata.allFiles.length > 0" :all-files="result.metadata.allFiles" :loading="loading" @repack="handleRepack" />website/client/components/Home/TryItFileSelection.vue (3)
43-45: Guard percentage calculations against zero total tokensWhen
totalTokensis 0, the percentage will render asNaN%. Guard with a zero check.Apply this diff:
- {{ selectedTokens.toLocaleString() }} tokens - ({{ ((selectedTokens / totalTokens) * 100).toFixed(1) }}%) + {{ selectedTokens.toLocaleString() }} tokens + ({{ totalTokens > 0 ? ((selectedTokens / totalTokens) * 100).toFixed(1) : '0.0' }}%)- {{ ((file.tokenCount / totalTokens) * 100).toFixed(1) }}<span class="unit">%</span> + {{ totalTokens > 0 ? ((file.tokenCount / totalTokens) * 100).toFixed(1) : '0.0' }}<span class="unit">%</span>Also applies to: 70-71
120-132: Directly mutating nested props is acceptable here, but consider trade-offsMutating
file.selected(nested prop) is allowed in Vue 3 and aligns with the goal to preserve selection state. If you want stricter one-way data flow in the future, consider maintaining local state and emitting updates to the parent, at the cost of more boilerplate.
61-62: Remove no-oponFileSelectionChangeand related listenersThe no-op handler and calls add noise. Vue reactivity will pick up
file.selectedmutations without it.Apply this diff:
- class="file-checkbox" - @change="onFileSelectionChange" + class="file-checkbox"- onFileSelectionChange();- onFileSelectionChange();-const onFileSelectionChange = () => { - // This will trigger reactivity updates -}; +// (removed: reactivity updates are automatic when mutating `file.selected`)Also applies to: 124-125, 131-132, 134-136
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
website/client/components/Home/TryIt.vue(4 hunks)website/client/components/Home/TryItFileSelection.vue(1 hunks)website/client/components/Home/TryItResult.vue(1 hunks)website/client/components/api/client.ts(3 hunks)website/client/composables/usePackRequest.ts(3 hunks)website/client/tsconfig.json(1 hunks)website/server/src/remoteRepo.ts(1 hunks)website/server/src/types.ts(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
website/client/composables/usePackRequest.ts (1)
website/client/components/api/client.ts (1)
FileInfo(13-18)
website/server/src/types.ts (1)
website/client/components/api/client.ts (1)
FileInfo(13-18)
🔇 Additional comments (9)
website/server/src/types.ts (1)
39-41: Addition of allFiles metadata looks good.The optional allFiles?: FileInfo[] aligns with the new UI flow and keeps the response backward compatible when omitted.
website/client/components/Home/TryIt.vue (2)
96-97: Good wiring of the repack event.Propagating the repack event from TryItResult into a local handler keeps TryIt.vue in control of the flow without coupling to child internals.
109-109: Type-only import for FileInfo is correct.Using
import typeavoids bundling the client API module at runtime.website/client/composables/usePackRequest.ts (2)
2-2: Importing FileInfo alongside PackResult is appropriate.Keeps the composable self-contained for typing both inputs and outputs of the repack flow.
208-209: Exposing repackWithSelectedFiles from the composable is the right API surface.This keeps the Try-It page thin and enables reuse elsewhere if needed.
website/client/components/api/client.ts (2)
13-18: New FileInfo type aligns with UI needs — LGTMThe shape supports path, token/char counts, and optional selection state needed for the checklist UI and repack flow.
40-45: Confirm server guaranteestokenCountintopFilesandallFilesThe client now expects
tokenCounton eachtopFilesentry andallFiles. If the website is ever pointed at an older API, this could break assumptions. If backward compatibility is a concern, consider makingtokenCountoptional here and guarding in the UI; otherwise, this looks consistent with the server changes described in the PR.Would you like me to scan the server types and payload construction to ensure
tokenCountis always present in bothtopFilesandallFiles?website/client/components/Home/TryItResult.vue (1)
26-49: Conditional flow is sensible
- Error has priority, then result content, then selection UI (conditionally), and finally a loading spinner when appropriate. The independent v-if for the selection panel allows showing it alongside the result, which matches the UX described.
website/client/components/Home/TryItFileSelection.vue (1)
80-93: Public API is clear and composable — LGTMProps, emits, and the computed structure are clean. Emitting
FileInfo[]ties neatly into the repack flow.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #791 +/- ##
=======================================
Coverage 87.44% 87.44%
=======================================
Files 113 113
Lines 6491 6491
Branches 1331 1331
=======================================
Hits 5676 5676
Misses 815 815 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Hi, @spandan-kumar ! The file selection feature with checkbox UI is a solid foundation, and showing token counts for each file is really helpful for users to understand the impact of their selections. For future iterations, we might consider implementing a tree view interface for more intuitive directory-level operations, but this flat list approach works well as an MVP. I really appreciate your contribution! 🎉 |
- Add FileInfo interface for individual file metadata - Extend PackResult to include allFiles array with complete file information - Create TryItFileSelection component with checkboxes for each file - Add bulk selection controls (Select All/Deselect All) - Implement re-packing functionality for selected files only - Add live statistics showing selected files and token counts - Fix TypeScript configuration for proper import.meta.env support - Add responsive design for mobile and desktop - Include scrollable file list with proper overflow handling Resolves the GitHub issue requesting checkboxes for file inclusion/exclusion on the website UI. Provides tree-based selection interface after initial packing process as suggested by maintainer. # Conflicts: # website/client/components/Home/TryItResult.vue
The Warp sponsor section was accidentally removed from the loading state in the file selection feature implementation. This commit restores the sponsor section with proper styling to maintain the original UX during repository processing.
Reverts the DEV/PROD logic change in API_BASE_URL back to the standard pattern used throughout the codebase. The original change was unintentional and inconsistent with project conventions.
Updates Warp sponsor section with: - New sponsor URL (go.warp.dev/repomix) - Updated brand image from Warp's official assets - Revised messaging emphasizing AI agent support - Improved loading state layout with sponsor content first
92bf74c to
a75248d
Compare
Reverts TryItResult.vue back to the original design to maintain consistency with existing UI patterns. Removes the file selection integration that was disrupting the component's original layout and styling structure.
Adds essential file selection capabilities while preserving the original TryItResult design structure: - Import FileInfo type and TryItFileSelection component - Add TypeScript interfaces for Props and Emits - Implement handleRepack function for file re-packing - Add TryItFileSelection component after result content - Maintain original styling and layout structure This provides the core file selection feature without disrupting the existing UI design patterns.
Separates loading state (spinner + sponsor section) into a dedicated component to prevent future conflicts: - Create TryItLoading.vue with loading spinner and Warp sponsor section - Update TryItResult.vue to use the new loading component - Remove loading-related CSS from TryItResult.vue - Improve component separation of concerns This change isolates sponsor-related updates from core functionality changes, reducing merge conflicts when sponsor content is updated.
Adjusts RateLimiter configuration to allow more requests during development: - Development: 10 requests per minute (increased from 3) - Production: 3 requests per minute (unchanged) This improves developer experience when testing file selection and re-packing functionality without being overly restrictive during development.
Add tokenCountTree option to CLI configuration to ensure token counts are calculated for all files in the repository, not just the top files. This is required for the file selection feature to display accurate token statistics for each individual file. The tokenCountTree option enables complete token count generation across the entire repository structure, supporting the selective re-packing functionality.
Add tabbed navigation to switch between result content and file selection: - Add tab navigation UI with Result and File Selection tabs - Implement tab switching logic with proper state management - Use v-show for performance optimization to maintain component state - Add defensive form submission handling to prevent accidental packing - Integrate file selection component with repack functionality - Maintain clean separation between result display and file management The tabbed interface improves UX by organizing content and preventing accidental form submissions from UI interaction buttons.
Update repomix package from v1.2.0 to v1.4.0 to support the new tokenCountTree option and other enhancements required for the file selection feature.
- Convert file list to table format with clickable rows and compact layout - Add warning message when selecting more than 500 files - Increase include patterns limit from 1,000 to 100,000 characters - Auto-switch to Result tab when Re-pack button is clicked - Fix re-pack loading state to use shared loading status - Sort files by token count instead of character count - Replace Package icon with custom SVG for re-pack button - Add row hover effects and full-row click selection - Implement proper file selection state management during re-pack
- Create PackIcon.vue component with configurable size prop - Replace inline SVG in PackButton.vue with PackIcon component - Replace inline SVG in TryItFileSelection.vue with PackIcon component - Improve maintainability and consistency across pack-related UI elements - Remove unused Package import from lucide-vue-next
- Replace forEach loops with for...of loops for better performance - Fix import ordering and spacing inconsistencies - Simplify interface definitions and type declarations - Add proper padding to tokens column in file selection table - Remove unnecessary line breaks in computed properties - Improve code readability and consistency across components
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a file selection feature, allowing users to choose which files to include in the packed output. The changes are well-structured, with new Vue components for the UI, updates to the API client and server to handle file metadata, and modifications to the usePackRequest composable to manage the re-packing logic.
The implementation is solid, but I've identified a couple of areas for improvement in the new TryItFileSelection.vue component. One is a high-severity issue regarding direct prop mutation, which goes against Vue's best practices. The other is a medium-severity issue concerning an edge case in the UI that could lead to displaying NaN.
Overall, this is a great feature addition. Addressing these points will improve the robustness and maintainability of the new functionality.
Remove redundant border and border-radius from file list container as the table already provides appropriate visual separation
- Add local reactive state to avoid mutating props directly - Use deep copying via watch to sync props.allFiles to localFiles - Remove unnecessary @change handler since v-model handles changes - This addresses Vue anti-pattern of mutating props identified by code review Fixes the issue where the component was directly mutating the `selected` property of files passed via props, which violates Vue's one-way data flow.
…ntainability - Extract constants: FILE_SELECTION_WARNING_THRESHOLD and TabType to dedicated files - Extract warning message to reusable FileSelectionWarning component - Optimize performance: replace deep watch with shallow watch and use structuredClone - Enhance accessibility: add comprehensive aria-label attributes to all interactive elements - Strengthen error handling: add zero division protection for percentage calculation - Improve code quality: enhance comments and maintain consistent type definitions This refactoring improves code maintainability, performance, and user experience while following Vue.js and accessibility best practices.
- Add try-catch around structuredClone with JSON fallback - Fixes DataCloneError in environments where structuredClone fails - Ensures compatibility across different browser environments
aceb45e to
7ff2e00
Compare
|
@spandan-kumar
I'll merge it as is! |
|
Looks great, kudos man! Happy to contribute. |

feat: add file selection checkboxes for website UI
Implements the requested checklist feature for including/excluding files from GitHub repositories on the website UI.
Closes #754
Overview
This PR adds a comprehensive file selection interface to the Repomix website, allowing users to selectively choose which files to include in their packed output after the initial repository processing.
Features Implemented
Backend Enhancements
allFilesarray)FileInfointerface with path, token count, character count, and selection statePackResultto support file-level information for UI interactionFrontend Components
TryItFileSelection.vuecomponent with interactive file listUser Experience
How It Works
UI Preview