Feat: Cursor code rules (#5246)
This commit is contained in:
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
`)
|
||||
)
|
||||
```
|
||||
@@ -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
|
||||
@@ -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>
|
||||
```
|
||||
Reference in New Issue
Block a user