Skip to content

Conversation

@rodleviton
Copy link
Owner

@rodleviton rodleviton commented Jun 25, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a high-performance, fully customizable Vue 3 card stack component with strong TypeScript support, drag/swipe gestures, accessibility, and advanced configuration options.
    • Added composables and utilities for card stack logic, drag handling, and stack calculations.
    • Provided a type-safe factory for custom card data and a plugin for easy global registration.
    • Added comprehensive documentation, configuration files, and a Vue 3 demo app.
  • Refactor

    • Modernized and modularized the codebase, transitioning from a Vue 2-centric setup to a framework-agnostic, monorepo structure.
    • Replaced legacy build, lint, and formatting tools with modern equivalents (Vite, Turbo, ESLint, Prettier).
  • Chores

    • Added and updated workspace, linting, formatting, and TypeScript configuration files for improved development experience and code quality.
    • Enhanced .gitignore rules for broader development scenarios.
  • Documentation

    • Added detailed READMEs for the core component, ESLint, Prettier, and TypeScript configurations, as well as usage examples and troubleshooting guides.
  • Style

    • Enforced consistent code style and formatting across the project with EditorConfig, Prettier, and recommended VS Code extensions.
  • Revert

    • Removed legacy Vue 2 components, build scripts, documentation, and demo files in favor of the new structure.

@coderabbitai
Copy link

coderabbitai bot commented Jun 25, 2025

Walkthrough

This update represents a major refactor and modernization of the card stack component project. The legacy Vue 2 codebase, documentation, and build tooling are removed. In their place, a new monorepo structure is introduced, featuring a Vue 3, TypeScript, and Vite-based implementation with improved linting, formatting, and type safety. New workspace, demo, and tooling configurations are added.

Changes

Files/Paths Change Summary
.browserslistrc, babel.config.js, build/rollup.config.js Deleted legacy browser, Babel, and Rollup configuration files.
.gitignore Expanded and reorganized ignore patterns for modern tooling and environments.
docs-src/, docs/, src/ (all files) Removed legacy documentation, demos, build artifacts, and Vue 2 component code.
package.json Refactored: renamed, simplified, removed Vue 2/build fields, added modern scripts and dev tooling.
pnpm-workspace.yaml, turbo.json Added workspace and Turborepo configuration for monorepo management.
.editorconfig, .vscode/extensions.json Added editor and extension recommendations for consistent development.
tooling/eslint/, tooling/prettier/, tooling/typescript/ Introduced shared ESLint, Prettier, and TypeScript configurations with documentation and package metadata.
packages/vue/ (all files) Added new Vue 3 component, composables, types, utilities, ESLint config, TypeScript config, and Vite build.
demos/vue/ (all files) Added a Vue 3 demo app, with strict TypeScript, Vite, and modern linting/formatting setup.

Changes Table

Files/Paths Change Summary
.browserslistrc, babel.config.js, build/rollup.config.js Deleted legacy browser, Babel, and Rollup configuration files.
.gitignore Expanded and reorganized ignore patterns for modern tooling and environments.
docs-src/, docs/, src/ (all files) Removed all legacy documentation, Vue 2 demos, build artifacts, and Vue 2 component code.
package.json Refactored: renamed, simplified, removed Vue 2/build fields, added modern scripts and dev tooling.
pnpm-workspace.yaml, turbo.json Added workspace and Turborepo configuration for monorepo management.
.editorconfig, .vscode/extensions.json Added editor and extension recommendations for consistent development.
tooling/eslint/, tooling/prettier/, tooling/typescript/ Introduced shared ESLint, Prettier, and TypeScript configurations with documentation and package metadata.
packages/vue/ (all files) Added Vue 3 component, composables, types, utilities, ESLint config, TypeScript config, and Vite build.
demos/vue/ (all files) Added Vue 3 demo app, strict TypeScript, Vite, and modern linting/formatting setup.

Sequence Diagram(s)

sequenceDiagram
    participant App as Vue Demo App
    participant VueCardStack as VueCardStack Component
    participant useCardStack as useCardStack Composable
    participant useDragHandling as useDragHandling Composable
    participant useStackCalculations as useStackCalculations Composable

    App->>VueCardStack: Render with cards/config/slots
    VueCardStack->>useCardStack: Initialize stack state & events
    useCardStack->>useStackCalculations: Compute layout & positions
    useCardStack->>useDragHandling: Setup drag/touch event handlers
    VueCardStack-->>App: Emit move events
    App->>VueCardStack: Handle navigation (onNext/onPrevious)
    VueCardStack->>useCardStack: Update stack & layout
    useCardStack->>useStackCalculations: Recompute positions
    VueCardStack-->>App: Update slot props (card/nav)
Loading

Poem

🐇✨
A hop, a leap, a sweeping change,
Old code is gone, the stack’s rearranged!
Vue 3 and TypeScript, composables new,
With linting and Prettier, the codebase grew.
Now demos are crisp, the monorepo neat—
This rabbit’s stack is truly elite!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

demos/vue/src/main.ts

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

packages/vue/src/composables/useCardStack.ts

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

demos/vue/src/App.vue

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

  • 13 others
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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 (8)
.gitignore (1)

3-3: Prefer dist/ to avoid false-positives

Adding the trailing slash limits the ignore rule to the directory itself and avoids accidentally ignoring a file literally named dist.

-dist
+dist/
packages/vue/tsconfig.node.json (1)

1-10: Add explicit Node typings for better IntelliSense

Vite configs frequently access Node globals (process, __dirname, etc.). Declaring the Node type definitions avoids implicit-any warnings in strict projects.

     "allowSyntheticDefaultImports": true,
+    "types": ["node"]

Optional: if the root tsconfig.json already contains common settings, consider extending it to avoid duplication:

{
  "extends": "../tsconfig.json",
  ...
}
examples/vue/tsconfig.node.json (1)

1-10: Mirror the Node typings improvement

Same rationale as in packages/vue/tsconfig.node.json.

     "allowSyntheticDefaultImports": true
+    ,"types": ["node"]
packages/vue/src/env.d.ts (1)

1-5: Replace banned {} with a safer object shape

Biome flags {} as “any non-nullable value”. Swap in Record<string, unknown> (or simply omit the generics) to satisfy the linter without changing behaviour.

-  const component: DefineComponent<{}, {}, any>;
+  const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>;
examples/vue/src/env.d.ts (1)

3-7: Mirror the fix for banned {} here as well

Same linter warning; apply the identical change.

-  const component: DefineComponent<{}, {}, any>;
+  const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>;
packages/vue/README.md (1)

91-105: Fix grammatical issues in the slots documentation.

The static analysis correctly identified grammatical issues that should be addressed for better documentation clarity.

Apply this diff to fix the grammatical issues:

-#### Props
+#### Slot Props
 
 - `card`: The card data object with additional internal properties
 - `$index`: Current index in the stack
 
 ### Navigation Slot (`#nav`)
 
 Slot for customizing navigation controls.
 
-#### Props
+#### Slot Props
 
 - `activeCardIndex`: Current active card index
-- `onNext`: Function to move to next card
-- `onPrevious`: Function to move to previous card
+- `onNext`: Function to move to the next card
+- `onPrevious`: Function to move to the previous card
packages/vue/src/composables/useStackCalculations.ts (1)

38-40: Simplify the scaleMultiplier calculation.

The current formula can be simplified for better readability.

Apply this diff to simplify the calculation:

-return ((config.value.scaleMultiplier - 1) * -1) / 10;
+return (1 - config.value.scaleMultiplier) / 10;
packages/vue/src/composables/useCardStack.ts (1)

1-264: Excellent implementation of the card stack composable!

The composable is well-architected with:

  • Proper use of shallowRef for performance optimization with large arrays
  • Clean separation of concerns using helper composables
  • Comprehensive lifecycle management with proper cleanup
  • Well-documented functions

Consider adding error handling for edge cases like empty card arrays or invalid configurations to make the component more robust.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 266bb79 and 1fa3a89.

