Upgrade to Nuxt 4 and fix icon issues
- Upgrade from Nuxt 3.6.0 to Nuxt 4.3.1 - Replace nuxt-icon with @nuxt/icon (Nuxt 4 compatible) - Install missing icon collections (@iconify-json/ion, heroicons, etc.) - Fix icon colors: use style instead of color prop - Add AGENTS.md with coding guidelines - Fix icon alignment in index.vue (items-center)
This commit is contained in:
parent
72d2d67842
commit
61332c28ef
131
AGENTS.md
Normal file
131
AGENTS.md
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
# AGENTS.md - Agent Coding Guidelines
|
||||||
|
|
||||||
|
This document provides guidelines for agents working on this codebase.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
- **Framework**: Nuxt 3 (Vue 3)
|
||||||
|
- **Styling**: Tailwind CSS
|
||||||
|
- **State Management**: Pinia
|
||||||
|
- **UI Components**: Headless UI + Nuxt Icon
|
||||||
|
- **Image Handling**: @nuxt/image
|
||||||
|
- **TypeScript**: Enabled (tsconfig extends .nuxt/tsconfig.json)
|
||||||
|
- **Storybook**: Available for component documentation
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development
|
||||||
|
npm run dev # Start development server
|
||||||
|
npm run build # Build for production
|
||||||
|
npm run generate # Generate static site (SSG)
|
||||||
|
npm run preview # Preview production build
|
||||||
|
|
||||||
|
# No test framework configured
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### General Conventions
|
||||||
|
|
||||||
|
- Use Vue 3 Composition API with `<script setup lang="ts">`
|
||||||
|
- TypeScript is preferred for new files; stores use JavaScript (.js)
|
||||||
|
- Follow Nuxt 3 auto-import conventions (no explicit imports for composables, components, etc.)
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
/pages/ - Page components (file-based routing)
|
||||||
|
/components/ - Vue components (auto-imported)
|
||||||
|
/layouts/ - Layout components
|
||||||
|
/server/api/ - Server API routes (Nitro)
|
||||||
|
/stores/ - Pinia stores (.js files)
|
||||||
|
/assets/ - Static assets
|
||||||
|
/public/ - Public static files
|
||||||
|
```
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
- **Components**: PascalCase (e.g., `Modal.vue`, `IconButton.vue`)
|
||||||
|
- **Files**: kebab-case for pages, PascalCase for components
|
||||||
|
- **Stores**: CamelCase (e.g., `ModalStore.js`, `AudioPlayerStore.js`)
|
||||||
|
- **Props/Emits**: camelCase
|
||||||
|
|
||||||
|
### Component Patterns
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
// Use withDefaults for optional props
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
modelValue?: boolean
|
||||||
|
persistent?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
modelValue: false,
|
||||||
|
persistent: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Use type-only emits
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value: boolean): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Use toRefs for reactive destructuring
|
||||||
|
const { modelValue } = toRefs(props)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- Template content -->
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tailwind CSS
|
||||||
|
|
||||||
|
- Use Tailwind utility classes for all styling
|
||||||
|
- Common classes used: `flex`, `grid`, `fixed`, `relative`, `z-*`, `p-*`, `m-*`, `text-*`, etc.
|
||||||
|
|
||||||
|
### API Routes (Server)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/api/example.ts
|
||||||
|
export default defineEventHandler((event) => {
|
||||||
|
// Handle request and return data
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Store Patterns (Pinia)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// stores/ExampleStore.js
|
||||||
|
import { defineStore } from "pinia"
|
||||||
|
|
||||||
|
export const useExampleStore = defineStore("ExampleStore", {
|
||||||
|
state: () => ({ count: 0 }),
|
||||||
|
actions: {
|
||||||
|
increment() {
|
||||||
|
this.count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
- Use try/catch in API routes
|
||||||
|
- Return appropriate HTTP status codes
|
||||||
|
- Handle undefined/null values gracefully
|
||||||
|
|
||||||
|
### Imports
|
||||||
|
|
||||||
|
- Vue/composables: Use Nuxt auto-imports (no import needed)
|
||||||
|
- External modules: Explicit import
|
||||||
|
- Server-only: Place in `/server/` directory
|
||||||
|
- Path aliases: `@/` maps to project root
|
||||||
|
|
||||||
|
### Additional Notes
|
||||||
|
|
||||||
|
- Project uses Storybook (`.stories.ts` files) for component documentation
|
||||||
|
- Environment variables should use `.env` files (not committed)
|
||||||
|
- Image domains configured for `unboundedpress.org` in nuxt.config.ts
|
||||||
|
|
@ -75,7 +75,7 @@ const toggle = () => {
|
||||||
<Icon
|
<Icon
|
||||||
name="heroicons:chevron-down"
|
name="heroicons:chevron-down"
|
||||||
:class="isOpen ? 'transform rotate-180' : ''"
|
:class="isOpen ? 'transform rotate-180' : ''"
|
||||||
class="w-5 h-5"
|
class="w-5 h-5 text-black"
|
||||||
/>
|
/>
|
||||||
<slot name="title"></slot>
|
<slot name="title"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
|
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
|
||||||
import { ref, toRefs, watch } from 'vue'
|
import { ref, toRefs, watch } from 'vue'
|
||||||
import Icon from '../Icon/index.vue'
|
|
||||||
import Collapsible from './Collapsible.vue'
|
import Collapsible from './Collapsible.vue'
|
||||||
|
|
||||||
interface CollapsibleItem {
|
interface CollapsibleItem {
|
||||||
|
|
|
||||||
|
|
@ -3,35 +3,35 @@
|
||||||
<div v-show="visible" class="bg-black rounded-full text-xs inline-flex" >
|
<div v-show="visible" class="bg-black rounded-full text-xs inline-flex" >
|
||||||
|
|
||||||
<NuxtLink v-if="type === 'score'" :to="link" class="inline-flex p-1">
|
<NuxtLink v-if="type === 'score'" :to="link" class="inline-flex p-1">
|
||||||
<Icon name="ion:book-sharp" color="white" />
|
<Icon name="ion:book-sharp" style="color: white" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink v-else-if="type === 'document'" class="inline-flex p-1" :to="link" :target="newTab ? '_blank' : undefined">
|
<NuxtLink v-else-if="type === 'document'" class="inline-flex p-1" :to="link" :target="newTab ? '_blank' : undefined">
|
||||||
<Icon name="ion:book-sharp" color="white" />
|
<Icon name="ion:book-sharp" style="color: white" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink v-else-if="type === 'buy'" class="inline-flex p-1" :to="link">
|
<NuxtLink v-else-if="type === 'buy'" class="inline-flex p-1" :to="link">
|
||||||
<Icon name="bxs:purchase-tag" color="white" />
|
<Icon name="bxs:purchase-tag" style="color: white" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink v-else-if="type === 'email'" class="inline-flex p-1" :to="link">
|
<NuxtLink v-else-if="type === 'email'" class="inline-flex p-1" :to="link">
|
||||||
<Icon name="ic:baseline-email" color="white" />
|
<Icon name="ic:baseline-email" style="color: white" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<NuxtLink v-else-if="type === 'discogs'" class="inline-flex p-1" :to="link">
|
<NuxtLink v-else-if="type === 'discogs'" class="inline-flex p-1" :to="link">
|
||||||
<Icon name="simple-icons:discogs" color="white" />
|
<Icon name="simple-icons:discogs" style="color: white" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
<button @click="audioPlayerStore.setSoundCloudTrackID(work.soundcloud_trackid)" v-else-if="type === 'audio'" class="inline-flex p-1">
|
<button @click="audioPlayerStore.setSoundCloudTrackID(work.soundcloud_trackid)" v-else-if="type === 'audio'" class="inline-flex p-1">
|
||||||
<Icon name="wpf:speaker" color="white" />
|
<Icon name="wpf:speaker" style="color: white" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="modalStore.setModalProps('video', 'aspect-video', true, '', '', work.vimeo_trackid)" v-else-if="type === 'video'" class="inline-flex p-1">
|
<button @click="modalStore.setModalProps('video', 'aspect-video', true, '', '', work.vimeo_trackid)" v-else-if="type === 'video'" class="inline-flex p-1">
|
||||||
<Icon name="fluent:video-48-filled" color="white" />
|
<Icon name="fluent:video-48-filled" style="color: white" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="modalStore.setModalProps('image', 'aspect-auto', true, 'images', work.gallery, '')" v-else="type === 'image'" class="inline-flex p-1">
|
<button @click="modalStore.setModalProps('image', 'aspect-auto', true, 'images', work.gallery, '')" v-else="type === 'image'" class="inline-flex p-1">
|
||||||
<Icon name="mdi:camera" color="white" />
|
<Icon name="mdi:camera" style="color: white" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
modules: ['@nuxtjs/tailwindcss', '@nuxt/image', 'nuxt-icon', '@pinia/nuxt', 'nuxt-headlessui', 'nuxt-swiper'],
|
modules: ['@nuxtjs/tailwindcss', '@nuxt/image', '@nuxt/icon', '@pinia/nuxt', 'nuxt-headlessui', 'nuxt-swiper', 'nuxt-umami'],
|
||||||
extends: ['nuxt-umami'],
|
|
||||||
image: {
|
image: {
|
||||||
domains: ['unboundedpress.org']
|
domains: ['unboundedpress.org']
|
||||||
},
|
},
|
||||||
|
|
|
||||||
30
package.json
30
package.json
|
|
@ -9,19 +9,27 @@
|
||||||
"postinstall": "nuxt prepare"
|
"postinstall": "nuxt prepare"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/image": "^1.0.0-rc.1",
|
"@iconify-json/bxs": "^1.2.2",
|
||||||
"@nuxtjs/tailwindcss": "^6.7.0",
|
"@iconify-json/fluent": "^1.2.39",
|
||||||
"@types/node": "^18",
|
"@iconify-json/heroicons": "^1.2.3",
|
||||||
"nuxt-headlessui": "^1.1.4",
|
"@iconify-json/ion": "^1.2.6",
|
||||||
"nuxt-icon": "^0.4.1"
|
"@iconify-json/mdi": "^1.2.3",
|
||||||
|
"@iconify-json/simple-icons": "^1.2.71",
|
||||||
|
"@iconify-json/wpf": "^1.2.0",
|
||||||
|
"@nuxt/icon": "^2.2.1",
|
||||||
|
"@nuxt/image": "^2.0.0",
|
||||||
|
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||||
|
"@types/node": "^25.2.3",
|
||||||
|
"nuxt-headlessui": "^1.2.2",
|
||||||
|
"nuxt-icon": "^1.0.0-beta.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pinia/nuxt": "^0.4.11",
|
"@pinia/nuxt": "^0.11.3",
|
||||||
"mongodb": "^7.1.0",
|
"mongodb": "^7.1.0",
|
||||||
"nuxt": "^3.6.0",
|
"nuxt": "^4.3.1",
|
||||||
"nuxt-swiper": "^1.1.0",
|
"nuxt-swiper": "^2.0.1",
|
||||||
"nuxt-umami": "^2.4.2",
|
"nuxt-umami": "^3.2.1",
|
||||||
"pinia": "^2.1.3",
|
"pinia": "^3.0.4",
|
||||||
"sharp": "^0.32.1"
|
"sharp": "^0.34.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<div class="py-2 ml-3" v-for="item in works">
|
<div class="py-2 ml-3" v-for="item in works">
|
||||||
<p class="font-thin">{{ item.year }}</p>
|
<p class="font-thin">{{ item.year }}</p>
|
||||||
<div class="leading-tight py-1 ml-3" v-for="work in item.works">
|
<div class="leading-tight py-1 ml-3" v-for="work in item.works">
|
||||||
<div class="grid grid-cols-[65%,30%] gap-1 font-thin">
|
<div class="grid grid-cols-[65%,30%] gap-1 font-thin items-center">
|
||||||
<div class="italic text-sm">{{ work.title }}</div>
|
<div class="italic text-sm">{{ work.title }}</div>
|
||||||
<div class="inline-flex">
|
<div class="inline-flex">
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
<p class="text-lg">writings</p>
|
<p class="text-lg">writings</p>
|
||||||
|
|
||||||
<div class="leading-tight py-2 ml-3 text-sm" v-for="item in pubs">
|
<div class="leading-tight py-2 ml-3 text-sm" v-for="item in pubs">
|
||||||
<div class="grid grid-cols-[95%,5%] gap-1">
|
<div class="grid grid-cols-[95%,5%] gap-1 items-center">
|
||||||
<div>
|
<div>
|
||||||
<span v-html="item.entryTags.title"></span>
|
<span v-html="item.entryTags.title"></span>
|
||||||
<div class="ml-4 text-[#7F7F7F]">
|
<div class="ml-4 text-[#7F7F7F]">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue