From 6801e106b83608e4449d37a7c30f56b316946ddc Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 19 Aug 2025 20:43:36 +0200 Subject: [PATCH] Feat: Cursor code rules (#5246) --- .cursor/mcp.json | 18 ++ .cursor/rules/frontend-core.mdc | 225 ++++++++++++++++++ .cursor/rules/graphql-patterns.mdc | 93 ++++++++ .cursor/rules/tailwind-design-system.mdc | 46 ++++ .cursor/rules/vue-patterns.mdc | 82 +++++++ .../.cursor/rules/nuxt-patterns.mdc | 99 ++++++++ .../.cursor/rules/component-library.mdc | 147 ++++++++++++ 7 files changed, 710 insertions(+) create mode 100644 .cursor/mcp.json create mode 100644 .cursor/rules/frontend-core.mdc create mode 100644 .cursor/rules/graphql-patterns.mdc create mode 100644 .cursor/rules/tailwind-design-system.mdc create mode 100644 .cursor/rules/vue-patterns.mdc create mode 100644 packages/frontend-2/.cursor/rules/nuxt-patterns.mdc create mode 100644 packages/ui-components/.cursor/rules/component-library.mdc diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 000000000..246bd92ce --- /dev/null +++ b/.cursor/mcp.json @@ -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" + ] + } + } +} diff --git a/.cursor/rules/frontend-core.mdc b/.cursor/rules/frontend-core.mdc new file mode 100644 index 000000000..3bb3de5f7 --- /dev/null +++ b/.cursor/rules/frontend-core.mdc @@ -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 diff --git a/.cursor/rules/graphql-patterns.mdc b/.cursor/rules/graphql-patterns.mdc new file mode 100644 index 000000000..6fccd9903 --- /dev/null +++ b/.cursor/rules/graphql-patterns.mdc @@ -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 + } + } + `) +) +``` diff --git a/.cursor/rules/tailwind-design-system.mdc b/.cursor/rules/tailwind-design-system.mdc new file mode 100644 index 000000000..ee7dd6980 --- /dev/null +++ b/.cursor/rules/tailwind-design-system.mdc @@ -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 diff --git a/.cursor/rules/vue-patterns.mdc b/.cursor/rules/vue-patterns.mdc new file mode 100644 index 000000000..18c4d58c0 --- /dev/null +++ b/.cursor/rules/vue-patterns.mdc @@ -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 + + + +``` + +## Script Setup Organization + +Organize ` +``` + +## 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 = { + 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 + +export const Primary: Story = { + args: { + variant: 'primary' + } +} + +export const AllVariants: Story = { + render: () => ({ + components: { Button }, + template: ` +
+ + + +
+ ` + }) +} +``` + +### 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