⛔ Files ignored due to path filters (15)
  • dist/vue-card-stack.esm.js is excluded by !**/dist/**
  • dist/vue-card-stack.min.js is excluded by !**/dist/**, !**/*.min.js
  • dist/vue-card-stack.ssr.js is excluded by !**/dist/**
  • docs/assets/img/search.83621669.svg is excluded by !**/*.svg
  • package-lock.json is excluded by !**/package-lock.json
  • packages/vue/dist/components/VueCardStack.vue.d.ts is excluded by !**/dist/**
  • packages/vue/dist/composables/useCardStack.d.ts is excluded by !**/dist/**
  • packages/vue/dist/composables/useDragHandling.d.ts is excluded by !**/dist/**
  • packages/vue/dist/composables/useStackCalculations.d.ts is excluded by !**/dist/**
  • packages/vue/dist/index.cjs is excluded by !**/dist/**
  • packages/vue/dist/index.d.ts is excluded by !**/dist/**
  • packages/vue/dist/index.js is excluded by !**/dist/**
  • packages/vue/dist/types/index.d.ts is excluded by !**/dist/**
  • packages/vue/dist/utils/debounce.d.ts is excluded by !**/dist/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (56)
  • .browserslistrc (0 hunks)
  • .gitignore (1 hunks)
  • babel.config.js (0 hunks)
  • build/rollup.config.js (0 hunks)
  • docs-src/.vuepress/components/BasicDemo.vue (0 hunks)
  • docs-src/.vuepress/components/InteractiveDemo.vue (0 hunks)
  • docs-src/.vuepress/components/Logo.vue (0 hunks)
  • docs-src/.vuepress/components/WithNavigation.vue (0 hunks)
  • docs-src/.vuepress/config.js (0 hunks)
  • docs-src/.vuepress/theme/components/Navbar.vue (0 hunks)
  • docs-src/.vuepress/theme/index.js (0 hunks)
  • docs-src/README.md (0 hunks)
  • docs/404.html (0 hunks)
  • docs/assets/css/0.styles.b1edaa7e.css (0 hunks)
  • docs/assets/js/10.e7543215.js (0 hunks)
  • docs/assets/js/11.f75c36d0.js (0 hunks)
  • docs/assets/js/12.84e3d62c.js (0 hunks)
  • docs/assets/js/2.c33d44d0.js (0 hunks)
  • docs/assets/js/3.8a696035.js (0 hunks)
  • docs/assets/js/4.f2c8cd7b.js (0 hunks)
  • docs/assets/js/5.e2f78417.js (0 hunks)
  • docs/assets/js/6.7305ea95.js (0 hunks)
  • docs/assets/js/7.b6985e8a.js (0 hunks)
  • docs/assets/js/8.cedb5513.js (0 hunks)
  • docs/assets/js/9.3de4e58b.js (0 hunks)
  • docs/index.html (0 hunks)
  • examples/vue/App.vue (1 hunks)
  • examples/vue/index.html (1 hunks)
  • examples/vue/package.json (1 hunks)
  • examples/vue/src/App.vue (1 hunks)
  • examples/vue/src/env.d.ts (1 hunks)
  • examples/vue/src/main.ts (1 hunks)
  • examples/vue/src/types.ts (1 hunks)
  • examples/vue/tsconfig.json (1 hunks)
  • examples/vue/tsconfig.node.json (1 hunks)
  • examples/vue/vite.config.ts (1 hunks)
  • package.json (1 hunks)
  • packages/vue/README.md (1 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/components/VueCardStack.vue (1 hunks)
  • packages/vue/src/composables/useCardStack.ts (1 hunks)
  • packages/vue/src/composables/useDragHandling.ts (1 hunks)
  • packages/vue/src/composables/useStackCalculations.ts (1 hunks)
  • packages/vue/src/env.d.ts (1 hunks)
  • packages/vue/src/index.ts (1 hunks)
  • packages/vue/src/types/index.ts (1 hunks)
  • packages/vue/src/utils/debounce.ts (1 hunks)
  • packages/vue/tsconfig.json (1 hunks)
  • packages/vue/tsconfig.node.json (1 hunks)
  • packages/vue/vite.config.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • src/entry.js (0 hunks)
  • src/serve-dev.js (0 hunks)
  • src/serve-dev.vue (0 hunks)
  • src/utils/debounce.js (0 hunks)
  • src/vue-card-stack.vue (0 hunks)
💤 Files with no reviewable changes (30)
  • .browserslistrc
  • docs-src/.vuepress/theme/index.js
  • babel.config.js
  • src/serve-dev.js
  • docs-src/.vuepress/config.js
  • docs/assets/js/11.f75c36d0.js
  • docs-src/.vuepress/components/Logo.vue
  • docs-src/.vuepress/components/BasicDemo.vue
  • docs/assets/js/9.3de4e58b.js
  • docs/404.html
  • src/entry.js
  • docs-src/README.md
  • build/rollup.config.js
  • docs-src/.vuepress/components/WithNavigation.vue
  • docs/assets/css/0.styles.b1edaa7e.css
  • docs/assets/js/10.e7543215.js
  • docs/assets/js/8.cedb5513.js
  • docs/assets/js/12.84e3d62c.js
  • docs-src/.vuepress/components/InteractiveDemo.vue
  • docs/index.html
  • src/serve-dev.vue
  • src/utils/debounce.js
  • docs-src/.vuepress/theme/components/Navbar.vue
  • docs/assets/js/3.8a696035.js
  • docs/assets/js/7.b6985e8a.js
  • docs/assets/js/6.7305ea95.js
  • src/vue-card-stack.vue
  • docs/assets/js/5.e2f78417.js
  • docs/assets/js/2.c33d44d0.js
  • docs/assets/js/4.f2c8cd7b.js
🧰 Additional context used
🧬 Code Graph Analysis (5)
examples/vue/src/types.ts (1)
packages/vue/src/types/index.ts (1)
  • BaseCardData (4-7)
packages/vue/src/composables/useDragHandling.ts (1)
packages/vue/src/types/index.ts (2)
  • CardStackConfig (12-31)
  • DragEvent (64-64)
packages/vue/src/composables/useStackCalculations.ts (1)
packages/vue/src/types/index.ts (2)
  • BaseCardData (4-7)
  • CardStackConfig (12-31)
packages/vue/src/composables/useCardStack.ts (4)
packages/vue/src/types/index.ts (4)
  • BaseCardData (4-7)
  • CardStackConfig (12-31)
  • InternalCard (36-59)
  • DragEvent (64-64)
packages/vue/src/composables/useStackCalculations.ts (1)
  • useStackCalculations (7-131)
packages/vue/src/composables/useDragHandling.ts (1)
  • useDragHandling (7-123)
packages/vue/src/utils/debounce.ts (1)
  • debounce (9-24)
packages/vue/src/types/index.ts (1)
packages/vue/dist/types/index.d.ts (8)
  • BaseCardData (4-7)
  • CardStackConfig (11-30)
  • InternalCard (34-57)
  • DragEvent (61-61)
  • CardStackProps (65-86)
  • CardStackEmits (90-93)
  • NavSlotProps (97-104)
  • CardSlotProps (108-113)
🪛 Biome (1.9.4)
packages/vue/src/env.d.ts

[error] 3-3: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 3-3: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

examples/vue/src/env.d.ts

[error] 5-5: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 5-5: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🪛 LanguageTool
packages/vue/README.md

[uncategorized] ~93-~93: Loose punctuation mark.
Context: ...dual card content. #### Props - card: The card data object with additional in...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~102-~102: Loose punctuation mark.
Context: ...ntrols. #### Props - activeCardIndex: Current active card index - onNext: F...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~103-~103: You might be missing the article “the” here.
Context: ...d index - onNext: Function to move to next card - onPrevious: Function to move t...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~104-~104: You might be missing the article “the” here.
Context: ...ard - onPrevious: Function to move to previous card ## TypeScript Support The compon...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🔇 Additional comments (27)
pnpm-workspace.yaml (1)

1-3: Workspace glob definitions look good

The YAML syntax and workspace globs are correct and will pick up every package under packages/* and examples/*. No issues spotted.

examples/vue/src/main.ts (1)

1-4: Implementation is concise and idiomatic

The bootstrap code follows the standard Vue 3 pattern and compiles fine under strict TypeScript settings. ✅

examples/vue/vite.config.ts (1)

1-6: Minimal, correct Vite config – looks good

Nothing to add. Default export with the Vue plugin is the canonical setup for a sample app.

examples/vue/index.html (1)

1-11: HTML scaffold is fine

The document is minimal yet sufficient for a Vite-served demo. No issues spotted.

examples/vue/src/types.ts (1)

1-11: Type alias is clear and extensible

Good use of intersection to extend the shared BaseCardData. No concerns.

examples/vue/tsconfig.json (1)

1-21: LGTM! Well-configured TypeScript setup for Vue 3.

The TypeScript configuration follows modern best practices with appropriate compiler options for a Vue 3 + Vite project. The strict type checking and safety options will help maintain code quality.

packages/vue/vite.config.ts (1)

1-31: Excellent Vite library configuration for Vue component package.

The configuration properly sets up library mode with TypeScript declarations, appropriate externals, and dual module format support. The CSS asset naming and code splitting settings are well-configured for a component library.

packages/vue/tsconfig.json (1)

1-22: Consistent TypeScript configuration for the Vue package.

The configuration matches the example project with the appropriate addition of Node.js types. This consistency will help maintain uniform development experience across the workspace.

examples/vue/package.json (1)

1-21: Well-structured example package configuration.

The package.json correctly uses workspace protocol for the local dependency, appropriate Vue 3 version, and standard Vite tooling setup. Good use of private flag for the example project.

packages/vue/package.json (1)

1-36: Excellent library package configuration with comprehensive exports.

The package.json follows modern packaging best practices with proper export mappings for TypeScript, ES modules, CommonJS, and CSS. The peer dependency approach for Vue and absence of runtime dependencies are appropriate design choices for a component library.

packages/vue/src/index.ts (1)

1-22: Well-structured Vue 3 library entry point.

The implementation correctly provides both modern Vue 3 plugin installation via VueCardStackPlugin and legacy global Vue support. The TypeScript declarations and component exports follow Vue 3 best practices.

packages/vue/src/composables/useDragHandling.ts (1)

1-28: Excellent composable structure with comprehensive device detection.

The composable provides good separation of concerns with reactive state management and cross-platform event handling. The device detection and event type mapping is well-implemented.

examples/vue/App.vue (1)

1-26: Good example demonstrating component usage with scoped slots.

The component usage properly demonstrates the VueCardStack with both card content customization and navigation controls using scoped slots. Event handling is correctly implemented.

packages/vue/src/utils/debounce.ts (1)

1-47: Well-implemented utility functions with excellent TypeScript support.

Both debounce and throttle functions are correctly implemented with proper generic type preservation and clear documentation. The implementations follow standard patterns and handle edge cases appropriately.

package.json (1)

2-22: Good transformation to framework-agnostic workspace structure.

The package.json changes appropriately reflect the architectural shift from a Vue-specific library to a framework-agnostic workspace. The private flag, pnpm workspace commands, and updated keywords align well with the refactor objectives.

examples/vue/src/App.vue (1)

1-27: Code implementation looks good!

The example properly demonstrates the usage of the VueCardStack component with TypeScript and Vue 3 Composition API.

Note: The AI summary mentions listening to change events, but the code only implements the @move event handler.

Likely an incorrect or invalid review comment.

packages/vue/src/composables/useStackCalculations.ts (1)

29-33: Potential edge case in maxVisibleCards calculation.

When cards.value.length equals config.value.maxVisibleCards, the function returns cards.value.length - 1, which might not be the intended behavior. Consider whether all cards should be visible in this case.

Could you clarify the intended behavior when the number of cards equals the configured maximum visible cards?

packages/vue/src/components/VueCardStack.vue (1)

1-102: Well-structured Vue 3 component!

The component follows Vue 3 best practices with proper TypeScript integration, reactive state management, and clean slot-based architecture. The use of toRef for creating the reactive config and the integration with composables is excellent.

packages/vue/src/types/index.ts (9)

4-7: Well-designed base interface.

The BaseCardData interface provides a clean foundation for card data with optional internal properties. The underscore prefix convention clearly indicates these are system-managed properties.


12-31: Comprehensive and well-documented configuration interface.

The CardStackConfig interface covers all necessary configuration options with clear documentation. The flexible stackWidth typing supports both pixel values and CSS strings, which is excellent for component flexibility.


36-59: Excellent internal state interface design.

The InternalCard interface properly captures all necessary internal state properties for card positioning, styling, and interaction. The distinction between optional properties in BaseCardData and required properties here demonstrates good architectural thinking.


64-64: Appropriate type union for cross-platform compatibility.

The DragEvent type correctly handles both mouse and touch interactions, ensuring the component works across different input methods.


69-90: Well-structured component props interface.

The generic CardStackProps interface correctly maps configuration options to optional component props while keeping the essential cards array required. This design supports default values while maintaining type safety.


95-98: Correct Vue 3 emit interface implementation.

The CardStackEmits interface properly uses tuple syntax for Vue 3 emit definitions. The single move event with a numeric parameter is appropriate for card stack interactions.


103-110: Clean navigation slot props interface.

The NavSlotProps interface provides the necessary data and actions for navigation slots with clear, focused properties.


115-120: Excellent slot props design with intersection types.

The CardSlotProps interface effectively combines external card data with internal properties using intersection types (T & InternalCard). This provides slots with complete access to both user data and internal state while maintaining type safety.


1-120: Outstanding TypeScript architecture for Vue 3 component.

This type definition file demonstrates excellent TypeScript practices and Vue 3 component design. The interfaces show thoughtful separation of concerns, proper generic usage, and comprehensive documentation. The type system provides strong guarantees while maintaining flexibility for various use cases.

Key strengths:

  • Clear distinction between external data (BaseCardData) and internal state (InternalCard)
  • Flexible configuration with appropriate optional/required property distinctions
  • Proper Vue 3 emit and slot typing patterns
  • Comprehensive documentation throughout

This forms a solid foundation for the refactored card stack component.

Comment on lines 83 to 89
const shouldChangeCard = (distanceTravelled: number): boolean => {
const minDistanceToTravel =
(config.value.cardWidth + config.value.paddingHorizontal) /
(1 / config.value.sensitivity);

return Math.abs(distanceTravelled) > minDistanceToTravel;
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Sensitivity calculation is inverted.

The sensitivity calculation produces counterintuitive behavior. When sensitivity is higher (closer to 1), the threshold becomes larger, requiring MORE drag distance to trigger a card change. This is opposite to expected behavior where higher sensitivity should require LESS distance.

The current formula:

const minDistanceToTravel = (config.value.cardWidth + config.value.paddingHorizontal) / (1 / config.value.sensitivity);

Should be:

- const minDistanceToTravel =
-   (config.value.cardWidth + config.value.paddingHorizontal) /
-   (1 / config.value.sensitivity);
+ const minDistanceToTravel =
+   (config.value.cardWidth + config.value.paddingHorizontal) /
+   config.value.sensitivity;

This ensures higher sensitivity values (closer to 1) result in lower thresholds for easier card changes.

🤖 Prompt for AI Agents
In packages/vue/src/composables/useDragHandling.ts around lines 83 to 89, the
sensitivity calculation is inverted, causing higher sensitivity values to
increase the drag distance threshold instead of decreasing it. To fix this,
change the formula so that minDistanceToTravel is multiplied by the inverse of
sensitivity rather than divided by it, ensuring that higher sensitivity values
reduce the required drag distance for a card change.

</template>

<script>
import { VueCardStack } from '@vue-card-stack/vue';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Verification correct

Incorrect import path - package name mismatch.

The import uses @vue-card-stack/vue but based on the package restructuring, this should likely be @card-stack/vue to match the new package naming convention.

- import { VueCardStack } from '@vue-card-stack/vue';
+ import { VueCardStack } from '@card-stack/vue';

Verify the correct package name by checking the Vue package's package.json:


🏁 Script executed:

#!/bin/bash
# Check the actual package name in the Vue package
cat packages/vue/package.json | jq '.name'

Length of output: 60


Incorrect import path - package name mismatch.

The import uses @vue-card-stack/vue but based on the package restructuring, this should likely be @card-stack/vue to match the new package naming convention.

- import { VueCardStack } from '@vue-card-stack/vue';
+ import { VueCardStack } from '@card-stack/vue';

Verify the correct package name by checking the Vue package's package.json:

#!/bin/bash
# Check the actual package name in the Vue package
cat packages/vue/package.json | jq '.name'
🤖 Prompt for AI Agents
In examples/vue/App.vue at line 29, the import path '@vue-card-stack/vue' is
incorrect due to package renaming. Change the import statement to use
'@card-stack/vue' instead to match the updated package naming convention. Verify
the correct package name by checking the package.json of the Vue package if
needed.

coderabbitai bot added a commit that referenced this pull request Jun 25, 2025
Docstrings generation was requested by @rodleviton.

* #25 (comment)

The following files were modified:

* `packages/vue/src/composables/useCardStack.ts`
* `packages/vue/src/composables/useDragHandling.ts`
* `packages/vue/src/composables/useStackCalculations.ts`
* `packages/vue/src/utils/debounce.ts`
@coderabbitai
Copy link

coderabbitai bot commented Jun 25, 2025

Note

Generated docstrings for this pull request at #26

Refactored VueCardStack to support generic card types for full TypeScript inference. Added createVueCardStack factory for type-safe usage, updated slot and prop typings, and improved documentation for custom card data. Updated example and README to demonstrate new strongly-typed API.
Card slot props now provide a `data` property containing only user-defined card fields, separating them from internal properties like _id and _index. This improves type safety and clarity for slot consumers. Example and typings updated accordingly in both source and dist files.
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: 1

🧹 Nitpick comments (1)
packages/vue/src/components/VueCardStackDefineComponent.ts (1)

38-38: Consider the data property type design.

The data: Omit<T, keyof BaseCardData> approach removes base properties from the generic type, but this might be confusing since the card object also contains _id and _index at the same level (lines 26-27).

Consider flattening the structure or making the separation clearer:

-          data: Omit<T, keyof BaseCardData>;
+          ...T; // Spread all T properties at the top level

Or provide clearer documentation about why data is separated from the base card properties.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fa3a89 and b357078.

⛔ Files ignored due to path filters (5)
  • packages/vue/dist/components/VueCardStack.vue.d.ts is excluded by !**/dist/**
  • packages/vue/dist/index.cjs is excluded by !**/dist/**
  • packages/vue/dist/index.d.ts is excluded by !**/dist/**
  • packages/vue/dist/index.js is excluded by !**/dist/**
  • packages/vue/dist/types/index.d.ts is excluded by !**/dist/**
📒 Files selected for processing (6)
  • examples/vue/src/App.vue (1 hunks)
  • packages/vue/README.md (1 hunks)
  • packages/vue/src/components/VueCardStack.vue (1 hunks)
  • packages/vue/src/components/VueCardStackDefineComponent.ts (1 hunks)
  • packages/vue/src/index.ts (1 hunks)
  • packages/vue/src/types/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/vue/README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/vue/src/index.ts
  • examples/vue/src/App.vue
  • packages/vue/src/components/VueCardStack.vue
  • packages/vue/src/types/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/vue/src/components/VueCardStackDefineComponent.ts (3)
packages/vue/dist/index.d.ts (2)
  • createVueCardStack (4-4)
  • VueCardStack (5-5)
packages/vue/src/index.ts (2)
  • createVueCardStack (3-3)
  • VueCardStack (6-6)
packages/vue/src/types/index.ts (1)
  • BaseCardData (4-7)
🔇 Additional comments (2)
packages/vue/src/components/VueCardStackDefineComponent.ts (2)

1-2: LGTM! Clean imports and exports.

The imports are well-structured and the re-export of the raw component provides flexibility for users who don't need the generic typing.

Also applies to: 52-52


24-39: Verify slot prop definitions match the actual component implementation.

The card slot has extensive prop definitions including positioning, styling, and data properties. Ensure these match exactly with what the actual VueCardStack.vue component provides to avoid runtime errors.

Run this script to verify the slot prop definitions:

#!/bin/bash
# Description: Check VueCardStack.vue implementation for slot prop consistency

# Search for slot definitions and prop passing in the Vue component
rg -A 10 -B 5 "slot.*card" packages/vue/src/components/VueCardStack.vue
rg -A 10 -B 5 "v-slot" packages/vue/src/components/VueCardStack.vue
rg -A 10 -B 5 "\$slots" packages/vue/src/components/VueCardStack.vue

Comment on lines 8 to 49
export function createVueCardStack<T extends BaseCardData = BaseCardData>() {
return VueCardStack as typeof VueCardStack & {
new (): {
$props: {
cards: T[];
cardWidth?: number;
cardHeight?: number;
stackWidth?: number | string | null;
sensitivity?: number;
maxVisibleCards?: number;
scaleMultiplier?: number;
speed?: number;
paddingHorizontal?: number;
paddingVertical?: number;
};
$slots: {
card: (props: {
card: {
_id: number;
_index: number;
xPos: number;
yPos: number;
scale: number;
opacity: number;
display: string;
zIndex: number;
width: number;
height: number;
isDragging: boolean;
$index: number;
data: Omit<T, keyof BaseCardData>;
};
}) => any;
nav: (props: {
activeCardIndex: number;
onNext: () => void;
onPrevious: () => void;
}) => any;
};
};
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using defineComponent or a more type-safe approach instead of type assertion.

The current implementation uses type assertion (as typeof VueCardStack & ...) to add generic typing, which bypasses TypeScript's type checking and could lead to runtime inconsistencies. Additionally, manually defining props and slots creates a maintenance burden and risk of divergence from the actual component implementation.

Consider these alternatives:

Option 1: Use Vue's defineComponent with generic support

-export function createVueCardStack<T extends BaseCardData = BaseCardData>() {
-  return VueCardStack as typeof VueCardStack & {
-    new (): {
-      $props: {
-        cards: T[];
-        // ... other props
-      };
-      $slots: {
-        // ... slots
-      };
-    };
-  };
-}
+export function createVueCardStack<T extends BaseCardData = BaseCardData>() {
+  return defineComponent({
+    extends: VueCardStack,
+    props: {
+      ...VueCardStack.props,
+      cards: {
+        type: Array as PropType<T[]>,
+        required: true
+      }
+    }
+  });
+}

Option 2: Use a HOC (Higher-Order Component) pattern

export function createVueCardStack<T extends BaseCardData = BaseCardData>() {
  return (VueCardStack as any) as DefineComponent<{
    cards: T[];
    // ... other props from VueCardStack
  }>;
}

This approach would provide better type safety and reduce the risk of prop/slot definitions becoming stale.

🤖 Prompt for AI Agents
In packages/vue/src/components/VueCardStackDefineComponent.ts lines 8 to 49,
replace the current type assertion approach with a more type-safe method by
using Vue's defineComponent with generic support or a Higher-Order Component
pattern. This means you should avoid manually asserting the component type and
instead create the component using defineComponent to infer props and slots
properly or cast VueCardStack to a DefineComponent with the appropriate generic
props. This will improve type safety, maintainability, and keep prop and slot
definitions in sync with the actual component implementation.

Introduces a new @card-stack/eslint package with a shared ESLint configuration supporting TypeScript and Vue 3. Adds .eslintrc.js files to the root, examples/vue, and packages/vue to extend the shared config. Updates package.json scripts and devDependencies to support linting and formatting for Vue files. Registers the new tooling/eslint workspace in pnpm-workspace.yaml.
Enhances ESLint base and Vue configs with improved rules and parser options, including better globals and stricter TypeScript checks. Updates @typescript-eslint packages to 8.35.0 and aligns all TypeScript dependencies to ^5.5.0 across the repo for consistency.
Refactored VueCardStack component and related composables for improved code style and consistency. Added Prettier and TypeScript shared configs as workspace packages, updated Prettier and ESLint configs to use them, and introduced Prettier plugin for Vue. Updated package and lock files to reflect new dependencies and configuration structure.
Split Prettier configuration into base and Vue-specific configs. Updated package.json to use the new Vue config in the Vue package. Adjusted exports in tooling/prettier/package.json to expose both base and Vue configs, and removed the old index.mjs file.
Refined type definitions for Vue components and slots to use more accurate typings, such as VNode and Record<string, unknown>. Updated debounce and throttle utilities for better type safety. Also updated prettier-plugin-tailwindcss to v0.6.13 in tooling.
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: 1

🧹 Nitpick comments (2)
.gitignore (2)

3-4: Yarn-specific PnP artifacts probably unnecessary after the switch to pnpm
.pnp and .pnp.js are generated by Yarn Plug-n-Play. Given the repo now relies on pnpm workspaces, keeping these patterns may create confusion about the supported tooling.

-.pnp
-.pnp.js

10-12: Duplicate dist ignore rules – keep the recursive one for clarity
dist/ is fully covered by **/dist/. Dropping the top-level pattern reduces noise without changing behaviour.

-dist/
 **/dist/
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b357078 and cc08ec0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • .gitignore (1 hunks)
  • .vscode/extensions.json (1 hunks)
  • examples/vue/eslint.config.mjs (1 hunks)
  • examples/vue/package.json (1 hunks)
  • examples/vue/src/App.vue (1 hunks)
  • examples/vue/src/env.d.ts (1 hunks)
  • examples/vue/src/main.ts (1 hunks)
  • examples/vue/src/types.ts (1 hunks)
  • examples/vue/turbo.json (1 hunks)
  • package.json (1 hunks)
  • packages/vue/eslint.config.mjs (1 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/components/VueCardStack.vue (1 hunks)
  • packages/vue/src/components/VueCardStackDefineComponent.ts (1 hunks)
  • packages/vue/src/composables/useCardStack.ts (1 hunks)
  • packages/vue/src/composables/useDragHandling.ts (1 hunks)
  • packages/vue/src/composables/useStackCalculations.ts (1 hunks)
  • packages/vue/src/env.d.ts (1 hunks)
  • packages/vue/src/index.ts (1 hunks)
  • packages/vue/src/types/index.ts (1 hunks)
  • packages/vue/src/utils/debounce.ts (1 hunks)
  • packages/vue/turbo.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • tooling/eslint/base.js (1 hunks)
  • tooling/eslint/package.json (1 hunks)
  • tooling/eslint/vue.js (1 hunks)
  • tooling/prettier/index.mjs (1 hunks)
  • tooling/prettier/package.json (1 hunks)
  • tooling/prettier/tsconfig.json (1 hunks)
  • tooling/typescript/base.json (1 hunks)
  • tooling/typescript/package.json (1 hunks)
  • turbo.json (1 hunks)
✅ Files skipped from review due to trivial changes (14)
  • tooling/typescript/package.json
  • examples/vue/eslint.config.mjs
  • tooling/prettier/tsconfig.json
  • packages/vue/eslint.config.mjs
  • .vscode/extensions.json
  • tooling/eslint/base.js
  • tooling/typescript/base.json
  • tooling/prettier/index.mjs
  • packages/vue/turbo.json
  • turbo.json
  • tooling/eslint/vue.js
  • examples/vue/turbo.json
  • tooling/eslint/package.json
  • tooling/prettier/package.json
🚧 Files skipped from review as they are similar to previous changes (15)
  • examples/vue/src/main.ts
  • examples/vue/src/types.ts
  • examples/vue/src/env.d.ts
  • pnpm-workspace.yaml
  • packages/vue/package.json
  • packages/vue/src/components/VueCardStackDefineComponent.ts
  • packages/vue/src/composables/useDragHandling.ts
  • packages/vue/src/index.ts
  • packages/vue/src/composables/useStackCalculations.ts
  • examples/vue/package.json
  • examples/vue/src/App.vue
  • packages/vue/src/utils/debounce.ts
  • package.json
  • packages/vue/src/components/VueCardStack.vue
  • packages/vue/src/types/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/vue/src/composables/useCardStack.ts (4)
packages/vue/src/types/index.ts (4)
  • BaseCardData (6-9)
  • CardStackConfig (14-33)
  • InternalCard (38-61)
  • DragEvent (66-66)
packages/vue/src/composables/useStackCalculations.ts (1)
  • useStackCalculations (7-120)
packages/vue/src/composables/useDragHandling.ts (1)
  • useDragHandling (7-111)
packages/vue/src/utils/debounce.ts (1)
  • debounce (9-24)
🔇 Additional comments (14)
.gitignore (1)

21-24: Verify that the broad .env* ignore does not block checked-in template files
The pattern ignores every .env*, then re-includes only .env.example.
If you need other templates such as .env.development.example, add explicit negations now to avoid accidental omission.

packages/vue/src/composables/useCardStack.ts (12)

1-5: LGTM: Clean and well-organized imports.

The imports are well-structured with clear separation between types, utilities, composables, and Vue APIs. All imports appear to be used within the composable.


7-15: LGTM: Well-designed function signature with good TypeScript generics.

The function signature properly uses generics to extend BaseCardData and accepts appropriate reactive references. The emit parameter could potentially use Vue's Emits type for stronger typing, but the current approach is acceptable and functional.


16-25: LGTM: Efficient reactive state management.

Good use of shallowRef for the stack array to optimize performance since deep reactivity isn't needed for the card objects. The composable initialization follows Vue 3 composition patterns well.


49-62: LGTM: Proper use of nextTick for DOM-dependent calculations.

The rebuild function correctly uses nextTick to ensure DOM updates are complete before recalculating card positions and properties. The spread operator usage preserves existing card data while updating calculated defaults.


64-72: LGTM: Efficient resize handling with appropriate debouncing.

The debounced resize handler with 250ms delay is well-implemented to prevent excessive calculations during window resizing. The logic correctly updates the width and triggers a rebuild.


74-94: LGTM: Clean implementation of circular navigation.

The navigation functions correctly implement circular card movement by shifting cards between the front and back of the stack array. The logic properly handles the rotation and triggers rebuild to update the visual state.


96-133: LGTM: Complex but well-structured drag update logic.

The moveStack function handles real-time drag updates effectively with appropriate calculations for position and scale transformations. The normalized progress emission is useful for external components to track drag state. The complex mathematical calculations for smooth drag transitions appear correct.


135-154: LGTM: Proper end-of-drag handling with threshold-based card switching.

The function correctly calculates the distance travelled and uses the shouldChangeCard logic to determine whether to switch cards or revert to the original position. Emitting move with 0 appropriately resets the progress state.


156-181: LGTM: Proper event listener management for drag interactions.

The event handlers correctly manage document-level event listeners to handle cases where the mouse/touch moves outside the element during drag. The conditional check in onDrag prevents errors from null drag data.


183-189: LGTM: Safe computed property with proper fallback handling.

The originalActiveCardIndex computed property correctly maps from the internal stack index to the original card index with appropriate null safety using optional chaining and a sensible fallback value.


191-208: LGTM: Comprehensive lifecycle management with proper cleanup.

The lifecycle hooks properly initialize the component and handle all event listener registration and cleanup. The conditional checks for elementRef.value provide appropriate safety, and all listeners added in onMounted are correctly removed in onBeforeUnmount.


210-231: LGTM: Well-structured API with appropriate encapsulation.

The return object provides a clean, well-organized API that exposes necessary state and methods while keeping internal complexity private. The commented sections make it easy for consumers to understand what's available.

packages/vue/src/env.d.ts (1)

1-5: LGTM: Standard TypeScript declaration for Vue SFC support.

This is a proper TypeScript module declaration that enables TypeScript recognition of .vue files. The DefineComponent typing with Record<string, unknown> provides appropriate flexibility for generic Vue component imports while maintaining type safety.

Comment on lines 30 to 47
const init = () => {
// Move bottom card to top of stack (positioned offscreen)
const cardsCopy = [...cards.value]
const lastCard = cardsCopy.pop()
if (lastCard) {
cardsCopy.unshift(lastCard)
}

stack.value = cardsCopy.map((card, index) => {
const defaults = calculations.cardDefaults.value[index]
return {
_id: Date.now() + index,
_index: index,
...card,
...defaults
} as T & InternalCard
})
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Suggest improving ID generation to avoid potential duplicates.

The card rotation logic is correct, but using Date.now() + index for ID generation could create duplicate IDs if the function is called multiple times within the same millisecond or if there are timing issues.

Consider using a more robust ID generation approach:

-        _id: Date.now() + index,
+        _id: crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`,

Or use a simple counter for sequential IDs if uniqueness within the component is sufficient.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const init = () => {
// Move bottom card to top of stack (positioned offscreen)
const cardsCopy = [...cards.value]
const lastCard = cardsCopy.pop()
if (lastCard) {
cardsCopy.unshift(lastCard)
}
stack.value = cardsCopy.map((card, index) => {
const defaults = calculations.cardDefaults.value[index]
return {
_id: Date.now() + index,
_index: index,
...card,
...defaults
} as T & InternalCard
})
}
const init = () => {
// Move bottom card to top of stack (positioned offscreen)
const cardsCopy = [...cards.value]
const lastCard = cardsCopy.pop()
if (lastCard) {
cardsCopy.unshift(lastCard)
}
stack.value = cardsCopy.map((card, index) => {
const defaults = calculations.cardDefaults.value[index]
return {
_id: crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`,
_index: index,
...card,
...defaults
} as T & InternalCard
})
}
🤖 Prompt for AI Agents
In packages/vue/src/composables/useCardStack.ts around lines 30 to 47, the
current ID generation using Date.now() + index can produce duplicate IDs if
called rapidly. Replace this with a more reliable method such as a UUID
generator or maintain a simple incrementing counter within the component to
ensure unique IDs for each card instance.

