Replace chat-bubble PWA icons with the four-pointed star (✦) used in the interface. Fix panelSide default so chat appears on the left and content surface on the right. Add CLAUDE.md project guide and .claude/launch.json dev server config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.8 KiB
CLAUDE.md — AIUI Project Guide
Project Overview
AIUI is a next-generation AI content surface UI. It's a pnpm monorepo with two packages:
@aiui/app— Reference application (Vite + Vue 3 + Tailwind CSS)@aiui/core— Reusable component library
Stack: Vue 3 (Composition API), TypeScript ~5.8 (strict), Vite, Tailwind CSS, Pinia, Vue Router, Turborepo Node: >=20.0.0 | pnpm: >=10.0.0
Quick Reference
pnpm dev # Run app dev server + Claude proxy
pnpm dev:core # Watch-build core library
pnpm build # Build all packages (turbo)
pnpm test # Run tests (vitest)
pnpm lint # Lint all packages (eslint)
pnpm typecheck # Type-check all packages (vue-tsc)
pnpm clean # Remove dist/ directories
Dev server: http://localhost:5173 | Claude proxy: http://localhost:3141
Core Philosophy
- Open source only — MIT/Apache-2.0 licensed dependencies only
- Decentralized-first — Pluggable adapters, no vendor lock-in
- Bitcoin only — sats/Lightning/Cashu/Fedimint. Never fiat, never altcoins. AIUI is never a wallet — always deep-link to external wallets
- Privacy-first — E2E encryption (tweetnacl.js), encrypted local storage (AES-256-GCM), no tracking/telemetry
- Mobile-first, everywhere-perfect — Desktop is an enhancement of the mobile experience
- Plugin-everything — All integrations go through typed plugin interfaces
Vue 3 Conventions
Always use <script setup lang="ts"> — never Options API.
Script section ordering
Imports → Props (defineProps) → Emits (defineEmits) → Reactive state → Computed → Watchers → Methods → Lifecycle hooks → defineExpose
Naming
| Thing | Convention | Example |
|---|---|---|
| Components | PascalCase | ProjectCard.vue |
| Composables | camelCase, use prefix |
useTheme.ts |
| Props (JS) | camelCase | projectName |
| Props (template) | kebab-case | project-name |
| Boolean props | is/has/can/should prefix |
isVisible, canEdit |
| Emits (template) | kebab-case with colon namespacing | project:updated |
| Stores | camelCase, use prefix, Store suffix |
useSettingsStore |
Reactive state rules
ref()for primitives,reactive()for objectscomputed()for derived values — no side effects in computedshallowRef()for large collections/objects not requiring deep reactivity- Always use unique IDs for
:key— never array index
Props
Always use object-style with type annotations, never array-style:
// Correct
defineProps<{ title: string; count?: number }>()
// Wrong
defineProps(['title', 'count'])
Performance
- Lazy load with
defineAsyncComponentfor non-critical components - Use
onErrorCapturedfor error boundaries - Always handle loading/error/data states in async operations
File Structure
packages/app/src/
├── components/
│ ├── ui/ # Generic UI components
│ ├── chat/ # Chat interface components
│ ├── content-panel/ # Content panel components
│ ├── renderers/ # Content type renderers
│ └── layout/ # Layout components
├── composables/ # Shared composition functions
├── stores/ # Pinia stores
├── pages/ # Route-level components
├── styles/ # Global CSS, themes, tokens
├── utils/ # Pure utility functions
├── types/ # TypeScript type definitions
├── plugins/ # Plugin system
└── mocks/ # Dev fixtures & mock data
packages/core/src/
├── plugins/ # Plugin system interfaces
└── types/ # Shared TypeScript types
Tailwind & Design System
Glass Morphism (Archy-derived)
This project uses a glass morphism design language. Key utility classes:
| Class | Purpose |
|---|---|
.glass |
Standard glass: rgba(0,0,0,0.35), blur(18px), white border 0.18 opacity |
.glass-strong |
Stronger blur: blur(24px) |
.glass-card |
Card variant: rgba(0,0,0,0.65), border-radius: 1rem |
.glass-button |
Button: 48px height, rgba(0,0,0,0.6), blur(18px) |
.glass-button-sm |
Compact button variant |
.gradient-button |
Primary action gradient button |
.gradient-card |
Gradient background card |
Spacing
4px grid system: 1=4px, 2=8px, 3=12px, 4=16px, etc.
Colors
- Background:
#0a0a0a(near-black) - Accent / Bitcoin orange:
#F7931A - Primary:
#606060 - Text opacity scale:
/25(placeholder) →/40(muted) →/60(secondary) →/70(interactive) →/80(body) →/90(emphasis) →/96(headings) →text-white(active) - No separator borders between major sections
Typography
Inter/system-ui for body, Menlo/Monaco for monospace.
Responsive breakpoints (mobile-first)
sm 640px → md 768px → lg 1024px → xl 1280px → 2xl 1536px
Animations
animate-fade-up(900ms),animate-fade-up-fast(400ms),animate-fade-in(500ms),animate-scale-in(250ms)- Duration: 100ms micro, 200ms fast, 300ms moderate, 500ms normal, 600ms max
- Easing:
ease-outfor entrances (90% of animations),ease-infor exits - Only animate
transformandopacity— avoid animating layout properties - Always respect
prefers-reduced-motion
Content Surfaces Architecture
Every content renderer supports up to five surfaces:
- Chat Preview (~120px max) — inline bubble, identify content at a glance
- Chat Play (~200px max) — inline playback with expand button
- Panel Preview (unlimited) — full browsing, filtering, sorting
- Panel Play — full immersive playback
- Panel Edit — full interaction, sends changes back to chat
On mobile, Panel surfaces open as full-screen overlays, not side-by-side.
interface RendererDefinition {
id: string
name: string
contentType: string
surfaces: SurfaceType[]
chatPreview?: Component
chatPlay?: Component
panelPreview?: Component
panelPlay?: Component
panelEdit?: Component
lazyDependencies?: () => Promise<any>
}
Chat surfaces must have zero lazy dependencies. Panel surfaces may lazy-load heavy libraries.
Plugin System
All integrations are plugins. Plugin types: ai-provider, media-source, messaging, storage, renderer, file-handler, crypto, search, auth, wallet, social-embed, mcp, media.
interface AIUIPlugin {
id: string
name: string
version: string
type: PluginType
description?: string
init(context: PluginContext): Promise<void>
destroy(): Promise<void>
isAvailable(): Promise<boolean>
}
Sandboxing: Tier 1 (trusted built-in), Tier 2 (community — sandboxed iframes), Tier 3 (external processes). Community plugins get no direct DOM access.
AI Provider Integration
interface AIProviderAdapter extends AIUIPlugin {
type: 'ai-provider'
chat(messages: Message[], options: ChatOptions): AsyncIterable<ChatChunk>
models(): Promise<Model[]>
supportsStreaming: boolean
supportsVision: boolean
supportsTools: boolean
}
Normalize tool calling across providers (OpenAI tool_calls vs Claude tool_use). Never include API keys in context injection.
Security & Crypto
- E2E encryption: tweetnacl.js XSalsa20-Poly1305
- Local storage: Web Crypto API AES-256-GCM + PBKDF2 (100K+ iterations)
- API keys: encrypted at rest, never in localStorage, never logged, masked in UI (last 4 chars)
- No
eval()orinnerHTMLwith untrusted content - Sanitize all user input against XSS
- HTTPS only, CSP headers in production
- Dev bypass:
VITE_DISABLE_CRYPTO=true(never in production)
Accessibility
WCAG AA minimum compliance:
- Color contrast: 4.5:1 normal text, 3:1 large/interactive
- Keyboard: all elements focusable via Tab, visible focus indicators, Escape closes modals
- Semantic HTML: use
<header>,<nav>,<main>,<article>,<aside>,<footer>— not div soup - ARIA:
aria-labelfor icon buttons,aria-live="polite"for dynamic updates,sr-onlyfor screen reader text - Touch targets: min 44x44px with 8px gaps
- All images need
altattributes (decorative:alt="") - Respect
prefers-reduced-motion
Performance Budget
- Initial load: < 250KB gzipped
- Core bundle: Vue + Tailwind + Pinia + Router + chat UI (~150KB) + markdown + streaming (~50KB)
- Everything else: lazy-loaded on demand
- Virtual scrolling (TanStack Virtual) for chat lists
- Clean up listeners in
onUnmounted, useshallowReffor large data - Core Web Vitals: LCP < 2.5s, FID < 100ms, CLS < 0.1
- Preconnect to API hosts, debounce inputs (100ms)
Mobile UX
- Primary actions in bottom thumb zone
- Touch targets: min 44x44px, 8px gap between targets
- Viewport:
height: 100dvhwith safe area insets - Form inputs: min 16px font (prevents iOS zoom), appropriate
inputmode - Content panels: full-screen overlay or bottom sheet on mobile
- Support both portrait and landscape
Environment & Dev Mode
Env vars must be prefixed VITE_. Secrets go in .env.local (gitignored). See .env.example for template.
Feature flags via useFeatureFlags(): isDev, isTauri, isMobile, isCryptoEnabled, isMockData
Dev mode enables: mock data, debug panel, verbose logging, disabled encryption, all renderers without lazy loading.
Git Conventions
Commit format
type(scope): description
Types: feat, fix, refactor, style, docs, test, chore, perf
Scope: package or area — core, app, chat, renderer-film, plugin-x
Branches
main (production), dev (integration), feat/description, fix/description
Rules
- One feature per PR
- All tests pass, TypeScript strict passes, no lint errors
- No force push to main/dev
- Never commit
.env.local, secrets, ornode_modules - Squash merge features, tag releases
v1.0.0