Feat: Cursor code rules (#5246)

This commit is contained in:
Mike
2025-08-19 20:43:36 +02:00
committed by GitHub
parent 12f5165608
commit 6801e106b8
7 changed files with 710 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest",
"--browser",
"chrome",
"--output-dir",
"/tmp/playwright-mcp",
"--save-trace",
"--save-session",
"--port",
"8081"
]
}
}
}
+225
View File
@@ -0,0 +1,225 @@
---
description: Core frontend development patterns for Vue 3 + Nuxt 3 applications
globs: ['packages/frontend-2/**/*', 'packages/ui-components/**/*']
alwaysApply: false
---
# Frontend Development Rules
## Tech Stack
### Frontend App (packages/frontend-2)
- **Framework**: Nuxt 3 with Vue 3 composition API
- **Styling**: Tailwind CSS with custom theme from @speckle/tailwind-theme
- **State**: Vue composables, no Vuex/Pinia
- **Forms**: vee-validate for form validation
- **GraphQL**: Apollo Client with @vue/apollo-composable
- **Icons**: Lucide icons (Heroicons are deprecated)
- **Rich Text**: TipTap editor
- **Build**: Vite with TypeScript
### Component Library (packages/ui-components)
- **Framework**: Vue 3 with composition API
- **Build**: Vite with TypeScript for library builds
- **Styling**: Tailwind CSS with custom theme
- **Development**: Storybook for component development and documentation
## Code Style & Formatting
### TypeScript
- **Strict mode enabled** - Use strict TypeScript everywhere
- **ES2021 target** with modern features
- **Module resolution**: "bundler" for modern imports
- **Path aliases**: Use `~/` for Nuxt auto-imports in frontend-2
- **Import style**: Prefer named imports, consistent type imports
- **Type definitions**: Explicit types over `any`, use utility types
- **Type guards over type assertions** - Use type narrowing instead of `as` casting
- **Explicit return types** for exported/public functions
### Formatting & Linting Configuration
**For up-to-date formatting and linting rules, reference these files:**
- **Prettier configuration**: `.prettierrc`
- Contains formatting rules for quotes, semicolons, indentation, print width
- Enforced via pre-commit hooks and CI
- **ESLint configurations**:
- **Frontend-2**: `packages/frontend-2/eslint.config.mjs`
- **UI Components**: `packages/ui-components/eslint.config.mjs`
- **Base config**: `eslint.config.mjs` (shared rules)
- **TypeScript configuration**:
- **Frontend-2**: `packages/frontend-2/tsconfig.json`
- **UI Components**: `packages/ui-components/tsconfig.json`
### Constants and Magic Strings
- **Use enums or constants** instead of duplicating string literals
- **Co-locate types** with implementations when not domain-specific
- **Object parameters** over positional parameters for functions
- **First parameter** for main params, **second optional** for options
### Icons
- **Always use Lucide icons** - Heroicons are deprecated
- **Import from** `lucide-vue-next` package
- **Consistent sizing** using Tailwind classes
## File & Directory Conventions
### Naming
- **kebab-case** for file names
- **PascalCase** for Vue component files
- **camelCase** for TypeScript/JavaScript files
- **kebab-case** for directories
### Path Resolution & Imports
- **Workspace packages**: `@speckle/package-name`
- **Frontend-2 paths**: Use `~/` for Nuxt auto-imports and absolute paths
- **Type imports**: Use `type` keyword for type-only imports
- **Always use alias imports** - Never use relative paths
```typescript
// 1. Node modules
import { computed, ref } from 'vue'
import { useQuery } from '@vue/apollo-composable'
// 2. Internal packages
import { type Nullable } from '@speckle/shared'
import { FormButton } from '@speckle/ui-components'
// 3. Local imports
import { useProjectData } from '~/lib/projects/composables'
import type { ProjectFragment } from '~/lib/common/generated/gql/graphql'
```
## Common Patterns & Decision Making
### When Creating New Files
- **Components**: Use PascalCase, place in feature-based directories
- **Composables**: Use camelCase with `use` prefix, group by domain
- **Types**: Co-locate with implementation unless domain-specific
- **Always check existing patterns** before creating new ones
### Component Composition Patterns
- **Small, focused components** over large multi-purpose ones
- **Props for data down**, **emits for events up**
- **Composables for logic sharing** between components
- **Fragments for data requirements** rather than over-fetching
### Import Resolution Priority
1. **Workspace packages** (`@speckle/package-name`)
2. **Nuxt auto-imports** (`~/lib/...`)
3. **Node modules** (external packages)
4. **Never use relative imports** beyond same directory
## Performance Guidelines
### General Performance Rules
- **Use `computed`** for derived reactive data
- **Use `ref`** for simple reactive values
- **Use `shallowRef`** for large objects that change by reference
- **Lazy loading** for routes and components
- **Virtual scrolling** for lists > 100 items
### Frontend-2 (Application Performance)
- **Image optimization** with Nuxt Image if relevant
- **Code splitting** at route level
- **Bundle analysis** to monitor size
### UI Components (Library Performance)
- **Efficient component composition** to avoid unnecessary re-renders
- **Prop validation** only in development mode
- **Minimal dependencies** to keep bundle size small
- **Tree-shakeable exports** for optimal bundling
## Logging Patterns
### Structured Logging
- **Structured logging** with Pino for production
- **useLogger()** composable for standard logging
- **useStrictLogger()** when you need guaranteed real Pino logger
- **devLog()** or **useDevLogger()** for development-only logging
- **Never use console.log** - use logging composables instead
- **Development logging** is automatically skipped in production
- **Log levels** appropriate to environment
- **Error context** for debugging
### Analytics & Tracking
- **Don't add Mixpanel events** unless specifically requested
- **Event naming convention**: Past tense with every first letter capitalized
- Examples: "Button Clicked", "Dialog Dismissed", "Form Submitted"
- **Use mixpanel composable** from `~/lib/core/composables/mp`
## Error Handling
### Frontend Applications
- **Try-catch** for async operations
- **Loading states** for all async actions
- **User feedback** for errors via toast/notification
- **Graceful degradation** when features fail
- **Error boundaries** for component-level error handling
## Development Workflow
### Code Quality
#### Pre-commit Checks
- **ESLint** must pass before commits
- **TypeScript** compilation without errors
- **Prettier** formatting enforced
- **Husky hooks** for automated checks
## Accessibility Guidelines
### Requirements
- **ARIA labels** for interactive elements
- **Keyboard navigation** support
- **Screen reader** compatibility
- **Color contrast** compliance when possible
- **Focus management** in modals/dialogs
## Working with This Codebase
### Key Principles
1. **Follow TypeScript strict mode** - No any types without good reason
2. **Use composition API** - Prefer composables over mixins
3. **Keep components focused** - Single responsibility principle
4. **Consider accessibility**
5. **Document complex logic** - Document business logic, but don't add redundant comments
6. **Follow existing patterns**
### When in Doubt
- **Check existing implementations** for similar functionality
- **Ask questions** in code reviews or discussions
- **Refer to the design system** before creating custom styles
- **Use the logging composables** instead of console.log
@.prettierrc
@eslint.config.mjs
@packages/frontend-2/eslint.config.mjs
@packages/ui-components/eslint.config.mjs
@packages/frontend-2/tsconfig.json
@packages/ui-components/tsconfig.json
@packages/frontend-2/composables/logging.ts
+93
View File
@@ -0,0 +1,93 @@
---
description: GraphQL patterns and fragment-based architecture
globs:
[
'packages/frontend-2/**/*.vue',
'packages/frontend-2/**/*.ts',
'packages/frontend-2/**/*gql*/**/*',
'packages/frontend-2/**/*generated*/**/*',
'packages/ui-components/**/*.vue',
'packages/ui-components/**/*.ts'
]
alwaysApply: false
---
# GraphQL Patterns
## Code Generation
- **Generated types** from GraphQL schema
- **Typed hooks** from @vue/apollo-composable
- **Fragment colocation** with components
- **Operation naming** follows schema conventions
## Fragment-Based Architecture
```typescript
// Define component data requirements via fragments
graphql(`
fragment SomeComponent_Project on Project {
id
name
# Only fields this component needs
}
`)
defineProps<{
project: SomeComponent_Project
}>()
```
## Query/Mutation Patterns
```typescript
// Use generated hooks
const { result, loading, error } = useQuery(SomeQuery)
const { mutate, loading: mutating } = useMutation(SomeMutation)
// Handle loading states
const isLoading = computed(() => loading.value || mutating.value)
```
## Data Requirements
- **Always include `id` field** in queries for Apollo cache management
- **Mutations return updated objects** instead of just success booleans
- **Use fragments** to define component data requirements
- **Fragment naming**: `{ComponentName}_{GraphQLType}`
## Examples
### Fragment Definition
```typescript
// UserCard.vue
graphql(`
fragment UserCard_User on User {
id
name
email
avatar
}
`)
defineProps<{
user: UserCard_User
}>()
```
### Query with Fragments
```typescript
// UsersPage.vue
const { result: usersResult } = useQuery(
graphql(`
query UsersPage_Users {
users {
id
...UserCard_User
}
}
`)
)
```
+46
View File
@@ -0,0 +1,46 @@
---
description: Tailwind design system and @speckle/tailwind-theme usage
globs:
[
'packages/frontend-2/**/*.vue',
'packages/frontend-2/**/*.tsx',
'packages/frontend-2/**/*.jsx',
'packages/ui-components/**/*.vue',
'packages/ui-components/**/*.tsx',
'packages/ui-components/**/*.jsx'
]
alwaysApply: false
---
# Design System & Styling
## Tailwind Theme (@speckle/tailwind-theme)
- **Custom design system** with predefined colors, typography, and components
- **CSS variables** for theme colors that support light/dark mode
- **Import plugin** from `@speckle/tailwind-theme` in your Tailwind config
- **Use semantic color classes** instead of arbitrary values
## Dynamic Configuration Reference
**For up-to-date class names and configuration, reference these files:**
- **Color system & CSS variables**: `packages/tailwind-theme/src/plugin.ts`
- Contains `lightThemeVariables` and `darkThemeVariables` with all available color tokens
- Shows exact CSS variable names and their values
- Defines custom utility classes like `.text-fancy-gradient`, `.simple-scrollbar`, `.grow-textarea`
- **Tailwind preset configuration**: `packages/tailwind-theme/src/preset.ts`
- Contains the complete Tailwind color mappings (foundation, foreground, primary, etc.)
- Defines typography settings and font configurations
- Shows available color variants and theme structure
## Usage Guidelines
- **Use semantic colors** instead of hardcoded hex values
- **Reference the actual config files** above for current available classes
- **Check plugin.ts** for custom utility classes and components
@packages/tailwind-theme/src/plugin.ts
@packages/tailwind-theme/src/preset.ts
+82
View File
@@ -0,0 +1,82 @@
---
description: Vue 3 composition API patterns and conventions
globs: ['packages/frontend-2/**/*.vue', 'packages/ui-components/**/*.vue']
alwaysApply: false
---
# Vue.js Patterns & Conventions
## Component Structure
```vue
<template>
<!-- Template content -->
</template>
<script setup lang="ts">
// Script content with composition API
</script>
<style>
/* Component styles */
</style>
```
## Script Setup Organization
Organize `<script setup>` blocks in this order:
1. **Type definitions, enums, constants** (e.g., GraphQL operations)
2. **defineEmits & defineProps**
3. **Invoked composables** (useGlobalToast, useForm, etc.)
4. **ref declarations**
5. **computed values**
6. **Function definitions**
7. **watch calls**
8. **Lifecycle hooks** (onMounted, etc.)
## Component Naming
- **PascalCase** for component names
- **Multi-word** component names preferred
- **Descriptive** names that indicate purpose
## Props & Emits
- **TypeScript interfaces** for props definition
- **defineProps<Interface>()** syntax
- **defineEmits<Interface>()** for type-safe events
- **Default values** using withDefaults() when needed
## Composables
- **Use prefix** for composables (useFeatureName)
- **Only invoke in setup scope** - Never in async handlers or outside Vue context
- **Return reactive refs** and computed values
- **Co-locate** related logic in composables
- **Extract** reusable logic into composables
## State Management
- **No global store** - use composables and provide/inject
- **All data must be reactive** - Use ref/computed, not plain constants
- **Reactive refs** for component state
- **Computed** for derived state
- **Watch** for side effects
## Template Rules
- **Single root node** - Always ensure one root element exists
- **No array indices as keys** - Use unique identifiers instead
- **Conditional root elements** must have v-else fallback
```vue
<!-- Bad: If !someCondition, there's no root node -->
<template>
<div v-if="someCondition" />
</template>
<!-- Good: There's always a root node no matter what -->
<template>
<div v-if="someCondition" />
<div v-else />
</template>
```
@@ -0,0 +1,99 @@
---
description: Nuxt 3 specific patterns and conventions for frontend-2
globs:
[
'**/*.vue',
'**/*.ts',
'**/*.js',
'**/pages/**/*',
'**/layouts/**/*',
'**/middleware/**/*',
'**/plugins/**/*',
'**/composables/**/*'
]
alwaysApply: false
---
# Nuxt 3 Application Patterns
## File Structure
```
packages/frontend-2/
├── components/ # Vue components by feature
├── composables/ # Vue composables
├── lib/ # Feature-specific logic
├── pages/ # Nuxt pages
├── layouts/ # Nuxt layouts
├── middleware/ # Nuxt middleware
└── plugins/ # Nuxt plugins
```
## Path Resolution
- **Use `~/` prefix** for all internal imports
- **Auto-imports**: Nuxt auto-imports composables, components, and utilities
- **No relative paths** - always use `~/` for absolute imports
```typescript
// Good: Use ~/
import { useProjectData } from '~/lib/projects/composables'
import { SomeUtility } from '~/lib/common/helpers'
// Bad: Relative paths
import { useProjectData } from '../../../lib/projects/composables'
```
## Nuxt Composables
### Server-Side Rendering
- **useLazyFetch** for data fetching that doesn't block navigation
- **$fetch** for programmatic requests
- **useNuxtData** to access cached fetch data
### Navigation
- **navigateTo** for programmatic navigation
- **NuxtLink** for declarative links
- **useRoute** for current route info
- **useRouter** for navigation methods
### State Management
- **useState** for cross-component reactive state
- **useNuxtApp** for app context
- **useCookie** for reactive cookies
## Plugin Patterns
```typescript
// plugins/example.client.ts (client-only)
export default defineNuxtPlugin(() => {
// Client-side initialization
})
// plugins/example.server.ts (server-only)
export default defineNuxtPlugin(() => {
// Server-side initialization
})
// plugins/example.ts (universal)
export default defineNuxtPlugin(() => {
// Runs on both client and server
})
```
## Middleware
```typescript
// middleware/auth.ts (route middleware)
export default defineNuxtRouteMiddleware((to, from) => {
// Route protection logic
})
// middleware/global.global.ts (global middleware)
export default defineNuxtRouteMiddleware((to, from) => {
// Runs on every route change
})
```
@@ -0,0 +1,147 @@
---
description: Component library patterns and Storybook conventions for ui-components
globs: ['**/*.vue', '**/*.ts', '**/*.js', '**/*.stories.*', '**/lib.ts']
alwaysApply: false
---
# Component Library Patterns
## File Structure
```
packages/ui-components/
├── src/
│ ├── components/ # Reusable UI components
│ ├── helpers/ # Component utilities
│ └── lib.ts # Main export file
```
## Component Development
### Reusable Components
- **Focus on reusability** - components should work in multiple contexts
- **Minimal external dependencies** - keep the library lightweight
- **Props-driven** - all variations should be controllable via props
- **No business logic** - components should be presentational
### Component Composition
- **Compound components** for complex UI patterns
- **Render props** or slots for customizable content
- **Headless patterns** where appropriate for maximum flexibility
```vue
<!-- Good: Flexible and reusable -->
<template>
<button :class="buttonClasses" :disabled="disabled" @click="$emit('click', $event)">
<Icon v-if="icon" :name="icon" />
<slot />
</button>
</template>
<script setup lang="ts">
interface Props {
variant?: 'primary' | 'secondary' | 'danger'
size?: 'sm' | 'md' | 'lg'
icon?: string
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md'
})
const emit = defineEmits<{
click: [event: MouseEvent]
}>()
</script>
```
## Storybook Development
### Story Organization
- **One story file per component**
- **Multiple variants** to showcase all component states
- **Interactive controls** for all props
- **Documentation** with usage examples
```typescript
// Button.stories.ts
import type { Meta, StoryObj } from '@storybook/vue3'
import Button from './Button.vue'
const meta: Meta<typeof Button> = {
title: 'Form/Button',
component: Button,
parameters: {
docs: {
description: {
component: 'Primary button component with multiple variants'
}
}
},
argTypes: {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary', 'danger']
}
}
}
export default meta
type Story = StoryObj<typeof meta>
export const Primary: Story = {
args: {
variant: 'primary'
}
}
export const AllVariants: Story = {
render: () => ({
components: { Button },
template: `
<div class="space-x-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
</div>
`
})
}
```
### Story Best Practices
- **Show all states** (default, hover, disabled, loading, etc.)
- **Responsive examples** for components that adapt to screen size
- **Accessibility testing** with a11y addon
- **Performance testing** for complex components
## Library Exports
### Main Export File
```typescript
// src/lib.ts
export { default as Button } from './components/Button.vue'
export { default as Input } from './components/Input.vue'
export { default as Modal } from './components/Modal.vue'
// Export types
export type { ButtonProps } from './components/Button.vue'
export type { InputProps } from './components/Input.vue'
// Export utilities
export * from './helpers/validation'
export * from './helpers/formatting'
```
### Tree-Shakeable Exports
- **Named exports only** - no default exports for the library
- **Separate type exports** - allow importing types without importing components
- **Utility separation** - helpers should be importable independently