Swapped 'format' and 'format:fix' scripts for consistency in both example and package Vue projects. Improved formatting in VueCardStack and App.vue templates for readability. Updated VueCardStackDefineComponent for better type clarity. Adjusted ESLint config to disable formatting rules that conflict with Prettier.
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

🧹 Nitpick comments (1)
demos/vue/src/App.vue (1)

32-69: Consider improving template formatting for better readability.

While the template logic is correct and the scoped slots are properly implemented, the formatting has inconsistent spacing and unusual line breaks that make it harder to read.

Consider reformatting for better readability:

-<template>
-
-  <div class="app">
-
-    <h1>Vue Card Stack Demo</h1>
-     <VueCardStack
+<template>
+  <div class="app">
+    <h1>Vue Card Stack Demo</h1>
+    <VueCardStack
       :cards="cards"
       :stack-width="460"
       :card-width="300"
       :card-height="460"
       :max-visible-cards="6"
       @move="onMove"
-      > <template #card="{ card }"
-        >
-        <div class="card" :style="{ background: card.data.background }">
-
-          <p v-if="card.data.title"> {{ card.data.title }} </p>
-
-        </div>
-         </template
-      > <template #nav="{ activeCardIndex, onNext, onPrevious }"
-        >
+    >
+      <template #card="{ card }">
+        <div class="card" :style="{ background: card.data.background }">
+          <p v-if="card.data.title">{{ card.data.title }}</p>
+        </div>
+      </template>
+      
+      <template #nav="{ activeCardIndex, onNext, onPrevious }">
         <nav class="nav">
-
-          <div class="counter"> {{ (activeCardIndex ?? 0) + 1 }}/{{ cards.length }} </div>
-           <button class="button" @click="onPrevious"> Previous </button> <button
-            class="button"
-            @click="onNext"
-          >
-             Next </button
-          >
+          <div class="counter">{{ (activeCardIndex ?? 0) + 1 }}/{{ cards.length }}</div>
+          <button class="button" @click="onPrevious">Previous</button>
+          <button class="button" @click="onNext">Next</button>
         </nav>
-         </template
-      > </VueCardStack
-    >
+      </template>
+    </VueCardStack>
   </div>
-
 </template>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc08ec0 and 81594b1.

⛔ Files ignored due to path filters (2)
  • demos/vue/public/vite.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • demos/vue/.gitignore (1 hunks)
  • demos/vue/eslint.config.mjs (1 hunks)
  • demos/vue/index.html (1 hunks)
  • demos/vue/package.json (1 hunks)
  • demos/vue/src/App.vue (1 hunks)
  • demos/vue/src/main.ts (1 hunks)
  • demos/vue/src/types.ts (1 hunks)
  • demos/vue/src/vite-env.d.ts (1 hunks)
  • demos/vue/tsconfig.app.json (1 hunks)
  • demos/vue/tsconfig.json (1 hunks)
  • demos/vue/tsconfig.node.json (1 hunks)
  • demos/vue/vite.config.ts (1 hunks)
  • packages/vue/package.json (1 hunks)
  • packages/vue/src/components/VueCardStack.vue (1 hunks)
  • packages/vue/src/components/VueCardStackDefineComponent.ts (1 hunks)
  • packages/vue/src/index.ts (1 hunks)
  • packages/vue/tsconfig.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
  • tooling/eslint/vue.js (1 hunks)
