159 lines
4.4 KiB
Markdown
159 lines
4.4 KiB
Markdown
|
|
# Agent Guidelines for Unboundedpress Dev
|
||
|
|
|
||
|
|
## Project Overview
|
||
|
|
|
||
|
|
This repository contains two main projects:
|
||
|
|
- **`portfolio-nuxt/`** - Primary Nuxt 3 application (Vue 3, TypeScript, Tailwind CSS, Pinia)
|
||
|
|
- **`portfolio/`** - Legacy Express.js application (plain JavaScript)
|
||
|
|
|
||
|
|
The Nuxt project is the main focus for development.
|
||
|
|
|
||
|
|
## Build Commands
|
||
|
|
|
||
|
|
### portfolio-nuxt (Primary Project)
|
||
|
|
```bash
|
||
|
|
cd portfolio-nuxt
|
||
|
|
npm install # Install dependencies
|
||
|
|
npm run dev # Development server
|
||
|
|
npm run build # Build for production
|
||
|
|
npm run generate # Generate static site
|
||
|
|
npm run preview # Preview production build
|
||
|
|
```
|
||
|
|
|
||
|
|
### portfolio (Legacy Project)
|
||
|
|
```bash
|
||
|
|
cd portfolio/src
|
||
|
|
npm run serve # Development with nodemon
|
||
|
|
npm run format # Format code
|
||
|
|
```
|
||
|
|
|
||
|
|
### Running a Single Test
|
||
|
|
**No test framework is currently configured.** If you add tests:
|
||
|
|
- For Nuxt: Use Vitest
|
||
|
|
- Run a single test: `npx vitest run --testNamePattern="test name"`
|
||
|
|
|
||
|
|
## Code Style Guidelines
|
||
|
|
|
||
|
|
### General Principles
|
||
|
|
- Use TypeScript in Vue components (`.vue` with `<script setup lang="ts">`)
|
||
|
|
- Use JavaScript (`.js`) in Pinia stores under `stores/`
|
||
|
|
- Follow Vue 3 Composition API with `<script setup>`
|
||
|
|
- Use Tailwind CSS classes for styling
|
||
|
|
|
||
|
|
### TypeScript Conventions
|
||
|
|
- Use `defineProps` and `defineEmits` with type-based syntax
|
||
|
|
- Use `withDefaults` for optional props with default values
|
||
|
|
- Prefer `ref` and `reactive` from Vue
|
||
|
|
- Use `toRefs` when destructuring props
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const props = withDefaults(
|
||
|
|
defineProps<{
|
||
|
|
modelValue?: boolean
|
||
|
|
persistent?: boolean
|
||
|
|
}>(),
|
||
|
|
{
|
||
|
|
modelValue: false,
|
||
|
|
persistent: false,
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
const emit = defineEmits<{
|
||
|
|
(e: 'update:modelValue', value: boolean): void
|
||
|
|
}>()
|
||
|
|
```
|
||
|
|
|
||
|
|
### Vue Component Structure
|
||
|
|
1. `<script setup lang="ts">` - Logic (imports, props, emits, composables)
|
||
|
|
2. `<template>` - Template with slots
|
||
|
|
3. `<style>` - Scoped styles (if needed, Tailwind preferred)
|
||
|
|
|
||
|
|
### Imports
|
||
|
|
- Use `@/` alias for imports (configured in Nuxt)
|
||
|
|
- Import Vue core: `import { ref, watch } from 'vue'`
|
||
|
|
- Import from stores: `import { useModalStore } from '@/stores/ModalStore'`
|
||
|
|
- Import icons: `import Icon from 'nuxt-icon'` or `<Icon name="..." />` in template
|
||
|
|
|
||
|
|
### File Naming
|
||
|
|
- Vue components: PascalCase (e.g., `IconButton.vue`, `Modal.vue`)
|
||
|
|
- Component folders: PascalCase with index.vue (e.g., `components/Modal/`)
|
||
|
|
- Stores: PascalCase with Store suffix (e.g., `AudioPlayerStore.js`, `ModalStore.js`)
|
||
|
|
- Pages: kebab-case (e.g., `index.vue`, `about-us.vue`)
|
||
|
|
|
||
|
|
### Tailwind CSS
|
||
|
|
- Use utility classes for all styling
|
||
|
|
- Follow existing patterns in components (see `components/Modal/Modal.vue`)
|
||
|
|
- Use `bg-`, `text-`, `p-`, `m-`, `flex-`, `grid-` prefixes
|
||
|
|
|
||
|
|
### State Management (Pinia)
|
||
|
|
- Create stores in `stores/` directory
|
||
|
|
- Use JavaScript (`.js`) for stores
|
||
|
|
- Follow the pattern in `stores/ModalStore.js`:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
import { defineStore } from "pinia"
|
||
|
|
|
||
|
|
export const useModalStore = defineStore("ModalStore", {
|
||
|
|
state: () => ({ key: "value" }),
|
||
|
|
actions: {
|
||
|
|
setKey(value) {
|
||
|
|
this.key = value
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### API Data Fetching
|
||
|
|
- Use Nuxt's `useFetch` and `useAsyncData` composables
|
||
|
|
- Transform data in the `transform` option
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { data: works } = await useFetch('https://unboundedpress.org/api/works', {
|
||
|
|
transform: (works) => {
|
||
|
|
// transformation logic
|
||
|
|
return transformedData
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Error Handling
|
||
|
|
- Handle null/undefined values in templates with `v-if` guards
|
||
|
|
- Use optional chaining (`?.`) when accessing nested properties
|
||
|
|
- Validate URLs before using them (see pattern in `pages/index.vue`)
|
||
|
|
|
||
|
|
### Head Management
|
||
|
|
Use `useHead` for SEO metadata:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
useHead({
|
||
|
|
titleTemplate: 'Site Title - %s'
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Transition and Animation
|
||
|
|
- Use Vue's built-in `<Transition>` and `<TransitionRoot>` components
|
||
|
|
- Use Headless UI for complex components (already installed)
|
||
|
|
|
||
|
|
## Additional Conventions
|
||
|
|
|
||
|
|
### Composables
|
||
|
|
- Place reusable logic in `composables/` directory
|
||
|
|
- Use the `use` prefix (e.g., `useModal`)
|
||
|
|
|
||
|
|
### Server Routes
|
||
|
|
- Place API routes in `server/api/` directory
|
||
|
|
- Use `.ts` files for TypeScript routes
|
||
|
|
|
||
|
|
### Image Handling
|
||
|
|
- Use `<NuxtImg>` component for optimized images
|
||
|
|
- Configure domains in `nuxt.config.ts` under `image.domains`
|
||
|
|
|
||
|
|
## Environment Variables
|
||
|
|
- Use `.env` files for local development
|
||
|
|
- Template available in `.env_template`
|
||
|
|
- Never commit secrets to version control
|
||
|
|
|
||
|
|
## Docker
|
||
|
|
- `Dockerfile` provided for deployment
|
||
|
|
- Multi-stage build available in `Dockerfile_upgrade`
|