Files
BRETT/AGENTS.md
T
FliesentischKingandDevin e9561ddb2a Translate AGENTS.md to English and add UI design rules
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-23 19:50:45 +02:00

5.9 KiB

Nuxt shadcn Demo

Workflow

  • No headless-browser verification after UI changes - the user tests manually. Only ask/debug after repeated failures. Syntax checks are fine; run npm run build only if the user explicitly asks for it.
  • Dev server: npm run dev (port 3000). The user usually runs it themselves - do not start it blindly, check whether it is already running first.
  • DB: MariaDB via docker-compose.yml (container nuxt-shadcn-demo-db), URL in DATABASE_URL. Migrations: npm run db:generate (schema → SQL), npm run db:migrate (apply; also runs automatically via Nitro plugin in dev mode).
  • No Git commits on your own - only commit when the user explicitly instructs it.

Conventions

  • Vue 3 <script setup lang="ts">, Composition API, shadcn-vue components under app/components/ui/.
  • Theme colors via semantic tokens (bg-primary, border-primary, …), never hardcode them. Themes are maintained in app/assets/css/tailwind.css, app/composables/useTheme.ts and app/components/ThemeSelector.vue - --primary is the signature color, --accent the contrast; neo has --radius: 0 (square), so use rounded-md/rounded-sm instead of rounded-2xl/rounded-xl there.
  • Tailwind: size-* instead of w-* h-*, gap-* instead of space-x/y-*, cn() for conditional classes.
  • Live updates run through server/routes/_ws.ts + broadcastToAll; client-side handling in app/plugins/notifications.client.ts.
  • Comment reactions: useCommentReactions (state map per comment ID). Emoji selection via UnicodeEmojiPicker.vue (emoji-picker-element web component, all Unicode emojis with search; local data from emoji-picker-element-data, UI texts from emoji-picker-element/i18n).
  • Internal terminology: Emoji = Unicode reactions (UnicodeEmojiPicker.vue, CommentReactionPicker.vue, emoji columns in the reaction tables); Sticker = uploaded media (StickerPicker.vue, StickerMedia.vue, useStickers, shared/utils/stickers.ts, table stickers, /api/stickers, /uploads/stickers, permission sticker.standard). Shortcode rendering :name: via stickerParts().
  • Thread notifications: threadSubscriptions stores subscriptions and explicit opt-outs. Creating, commenting, favoriting and post reactions call ensureThreadSubscription(); a disabled subscription must not be reactivated by this. notifyComment() notifies active subscribers and mentioned users, respects opt-outs even for mentions and groups notifications per user/thread.
  • Direct messages: table direct_messages (sender/recipient/content/read_at). API under /api/messages (list, history with ?before=, send, read). Live via pushToUser with dm event (message + sender/recipient user info) and dm-read read receipt. Client state in useDirectMessages - openPartnerId marks the open conversation; liveMessageHandler/liveReadHandler/liveReactionHandler are module singletons registered by the messages page. DM reactions: table direct_message_reactions (message/user/emoji, seen_at), toggle under /api/messages/reactions/:messageId, WS event dm-reaction to both participants. Unseen foreign reactions count like unread messages for the unread badge; read sets seen_at as well. DM attachments: attachments JSON column in CommentAttachment shape (meta.voice = voice message); POST /api/messages/:userId accepts multipart with files/voice fields like the comment form, rendered via CommentAttachments.
  • Permissions: catalog in shared/utils/permissions.ts (PERMISSIONS), server check via requirePermission(event, key) in server/utils/auth.ts - the admin role is an implicit wildcard. Client-side can(key) from useAuth. Seeding via seedPermissions() in the migration plugin/script.
  • i18n: @nuxtjs/i18n, locales de (default) + en in i18n/locales/*.json - always keep both files with identical key structure. All UI strings via $t()/t(), never hardcoded. strategy: 'no_prefix', locale cookie i18n_locale. Permission labels in admin via admin.perms.<key>; effect/theme names remain proper nouns.

UI & Design System Rules (Anti-Vibe-Coding)

When auditing, refactoring, or building UI, strictly apply these design rules:

1. Visual Hierarchy & Card Fatigue

  • Eliminate "Card-in-Card" nesting: Never place a Card inside another Card. Use whitespace, divider lines (<Separator />), or subtle background shifts instead of borders.
  • Icon Restraint: Do NOT add icons to every button, table header, or card title. Icons should only exist when they materially aid scanning (max 1 icon per actionable group).
  • Border Reduction: If a border doesn't separate distinct functional contexts, remove it. Favor spacing (gap, space-y) over border boxes.

2. Spacing & Rhythm (8pt Grid)

  • Stick strictly to a 4-step spacing hierarchy:
    • Tight (inline/tags): gap-1.5 to gap-2
    • Grouped elements (form fields, lists): space-y-3 / gap-3
    • Section spacing: space-y-6 / gap-6
    • Layout/Page level: py-8 to py-12, max container max-w-7xl mx-auto px-4 sm:px-6 lg:px-8
  • Do not mix random paddings (e.g. p-5, p-7). Stick to p-4 (compact) or p-6 (standard).

3. Typography & Contrast

  • Contrast comes from weight and color, not just font size.
  • Secondary text must consistently use text-muted-foreground text-xs or text-sm.
  • Main headers: Use tracking-tight font-semibold instead of generic font-bold.
  • Avoid 5 different font sizes on a single screen. Max 3 sizes per view (e.g. Title text-xl, Body text-sm, Metadata text-xs).

4. Nuxt 4 & Shadcn Vue Specifics

  • Always leverage semantic Shadcn primitives (CardHeader, CardTitle, CardDescription, CardContent, CardFooter) properly—never inline custom headers inside CardContent.
  • Keep templates clean: Move complex inline Tailwind classes into reusable Nuxt components or tailwind-variants (cva) if reused.
  • Ensure proper empty states (EmptyState pattern) and loading skeletons instead of blank spaces.