✅ Files skipped from review due to trivial changes (8)
  • demos/vue/src/vite-env.d.ts
  • demos/vue/tsconfig.json
  • demos/vue/vite.config.ts
  • demos/vue/src/types.ts
  • demos/vue/index.html
  • demos/vue/.gitignore
  • demos/vue/eslint.config.mjs
  • demos/vue/package.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/vue/tsconfig.json
  • pnpm-workspace.yaml
  • packages/vue/src/index.ts
  • tooling/eslint/vue.js
  • packages/vue/src/components/VueCardStackDefineComponent.ts
  • packages/vue/package.json
  • packages/vue/src/components/VueCardStack.vue
🧰 Additional context used
🪛 Biome (1.9.4)
demos/vue/tsconfig.app.json

[error] 6-6: JSON standard does not allow comments.

(parse)


[error] 7-7: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 7-7: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 7-7: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 7-7: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 8-8: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 8-8: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 8-8: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 8-8: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 9-9: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 9-9: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 9-9: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 9-9: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

demos/vue/tsconfig.node.json

[error] 9-9: JSON standard does not allow comments.

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: JSON standard does not allow comments.

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

🔇 Additional comments (5)
demos/vue/src/main.ts (1)

1-5: Clean and standard Vue 3 entry point implementation.

The bootstrap code follows Vue 3 best practices with proper imports and app mounting. The implementation is minimal and correct.

demos/vue/tsconfig.app.json (1)

1-15: Well-configured TypeScript setup with appropriate strict settings.

The configuration properly extends the Vue base config and enables strict type checking options that will help maintain code quality. The build info caching and file inclusion patterns are correctly set up.

Note: The static analysis errors about JSON comments are false positives - TypeScript config files support comments (JSONC format).

demos/vue/tsconfig.node.json (1)

1-25: Appropriate TypeScript configuration for bundler/build tooling.

The configuration correctly targets ES2023 with bundler mode settings and strict linting options. Including only the Vite config file is appropriate for this Node.js-specific TypeScript config.

demos/vue/src/App.vue (2)

1-30: Excellent use of Vue 3 Composition API with strong TypeScript typing.

The component demonstrates best practices with:

  • Proper use of <script setup lang="ts">
  • Strongly typed component creation via generic factory function
  • Reactive data management with ref()
  • Clean event handling

The implementation showcases the new Vue 3 card stack component effectively.


71-123: Clean and well-structured CSS styles.

The styles provide good visual design with:

  • Proper flexbox layout for centering
  • Consistent spacing and typography
  • Good button hover states
  • Appropriate border radius and colors

The styling effectively supports the card stack demo functionality.

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: 6

♻️ Duplicate comments (3)
packages/vue/src/composables/useDragHandling.ts (2)

156-166: Critical: Sensitivity calculation is inverted.

This issue was previously identified and remains unaddressed. The current formula produces counterintuitive behavior where higher sensitivity values require MORE drag distance instead of LESS.


185-194: Same inverted sensitivity calculation in getThresholdDistance.

This computed property has the same inverted sensitivity calculation issue.

Apply this diff to fix both occurrences:

 const getThresholdDistance = computed((): number => {
   try {
     return (
-      (config.value.cardWidth + config.value.paddingHorizontal) / (1 / config.value.sensitivity)
+      (config.value.cardWidth + config.value.paddingHorizontal) * config.value.sensitivity
     )
   } catch (error) {
packages/vue/src/composables/useCardStack.ts (1)

129-137: Improve ID generation to avoid potential duplicates.

While adding Math.random() reduces collision risk, it's still not guaranteed to be unique. Consider using a more robust approach.

Apply this diff for more reliable ID generation:

-        _id: Date.now() + index + Math.random(), // Ensure unique IDs
+        _id: card._id ?? `${Date.now()}-${index}-${Math.random().toString(36).substr(2, 9)}`,

Or use a UUID if available:

-        _id: Date.now() + index + Math.random(), // Ensure unique IDs
+        _id: card._id ?? (crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`),
🧹 Nitpick comments (11)
tooling/eslint/package.json (1)

41-44: Consider adding npm to the engines block

Tooling users on plain npm will not receive an engine-mismatch warning and may unknowingly use an older runtime.

   "engines": {
     "node": ">=18.0.0",
-    "pnpm": ">=8.0.0"
+    "pnpm": ">=8.0.0",
+    "npm": ">=9.0.0"
   },
tooling/typescript/base.json (2)

5-13: allowJs + checkJs + strict flags will surface thousands of JS errors

With allowJs: true, all .js files in the workspace are type-checked under strict mode.
Unless every package is 100 % TS-compatible JavaScript, this will be extremely noisy and slow incremental builds.

Consider:

-    "allowJs": true,
-    "checkJs": true,
+    // JS is compiled only when a package explicitly opts-in
+    "allowJs": false,
+    "checkJs": false,

or documenting an opt-in per-package override.


6-7: Missing Node types from lib array

"lib": ["DOM", ...] omits "DOM.Iterable","ES2022.Array", and most importantly "DOM" includes no Node globals. Any back-end or build-tooling file that uses process, Buffer`, etc. will lack type definitions.

If the base config is meant for both browser and tooling code, append "DOM","DOM.Iterable","ES2022","ESNext.AsyncIterable","DOM" ,"DOM.Iterable","ES2022","ESNext.AsyncIterable","DOM","ES2022"] maybe mixture.

Better: split into browser vs Node presets.

tooling/typescript/package.json (1)

30-37: Export does not expose the preset at package root

Consumers commonly import as:

{
  "extends": "@card-stack/tsconfig"
}

That requires the root export (".") to resolve to the config file:

   "exports": {
-    "./base.json": "./base.json",
+    ".": "./base.json",
+    "./base.json": "./base.json",
     "./package.json": "./package.json"
   },
tooling/eslint/README.md (1)

148-151: Markdown-lint: MD036 – emphasis used as heading

**Made with ❤️ by ReactivePixels** triggers MD036.
If you want a separator/header style, use an <h3> or plain text to avoid automated lint failures.

-**Made with ❤️ by ReactivePixels**
+### Made with ❤️ by ReactivePixels
tooling/typescript/README.md (1)

152-152: Add hyphen to "OS-specific" for grammatical correctness.

When used as a modifier, "OS-specific" should be hyphenated.

-- IDE and OS specific files
+- IDE and OS-specific files
packages/vue/src/components/VueCardStack.vue (3)

73-89: Consider reactive prop validation

The prop validation only runs once during setup. Consider moving this to a watchEffect or computed property to ensure props remain valid if they change dynamically.

-// Validate props on setup
-try {
-  validateProps()
-} catch (err) {
-  error.value = err as Error
-}
+// Validate props reactively
+watchEffect(() => {
+  try {
+    validateProps()
+    error.value = null
+  } catch (err) {
+    error.value = err as Error
+  }
+})

145-152: Complete keyboard navigation implementation

The Home and End key handlers are not implemented. These are important for accessibility.

Would you like me to implement the navigation to first/last card functionality or create an issue to track this?

 case 'Home':
   event.preventDefault()
-  // Move to first card (implement if needed)
+  // Navigate to first card
+  void onFirst?.() // Needs to be exposed from useCardStack
   break
 case 'End':
   event.preventDefault()
-  // Move to last card (implement if needed)
+  // Navigate to last card  
+  void onLast?.() // Needs to be exposed from useCardStack
   break

223-228: Simplify card data transformation for better readability

The inline destructuring and spread operation is complex. Consider extracting this to a computed property or method for clarity.

-:card="{
-  ...card,
-  $index: index,
-  data: (({ _id, _index, ...rest }) => rest)(card)
-}"
+:card="getCardSlotProps(card, index)"

Add this method to the script:

const getCardSlotProps = (card: InternalCard, index: number) => ({
  ...card,
  $index: index,
  data: omit(card, ['_id', '_index'])
})
packages/vue/README.md (2)

6-7: Use proper heading instead of bold text

For better document structure and accessibility, use a proper heading tag instead of bold emphasis.

-
-**A performant, accessible card stack component for Vue 3 with drag-to-swipe functionality**
-
+
+### A performant, accessible card stack component for Vue 3 with drag-to-swipe functionality
+

637-637: Update donation link placeholder

The "Buy us a coffee" link contains a placeholder username.

-- ☕ [Buying us a coffee](https://buymeacoffee.com/your-username)
+- ☕ [Buying us a coffee](https://buymeacoffee.com/reactivepixels)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69ff7a6 and 4608ed3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (29)
  • .editorconfig (1 hunks)
  • .vscode/extensions.json (1 hunks)
  • demos/vue/eslint.config.mjs (1 hunks)
  • demos/vue/src/App.vue (1 hunks)
  • demos/vue/src/main.ts (1 hunks)
  • package.json (1 hunks)
  • packages/vue/.eslintignore (1 hunks)
  • packages/vue/README.md (1 hunks)
  • packages/vue/eslint.config.mjs (1 hunks)
  • packages/vue/src/components/VueCardStack.vue (1 hunks)
  • packages/vue/src/components/VueCardStackDefineComponent.ts (1 hunks)
  • packages/vue/src/composables/useCardStack.ts (1 hunks)
  • packages/vue/src/composables/useDragHandling.ts (1 hunks)
  • packages/vue/src/composables/useStackCalculations.ts (1 hunks)
  • packages/vue/src/index.ts (1 hunks)
  • packages/vue/src/types/index.ts (1 hunks)
  • packages/vue/src/utils/debounce.ts (1 hunks)
  • packages/vue/vite.config.ts (1 hunks)
  • tooling/eslint/README.md (1 hunks)
  • tooling/eslint/base.js (1 hunks)
  • tooling/eslint/package.json (1 hunks)
  • tooling/eslint/vue.js (1 hunks)
  • tooling/prettier/README.md (1 hunks)
  • tooling/prettier/index.mjs (1 hunks)
  • tooling/prettier/package.json (1 hunks)
  • tooling/typescript/README.md (1 hunks)
  • tooling/typescript/base.json (1 hunks)
  • tooling/typescript/package.json (1 hunks)
  • turbo.json (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/vue/.eslintignore
  • .editorconfig
🚧 Files skipped from review as they are similar to previous changes (9)
  • .vscode/extensions.json
  • demos/vue/eslint.config.mjs
  • packages/vue/eslint.config.mjs
  • packages/vue/src/components/VueCardStackDefineComponent.ts
  • demos/vue/src/main.ts
  • turbo.json
  • package.json
  • packages/vue/vite.config.ts
  • demos/vue/src/App.vue
🧰 Additional context used
🧬 Code Graph Analysis (4)
packages/vue/src/composables/useDragHandling.ts (2)
packages/vue/src/index.ts (1)
  • useDragHandling (58-58)
packages/vue/src/types/index.ts (3)
  • CardStackConfig (36-104)
  • DragEvent (141-141)
  • DragData (244-249)
packages/vue/src/composables/useStackCalculations.ts (2)
packages/vue/src/index.ts (1)
  • useStackCalculations (59-59)
packages/vue/src/types/index.ts (2)
  • BaseCardData (17-28)
  • CardStackConfig (36-104)
packages/vue/src/composables/useCardStack.ts (5)
packages/vue/src/index.ts (4)
  • useCardStack (57-57)
  • useStackCalculations (59-59)
  • useDragHandling (58-58)
  • debounce (62-62)
packages/vue/src/types/index.ts (5)
  • BaseCardData (17-28)
  • CardStackConfig (36-104)
  • InternalCard (112-135)
  • CARD_STACK_CONSTANTS (254-281)
  • DragEvent (141-141)
packages/vue/src/composables/useStackCalculations.ts (1)
  • useStackCalculations (34-316)
packages/vue/src/composables/useDragHandling.ts (1)
  • useDragHandling (29-234)
packages/vue/src/utils/debounce.ts (1)
  • debounce (47-86)
packages/vue/src/utils/debounce.ts (1)
packages/vue/src/index.ts (2)
  • debounce (62-62)
  • throttle (62-62)
🪛 Biome (1.9.4)
tooling/eslint/vue.js

[error] 79-79: This property value named vue/prefer-template is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named vue/prefer-template

(lint/suspicious/noDuplicateObjectKeys)

🪛 markdownlint-cli2 (0.17.2)
packages/vue/README.md

7-7: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

tooling/eslint/README.md

150-150: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

tooling/prettier/README.md

214-214: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

🪛 LanguageTool
tooling/typescript/README.md

[uncategorized] ~152-~152: When ‘OS-specific’ is used as a modifier, it is usually spelled with a hyphen.
Context: ...s - Cache and temporary files - IDE and OS specific files ## Vue 3 Integration ### JSX Co...

(SPECIFIC_HYPHEN)

🔇 Additional comments (10)
tooling/eslint/package.json (1)

45-48: ```shell
#!/bin/bash
set -e

echo "eslint-plugin-vue@latest peerDependencies:"
curl -s https://registry.npmjs.org/eslint-plugin-vue/latest | jq '.peerDependencies'

echo -e "\neslint-config-prettier@latest peerDependencies:"
curl -s https://registry.npmjs.org/eslint-config-prettier/latest | jq '.peerDependencies'

echo -e "\n@eslint/js@latest peerDependencies:"
curl -s https://registry.npmjs.org/@eslint/js/latest | jq '.peerDependencies'


</details>
<details>
<summary>tooling/prettier/package.json (1)</summary>

`44-50`: **Verify plugin compatibility with Prettier 3**

`@trivago/prettier-plugin-sort-imports@5.2.2` has not released a Prettier 3 compatible build at the time of writing. Projects will error with:

Error: Cannot find module 'prettier/parser' …


Double-check the plugin’s changelog or lock to Prettier 2 until an update lands.

</details>
<details>
<summary>tooling/prettier/README.md (1)</summary>

`1-215`: **LGTM! Comprehensive and well-structured documentation.**

The README provides excellent coverage of the Prettier configuration features, usage, and integration options. The documentation is clear and includes helpful examples for various use cases.

</details>
<details>
<summary>tooling/typescript/README.md (1)</summary>

`1-255`: **Excellent TypeScript configuration documentation!**

The README provides thorough coverage of the TypeScript configuration features, strict mode benefits, performance optimizations, and Vue 3 integration. The examples and troubleshooting sections are particularly helpful.

</details>
<details>
<summary>tooling/prettier/index.mjs (1)</summary>

`1-127`: **Well-crafted Prettier configuration with excellent documentation!**

The configuration provides sensible defaults and comprehensive import sorting rules. The language-specific overrides are appropriate, and the JSDoc documentation clearly explains the purpose and features.

</details>
<details>
<summary>packages/vue/src/composables/useDragHandling.ts (1)</summary>

`1-235`: **Good drag handling implementation with comprehensive error handling.**

Apart from the sensitivity calculation issue, the composable is well-structured with:
- Proper touch/mouse event unification
- Comprehensive error handling
- Clear documentation
- Performance optimizations

Once the sensitivity calculation is fixed, this will be a solid implementation.

</details>
<details>
<summary>packages/vue/src/composables/useStackCalculations.ts (2)</summary>

`160-175`: **Great job fixing the division by zero issue!**

The guard condition `if (visibleCards <= 2) return 0` properly prevents the division by zero error that was previously identified. The implementation is now safe and handles edge cases correctly.

---

`1-317`: **Excellent stack calculations implementation!**

This composable demonstrates high-quality code with:
- Comprehensive error handling throughout
- Performance optimizations with caching and memoization
- Clear, detailed documentation
- Well-structured computed properties
- Sound mathematical calculations

The calculations for card positioning, scaling, and visibility are well-thought-out and properly implemented.

</details>
<details>
<summary>tooling/eslint/base.js (1)</summary>

`1-190`: **Well-structured ESLint configuration with modern best practices!**

The configuration properly uses the modern `projectService` approach for type-aware linting and includes comprehensive rules for code quality, performance, and maintainability. The placement of Prettier config at the end ensures proper rule override precedence.

</details>
<details>
<summary>packages/vue/src/utils/debounce.ts (1)</summary>

`1-170`: **Excellent implementation of debounce and throttle utilities!**

The functions are well-documented, properly typed with generics, include comprehensive input validation, and provide useful cancel/flush methods for advanced control. The implementations follow standard patterns correctly.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +50 to +56
"@eslint/js": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^2.2.3",
"eslint-plugin-vue": "^9.33.0",
"globals": "^15.12.0",
"typescript-eslint": "^8.15.0"
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect typescript-eslint package name

There is no package published simply as typescript-eslint.
The maintained packages are scoped under @typescript-eslint/* (e.g. @typescript-eslint/eslint-plugin, @typescript-eslint/parser). Installing the current entry will resolve to an unmaintained placeholder and break consumers that rely on the rules/parser.

-    "typescript-eslint": "^8.15.0"
+    "@typescript-eslint/eslint-plugin": "^8.15.0",
+    "@typescript-eslint/parser": "^8.15.0"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@eslint/js": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^2.2.3",
"eslint-plugin-vue": "^9.33.0",
"globals": "^15.12.0",
"typescript-eslint": "^8.15.0"
},
"@eslint/js": "^9.15.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^2.2.3",
"eslint-plugin-vue": "^9.33.0",
"globals": "^15.12.0",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"@typescript-eslint/parser": "^8.15.0"
🤖 Prompt for AI Agents
In tooling/eslint/package.json around lines 50 to 56, the dependency
"typescript-eslint" is incorrectly named and does not exist as a standalone
package. Replace "typescript-eslint" with the correct scoped packages such as
"@typescript-eslint/eslint-plugin" and/or "@typescript-eslint/parser" depending
on what is needed for your ESLint setup to ensure proper functionality and avoid
breaking consumers.

"vue/no-useless-v-bind": "error",
"vue/no-v-text-v-html-on-component": "error",
"vue/padding-line-between-blocks": "error",
"vue/prefer-template": "error",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate property definition.

This vue/prefer-template rule is defined again at line 143, making this declaration redundant.

Apply this diff to remove the duplicate:

-      "vue/prefer-template": "error",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"vue/prefer-template": "error",
🧰 Tools
🪛 Biome (1.9.4)

[error] 79-79: This property value named vue/prefer-template is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named vue/prefer-template

(lint/suspicious/noDuplicateObjectKeys)

🤖 Prompt for AI Agents
In tooling/eslint/vue.js at line 79, the rule "vue/prefer-template" is defined
twice, once here and again at line 143. Remove the duplicate definition at line
79 to avoid redundancy and potential conflicts, keeping only the definition at
line 143.

Comment on lines +82 to +99
export const VueCardStackPlugin = {
/**
* Install the plugin by registering the VueCardStack component globally.
*
* @param app - The Vue application instance
* @param options - Optional configuration for the plugin
*/
install: (app: App, options?: { componentName?: string }) => {
const componentName = options?.componentName ?? 'VueCardStack'

try {
app.component(componentName, VueCardStack)
} catch (error) {
console.error(`Failed to register ${componentName} component:`, error)
throw new Error(`VueCardStackPlugin installation failed: ${String(error)}`)
}
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider more graceful error handling for plugin installation.

Throwing an error during plugin installation might be too aggressive and could break the entire application initialization. Consider logging a warning instead of throwing, as is common practice with Vue plugins.

Apply this diff for more graceful error handling:

 install: (app: App, options?: { componentName?: string }) => {
   const componentName = options?.componentName ?? 'VueCardStack'

   try {
     app.component(componentName, VueCardStack)
   } catch (error) {
     console.error(`Failed to register ${componentName} component:`, error)
-    throw new Error(`VueCardStackPlugin installation failed: ${String(error)}`)
+    // Don't throw to avoid breaking the entire app initialization
+    console.warn(`VueCardStackPlugin: Component registration failed but plugin installation will continue`)
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const VueCardStackPlugin = {
/**
* Install the plugin by registering the VueCardStack component globally.
*
* @param app - The Vue application instance
* @param options - Optional configuration for the plugin
*/
install: (app: App, options?: { componentName?: string }) => {
const componentName = options?.componentName ?? 'VueCardStack'
try {
app.component(componentName, VueCardStack)
} catch (error) {
console.error(`Failed to register ${componentName} component:`, error)
throw new Error(`VueCardStackPlugin installation failed: ${String(error)}`)
}
}
}
export const VueCardStackPlugin = {
/**
* Install the plugin by registering the VueCardStack component globally.
*
* @param app - The Vue application instance
* @param options - Optional configuration for the plugin
*/
install: (app: App, options?: { componentName?: string }) => {
const componentName = options?.componentName ?? 'VueCardStack'
try {
app.component(componentName, VueCardStack)
} catch (error) {
console.error(`Failed to register ${componentName} component:`, error)
// Don't throw to avoid breaking the entire app initialization
console.warn(`VueCardStackPlugin: Component registration failed but plugin installation will continue`)
}
}
}
🤖 Prompt for AI Agents
In packages/vue/src/index.ts around lines 82 to 99, the plugin installation
currently throws an error if component registration fails, which can disrupt app
initialization. Modify the catch block to log a warning or error message without
throwing, allowing the app to continue running gracefully even if the plugin
fails to install.

Comment on lines +95 to +97
if (cfg.speed < 0.1 || cfg.speed > 5) {
throw new Error('Animation speed must be between 0.1 and 5 seconds')
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use constants for speed validation to maintain consistency.

The hardcoded value of 5 seconds doesn't match the CARD_STACK_CONSTANTS.MAX_SPEED which is 2.0 seconds according to the types file.

Apply this diff to use the constant:

-    if (cfg.speed < 0.1 || cfg.speed > 5) {
-      throw new Error('Animation speed must be between 0.1 and 5 seconds')
+    if (cfg.speed < CARD_STACK_CONSTANTS.MIN_SPEED || cfg.speed > CARD_STACK_CONSTANTS.MAX_SPEED) {
+      throw new Error(`Animation speed must be between ${CARD_STACK_CONSTANTS.MIN_SPEED} and ${CARD_STACK_CONSTANTS.MAX_SPEED} seconds`)
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (cfg.speed < 0.1 || cfg.speed > 5) {
throw new Error('Animation speed must be between 0.1 and 5 seconds')
}
if (cfg.speed < CARD_STACK_CONSTANTS.MIN_SPEED || cfg.speed > CARD_STACK_CONSTANTS.MAX_SPEED) {
throw new Error(`Animation speed must be between ${CARD_STACK_CONSTANTS.MIN_SPEED} and ${CARD_STACK_CONSTANTS.MAX_SPEED} seconds`)
}
🤖 Prompt for AI Agents
In packages/vue/src/composables/useCardStack.ts around lines 95 to 97, the speed
validation uses hardcoded values 0.1 and 5, but the maximum speed should match
the constant CARD_STACK_CONSTANTS.MAX_SPEED (2.0). Replace the hardcoded upper
limit 5 with CARD_STACK_CONSTANTS.MAX_SPEED to ensure consistency with the
defined constants.


<div align="center">

![Vue Card Stack Demo](https://raw.githubusercontent.com/your-username/vue-card-stack/main/demo.gif)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace placeholder URLs with actual links

The README contains placeholder URLs that need to be updated before release:

  • Demo GIF URL (line 5)
  • Live demo link (line 14)
  • Documentation link (line 14)
  • Examples link (line 14)
-![Vue Card Stack Demo](https://raw.githubusercontent.com/your-username/vue-card-stack/main/demo.gif)
+![Vue Card Stack Demo](https://raw.githubusercontent.com/rodleviton/vue-card-stack/main/demo.gif)

-[Live Demo](https://your-demo-link.com) | [Documentation](https://your-docs-link.com) | [Examples](https://github.com/your-username/vue-card-stack/tree/main/examples)
+[Live Demo](https://rodleviton.github.io/vue-card-stack/) | [Documentation](https://rodleviton.github.io/vue-card-stack/docs) | [Examples](https://github.com/rodleviton/vue-card-stack/tree/main/examples)

Also applies to: 14-14

🤖 Prompt for AI Agents
In packages/vue/README.md at lines 5 and 14, replace all placeholder URLs with
the actual, correct links for the demo GIF, live demo, documentation, and
examples. Update the demo GIF URL on line 5 and the live demo, documentation,
and examples URLs on line 14 to point to the real resources before release.

Comment on lines +190 to +198
*/
export interface NavSlotProps {
/** Current active card index (0-based) */
activeCardIndex: number
/** Function to move to the next card */
onNext: () => void
/** Function to move to the previous card */
onPrevious: () => void
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

NavSlotProps interface is incomplete

The NavSlotProps interface is missing properties that are actually passed to the navigation slot in the component (totalCards, isDragging).

 export interface NavSlotProps {
   /** Current active card index (0-based) */
   activeCardIndex: number
   /** Function to move to the next card */
   onNext: () => void
   /** Function to move to the previous card */
   onPrevious: () => void
+  /** Total number of cards in the stack */
+  totalCards: number
+  /** Whether a card is currently being dragged */
+  isDragging: boolean
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
*/
export interface NavSlotProps {
/** Current active card index (0-based) */
activeCardIndex: number
/** Function to move to the next card */
onNext: () => void
/** Function to move to the previous card */
onPrevious: () => void
}
export interface NavSlotProps {
/** Current active card index (0-based) */
activeCardIndex: number
/** Function to move to the next card */
onNext: () => void
/** Function to move to the previous card */
onPrevious: () => void
/** Total number of cards in the stack */
totalCards: number
/** Whether a card is currently being dragged */
isDragging: boolean
}
🤖 Prompt for AI Agents
In packages/vue/src/types/index.ts between lines 190 and 198, the NavSlotProps
interface is missing the totalCards and isDragging properties that are passed to
the navigation slot. Add totalCards as a number and isDragging as a boolean to
the interface to accurately reflect all props provided to the navigation slot.

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