feat: initialize AIUI monorepo with project rules and core types
Foundation for the next-generation AI content surface UI: - 16 Cursor rules files covering philosophy, Vue conventions, design system, content surfaces, plugin system, AI integration, renderers, security, Bitcoin-only policy, dev/prod modes, accessibility, performance, animation, mobile UX, and git workflow - pnpm workspaces + Turborepo monorepo (@aiui/core, @aiui/app) - Vue 3 + Vite + TypeScript + Tailwind CSS 4 - Core type system: plugins, renderers, messages, content blocks - Plugin registry with renderer registration - 50 mock film fixtures with search/filter utilities - App shell with chat page layout - Environment config templates Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
---
|
||||
description: Core development philosophy for AIUI - the foundational rules that govern all code and design decisions
|
||||
globs: "**/*"
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Master Philosophy
|
||||
|
||||
## Mission
|
||||
Build the next-generation AI content surface UI — a paradigm where AI responses are rendered as rich, interactive content, not plain text. Delivered as a reusable component library (@aiui/core) and a reference application (AIUI App).
|
||||
|
||||
## Philosophical Pillars
|
||||
|
||||
### 1. Open Source Only
|
||||
Every dependency must be OSS (MIT, Apache-2.0, GPL-compatible). No proprietary SDKs, no vendor-locked services. Before adding any dependency, verify its license.
|
||||
|
||||
### 2. Decentralized-First
|
||||
No hard dependency on any centralized service. AI backends, messaging protocols, storage, search — all connect through pluggable adapter interfaces. Users choose their own providers.
|
||||
|
||||
### 3. Bitcoin Only
|
||||
Bitcoin is the only monetary unit. On-chain, Lightning, ecash (Cashu, Fedimint/Fedi). No fiat payment rails, no altcoins, no stablecoins — anywhere in the UI or codebase. AIUI is never a wallet and never handles funds directly. See `10-bitcoin-only.mdc` for full rules.
|
||||
|
||||
### 4. Cryptography for Everything Sensitive
|
||||
E2E encryption for messages, encrypted local storage, proper key management. Privacy is not a feature — it is a requirement.
|
||||
|
||||
### 5. Mobile-First, Everywhere-Perfect
|
||||
Every component works flawlessly on mobile, tablet, and desktop. Mobile is the foundation, not an afterthought. Touch targets, viewport management, and safe areas are first-class citizens.
|
||||
|
||||
### 6. Consistency is Sacred
|
||||
Mobile and desktop versions show identical content and functionality unless explicitly designed otherwise. Design tokens ensure visual consistency across all breakpoints.
|
||||
|
||||
### 7. Theme-First Architecture
|
||||
Theming is a core architectural decision from day one. Themes are CSS-based with reactive state management. Dark mode and light mode are equals.
|
||||
|
||||
### 8. Utility-First, Component-Second
|
||||
Tailwind CSS utilities in templates for maximum flexibility. Component classes only for truly reusable patterns. Extract components when you repeat, not before.
|
||||
|
||||
### 9. Performance as a Feature
|
||||
Initial load < 250KB gzipped. Lazy load everything that isn't immediately visible. CSS transforms for GPU acceleration. SVG over raster images. Code splitting by default.
|
||||
|
||||
### 10. Plugin-Everything
|
||||
Every external integration connects through a typed plugin interface. AI providers, media sources, messaging protocols, wallets, social embeds — all pluggable.
|
||||
|
||||
### 11. Accessibility is Not Optional
|
||||
WCAG AA compliance minimum. Keyboard navigation everywhere. Screen reader friendly. Color contrast tested and validated.
|
||||
|
||||
### 12. MCP-Native
|
||||
First-class Model Context Protocol support for AI tool interoperability.
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- Desktop-first thinking
|
||||
- Hardcoded values (use design tokens)
|
||||
- Premature abstraction (build three times before abstracting)
|
||||
- Magic numbers without comments
|
||||
- Invisible state (user should always know what's happening)
|
||||
- Handling funds or private keys
|
||||
- Loading third-party tracking scripts
|
||||
- Proprietary dependencies
|
||||
|
||||
## The Ultimate Goal
|
||||
|
||||
When someone uses AIUI, they should think: "This feels incredibly polished", "Everything just works", "My data is safe", "I control my own setup."
|
||||
|
||||
When a developer reads the code: "This is well organized", "I understand exactly what's happening", "Adding a new renderer is straightforward."
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
description: Vue 3 Composition API conventions and best practices for AIUI
|
||||
globs: "**/*.vue,**/*.ts"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Vue 3 Conventions
|
||||
|
||||
## Composition API with `<script setup>`
|
||||
Always use `<script setup lang="ts">`. Never use Options API.
|
||||
|
||||
## Component Organization Order
|
||||
1. Imports — external, then internal
|
||||
2. Props — with TypeScript-style validation
|
||||
3. Emits — explicitly defined
|
||||
4. State (refs and reactive)
|
||||
5. Computed — derived values, always pure
|
||||
6. Watchers — side effects only
|
||||
7. Methods — business logic
|
||||
8. Lifecycle hooks — ordered by execution
|
||||
9. Expose — public API (if needed)
|
||||
|
||||
## File Organization
|
||||
```
|
||||
src/
|
||||
components/
|
||||
ui/ # Primitives (Button, Card, Badge, Input)
|
||||
chat/ # Chat window, message list, input
|
||||
content-panel/ # Side panel for surfaced content
|
||||
renderers/ # Content type renderers
|
||||
layout/ # Shell, split-pane, responsive containers
|
||||
composables/ # Shared composition functions (useTheme, useMedia, useCrypto)
|
||||
stores/ # Pinia stores
|
||||
plugins/ # Plugin system
|
||||
types/ # Shared TypeScript types
|
||||
styles/ # Global CSS, themes, design tokens
|
||||
utils/ # Pure utility functions
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
- Components: PascalCase (`ProjectCard.vue`)
|
||||
- Composables: camelCase, prefixed with "use" (`useTheme.ts`)
|
||||
- Props: camelCase in JS, kebab-case in templates
|
||||
- Boolean props: prefix with `is`, `has`, `can`, `should`
|
||||
- Handler props: prefix with `on` (`onClick`, `onClose`)
|
||||
- Emits: explicit, kebab-case in templates (`project:updated`)
|
||||
|
||||
## Props — Always Validate
|
||||
```typescript
|
||||
defineProps({
|
||||
title: { type: String, required: true },
|
||||
count: { type: Number, default: 0 },
|
||||
status: {
|
||||
type: String as PropType<'pending' | 'active' | 'complete'>,
|
||||
default: 'pending'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Never use array-style props: `defineProps(['title', 'count'])`
|
||||
|
||||
## Reactive State
|
||||
- `ref` for primitives and single values
|
||||
- `reactive` for objects with multiple properties
|
||||
- `computed` for derived state (never side effects in computed)
|
||||
- `shallowRef` for large objects that change at top level only
|
||||
|
||||
## Templates — Keep Clean
|
||||
Move complex logic to computed properties or methods. No inline logic in templates. Use `v-if` for infrequent toggles, `v-show` for frequent ones.
|
||||
|
||||
## Composables
|
||||
- One responsibility per composable
|
||||
- Return only what's needed
|
||||
- Handle cleanup in `onUnmounted`
|
||||
- Make composables testable
|
||||
|
||||
## Performance
|
||||
- Lazy load heavy components: `defineAsyncComponent(() => import(...))`
|
||||
- Use `shallowRef` for large lists
|
||||
- Use `:key` with unique identifiers, never index
|
||||
- Avoid reactive objects in templates (create in script)
|
||||
|
||||
## Error Handling
|
||||
Use `onErrorCaptured` for component-level error boundaries. Always handle async errors with try/catch/finally pattern (loading, error, data states).
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
description: Tailwind CSS utility-first styling conventions for AIUI
|
||||
globs: "**/*.vue,**/*.css,**/*.ts"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Tailwind CSS Styling
|
||||
|
||||
## Utility-First
|
||||
Use Tailwind utilities directly in templates. Extract to component classes only when a pattern repeats 3+ times.
|
||||
|
||||
## 4px Spacing Grid
|
||||
All spacing follows a 4px base grid:
|
||||
- 1 = 4px, 2 = 8px, 3 = 12px, 4 = 16px, 5 = 20px, 6 = 24px, 8 = 32px, 12 = 48px, 16 = 64px
|
||||
|
||||
## Typography Scale
|
||||
```
|
||||
text-xs = 12px (metadata, timestamps)
|
||||
text-sm = 14px (body text, buttons)
|
||||
text-base = 16px (default body, inputs)
|
||||
text-lg = 18px (subtitles)
|
||||
text-xl = 20px (card titles)
|
||||
text-2xl = 24px (section headings)
|
||||
text-3xl = 30px (page headings)
|
||||
text-4xl = 36px (hero headings)
|
||||
```
|
||||
|
||||
Font weights: `font-normal` (body), `font-medium` (emphasis), `font-semibold` (headings/buttons), `font-bold` (strong emphasis).
|
||||
|
||||
## Border Radius
|
||||
- `rounded-md` (6px) — inputs
|
||||
- `rounded-lg` (8px) — buttons, standard cards
|
||||
- `rounded-2xl` (16px) — modern cards, modals
|
||||
- `rounded-full` — pills, avatars
|
||||
|
||||
## Shadows
|
||||
- `shadow-sm` — subtle elevation
|
||||
- `shadow-md` — standard card
|
||||
- `shadow-lg` — modal, dropdown
|
||||
- `shadow-xl` — hero card
|
||||
|
||||
## Glass Morphism
|
||||
For cards over complex backgrounds:
|
||||
```html
|
||||
<div class="bg-white/10 backdrop-blur-sm border border-white/10 rounded-2xl">
|
||||
```
|
||||
Never use glass morphism on body text containers or form inputs.
|
||||
|
||||
## Responsive — Mobile First
|
||||
Always write base styles for mobile, enhance with breakpoints:
|
||||
```html
|
||||
<div class="text-base md:text-lg lg:text-xl p-4 md:p-6 lg:p-8">
|
||||
```
|
||||
Breakpoints: `sm` (640px), `md` (768px), `lg` (1024px), `xl` (1280px), `2xl` (1536px).
|
||||
|
||||
## Dark Mode
|
||||
Use CSS custom properties for theme switching. Both Tailwind `dark:` and custom theme classes supported:
|
||||
```html
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
||||
```
|
||||
|
||||
## Gradients for Hierarchy
|
||||
Use gradients strategically for emphasis, not decoration:
|
||||
```html
|
||||
<h2 class="bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
|
||||
```
|
||||
|
||||
## Hover States
|
||||
All interactive elements need immediate visual feedback (< 100ms):
|
||||
```html
|
||||
<button class="transition-colors duration-200 hover:bg-primary-dark">
|
||||
```
|
||||
|
||||
## Focus States
|
||||
Always visible focus for accessibility:
|
||||
```html
|
||||
<button class="focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2">
|
||||
```
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
description: Design system foundations - colors, typography, spacing, component patterns
|
||||
globs: "**/*.vue,**/*.css,**/*.ts"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Design System
|
||||
|
||||
## Design Tokens
|
||||
|
||||
### Color Palette
|
||||
Semantic color tokens defined by purpose, not appearance:
|
||||
- `primary` — main brand actions (#606060 / configurable)
|
||||
- `accent` — highlight, Bitcoin orange (#F7931A)
|
||||
- `success` — positive states (#10B981)
|
||||
- `error` — negative states (#EF4444)
|
||||
- `warning` — caution states (#F59E0B)
|
||||
- `info` — informational (#3B82F6)
|
||||
- Neutrals: gray-50 through gray-900
|
||||
|
||||
### Typography
|
||||
- Display font: Geometric sans-serif (Space Grotesk, Inter Display)
|
||||
- Body font: Clean sans-serif (Inter, system-ui)
|
||||
- Mono font: Menlo, Monaco, Courier New
|
||||
- Use single font family with weight variations for simplicity
|
||||
|
||||
### Spacing
|
||||
All spacing on 4px grid. Common patterns:
|
||||
- Component padding: `p-4` (16px) to `p-6` (24px)
|
||||
- Section spacing: `py-12` (48px) to `py-16` (64px)
|
||||
- Element gaps: `gap-4` (16px) default
|
||||
|
||||
### Shadows & Glows
|
||||
```css
|
||||
shadow-soft: 0 2px 8px rgba(0, 0, 0, 0.08)
|
||||
shadow-card: 0 4px 16px rgba(0, 0, 0, 0.1)
|
||||
shadow-elevated: 0 8px 32px rgba(0, 0, 0, 0.15)
|
||||
```
|
||||
|
||||
## Component Patterns
|
||||
|
||||
### Buttons
|
||||
- Primary: solid background, white text, `rounded-lg`, `font-semibold`
|
||||
- Secondary: border only, primary text, fill on hover
|
||||
- Ghost: text only, subtle background on hover
|
||||
- Icon: `p-2 rounded-md`, minimum 44x44px touch target
|
||||
- Sizes: sm (`px-4 py-2`), md (`px-6 py-3`), lg (`px-8 py-4`)
|
||||
|
||||
### Cards
|
||||
- Background: white/dark with `rounded-2xl` (16px radius)
|
||||
- Padding: `p-6`
|
||||
- Shadow: `shadow-card` default, `shadow-elevated` on hover
|
||||
- Interactive cards get `hover:shadow-lg transition-shadow duration-300 cursor-pointer`
|
||||
|
||||
### Form Elements
|
||||
- Inputs: `min-h-[44px]` touch target, `text-base` (prevents iOS zoom), `rounded-lg`
|
||||
- Labels: `text-sm font-semibold text-gray-700 mb-2`
|
||||
- Error text: `text-sm text-error mt-1`
|
||||
- Focus: `focus:border-primary focus:ring-2 focus:ring-primary/50`
|
||||
|
||||
### Badges
|
||||
- Status badges: `inline-flex items-center px-3 py-1 rounded-full text-sm font-medium`
|
||||
- Color variants: success (green-100/green-800), warning (yellow-100/yellow-800), error (red-100/red-800)
|
||||
|
||||
### Icons
|
||||
- SVG preferred, using Lucide Icons
|
||||
- Sizes: xs (16px), sm (20px), md (24px), lg (32px), xl (48px)
|
||||
- Always use `currentColor` for stroke/fill
|
||||
- Icon-only buttons must have `aria-label`
|
||||
|
||||
## Layout Patterns
|
||||
- Standard container: `max-w-7xl mx-auto px-4 md:px-6`
|
||||
- Reading container: `max-w-3xl mx-auto px-4 md:px-6`
|
||||
- Sidebar + content: `grid grid-cols-1 lg:grid-cols-[280px_1fr] gap-6`
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
description: Component architecture principles - composition, patterns, and structure
|
||||
globs: "**/*.vue,**/*.ts"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Component Architecture
|
||||
|
||||
## Core Philosophy: Composition Over Configuration
|
||||
Build complex UIs from simple, focused components that compose well together.
|
||||
|
||||
- Single Responsibility: each component does one thing well
|
||||
- Use slots instead of complex prop APIs
|
||||
- Provide sensible defaults
|
||||
- Clear TypeScript interfaces for props
|
||||
- Keep component state local and minimal
|
||||
|
||||
## Anti-Patterns
|
||||
- God components that do everything
|
||||
- Prop drilling through many layers (use provide/inject or Pinia)
|
||||
- Hard-coded values instead of props
|
||||
- Component logic mixed with layout
|
||||
- Tight coupling between components
|
||||
|
||||
## Compound Component Pattern
|
||||
Components that work together as a cohesive unit:
|
||||
```vue
|
||||
<Card>
|
||||
<Card.Header>Title</Card.Header>
|
||||
<Card.Body>Content</Card.Body>
|
||||
<Card.Footer>Actions</Card.Footer>
|
||||
</Card>
|
||||
```
|
||||
|
||||
## Container/Presenter Pattern
|
||||
Separate logic from presentation:
|
||||
- Container: handles data fetching, state, side effects
|
||||
- Presenter: pure rendering, receives data via props, emits events
|
||||
|
||||
## Slot Pattern (Vue)
|
||||
Use named slots for flexible content injection:
|
||||
```vue
|
||||
<template>
|
||||
<div class="section">
|
||||
<slot name="title" />
|
||||
<slot name="content" />
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Prop Interface Design
|
||||
```typescript
|
||||
interface BaseComponentProps {
|
||||
class?: string
|
||||
testId?: string
|
||||
}
|
||||
|
||||
interface ButtonProps extends BaseComponentProps {
|
||||
variant?: 'primary' | 'secondary' | 'ghost'
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
## Component File Template
|
||||
```
|
||||
1. Imports (external, then internal)
|
||||
2. Types/Interfaces
|
||||
3. Constants
|
||||
4. Main component (props, emits, state, computed, methods, lifecycle)
|
||||
5. Sub-components (if any)
|
||||
```
|
||||
|
||||
## Error Boundaries
|
||||
Every major section should have error boundary handling via `onErrorCaptured`. Show fallback UI, never a blank screen.
|
||||
|
||||
## Responsive Components
|
||||
Use CSS-based responsive (`hidden md:block`) over JS-based (`useMediaQuery`) when possible. JS-based only when behavior changes (not just visibility).
|
||||
|
||||
## Component Checklist
|
||||
Before shipping any component:
|
||||
- [ ] TypeScript interface defined
|
||||
- [ ] Sensible default props
|
||||
- [ ] Loading and error states handled
|
||||
- [ ] ARIA attributes added
|
||||
- [ ] Keyboard navigation works
|
||||
- [ ] Responsive behavior tested
|
||||
- [ ] Dark mode styling works
|
||||
- [ ] Touch interactions verified on mobile
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
description: The five content surfaces that define how content is rendered in AIUI
|
||||
globs: "**/renderers/**,**/chat/**,**/content-panel/**"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Content Surfaces
|
||||
|
||||
AIUI has five distinct surfaces where content can appear. Every renderer must define how it behaves in each applicable surface.
|
||||
|
||||
## Surface 1: Chat Preview
|
||||
- Location: inline in chat message bubble
|
||||
- Max height: ~120px
|
||||
- Purpose: identify content at a glance (thumbnail, title, brief metadata)
|
||||
- Always tappable/clickable to expand to Panel Preview or Panel Play
|
||||
- Lightweight rendering only — no heavy libraries loaded
|
||||
- Examples: film poster thumbnail strip, file icon with name, code snippet (first 5 lines), image thumbnail
|
||||
|
||||
## Surface 2: Chat Play
|
||||
- Location: inline in chat message bubble
|
||||
- Max height: ~200px
|
||||
- Purpose: inline playback without leaving the chat
|
||||
- Must not disrupt chat scrolling
|
||||
- Has an "expand" button to open in Panel Play
|
||||
- Examples: voice note waveform with play button, short video player, audio player, small interactive widget
|
||||
|
||||
## Surface 3: Panel Preview
|
||||
- Location: content panel (beside chat on desktop, overlay on mobile)
|
||||
- No height limit (scrollable within panel)
|
||||
- Purpose: full browsing/exploration experience
|
||||
- Supports: filtering, sorting, searching, pagination
|
||||
- Click items to go to Panel Play or Panel Edit
|
||||
- Examples: film grid (tiled, filterable), image gallery, search results list, document preview, file tree
|
||||
|
||||
## Surface 4: Panel Play
|
||||
- Location: content panel
|
||||
- Purpose: full immersive media playback
|
||||
- Examples: full video player with controls, audio with spectrum visualization, slideshow, trailer playback
|
||||
|
||||
## Surface 5: Panel Edit/Interactive
|
||||
- Location: content panel
|
||||
- Purpose: full interaction and editing
|
||||
- Changes can be sent back to chat as new messages
|
||||
- Examples: code editor (CodeMirror), form filling, approval workflow, spreadsheet editing, diagram creation
|
||||
|
||||
## Surface Transitions
|
||||
```
|
||||
Chat Preview --tap--> Panel Preview --tap item--> Panel Play
|
||||
--tap item--> Panel Edit
|
||||
Chat Play --expand--> Panel Play
|
||||
Panel Edit --submit--> Chat (new message with result)
|
||||
```
|
||||
|
||||
## Renderer Interface
|
||||
Every renderer must export:
|
||||
```typescript
|
||||
interface RendererDefinition {
|
||||
id: string
|
||||
name: string
|
||||
contentType: string // MIME-like type identifier
|
||||
surfaces: SurfaceType[] // which surfaces this renderer supports
|
||||
chatPreview?: Component // Surface 1
|
||||
chatPlay?: Component // Surface 2
|
||||
panelPreview?: Component // Surface 3
|
||||
panelPlay?: Component // Surface 4
|
||||
panelEdit?: Component // Surface 5
|
||||
lazyDependencies?: () => Promise<any> // heavy libs loaded on demand
|
||||
}
|
||||
```
|
||||
|
||||
## Mobile Behavior
|
||||
- On mobile, there is no side-by-side layout
|
||||
- Panel surfaces open as a full-screen overlay or bottom sheet
|
||||
- Chat Preview and Chat Play remain inline
|
||||
- Transition: tap Chat Preview → full-screen Panel Preview (slide up)
|
||||
- Back gesture or button returns to chat
|
||||
|
||||
## Performance Rules
|
||||
- Chat Preview and Chat Play must render with zero lazy-loaded dependencies
|
||||
- Panel surfaces may lazy-load heavy libraries (CodeMirror, pdf.js, etc.)
|
||||
- Never block the chat scroll with renderer loading
|
||||
- Use skeleton/placeholder while panel content loads
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
description: Plugin architecture rules - interfaces, registration, lifecycle, sandboxing
|
||||
globs: "**/plugins/**,**/*.plugin.ts"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Plugin System
|
||||
|
||||
## Philosophy
|
||||
Every external integration connects through a typed plugin interface. No direct coupling to any service, provider, or protocol.
|
||||
|
||||
## Plugin Types
|
||||
```typescript
|
||||
type PluginType =
|
||||
| 'ai-provider' // LLM backends (OpenRouter, Ollama, Claude, etc.)
|
||||
| 'media-source' // Content sources (Plex, YouTube, Nextcloud, Archive.org)
|
||||
| 'messaging' // Chat protocols (Nostr, Matrix, local)
|
||||
| 'storage' // File storage (local FS, IPFS, Nextcloud)
|
||||
| 'renderer' // Custom content renderers
|
||||
| 'file-handler' // File open/preview handlers
|
||||
| 'crypto' // Encryption providers
|
||||
| 'search' // Search backends (SearXNG, local)
|
||||
| 'auth' // Authentication (Nostr keys, DID, passkeys)
|
||||
| 'wallet' // Bitcoin wallet deep-linking (Phoenix, Zeus, Alby, etc.)
|
||||
| 'social-embed' // Social post fetching (X, Nostr, Mastodon)
|
||||
| 'mcp' // Model Context Protocol servers
|
||||
| 'media' // Media processing (ffmpeg.wasm, whisper, TTS)
|
||||
```
|
||||
|
||||
## Base Plugin Interface
|
||||
```typescript
|
||||
interface AIUIPlugin {
|
||||
id: string
|
||||
name: string
|
||||
version: string
|
||||
type: PluginType
|
||||
description?: string
|
||||
icon?: string
|
||||
init(context: PluginContext): Promise<void>
|
||||
destroy(): Promise<void>
|
||||
isAvailable(): Promise<boolean>
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin Context
|
||||
Plugins receive a context object with access to:
|
||||
- Settings store (read/write plugin-specific settings)
|
||||
- Event bus (emit/listen for app events)
|
||||
- Logger (structured logging)
|
||||
- Crypto utilities (for encrypting plugin data at rest)
|
||||
|
||||
Plugins do NOT receive:
|
||||
- Direct DOM access (community plugins)
|
||||
- File system access (without explicit capability grant)
|
||||
- Network access to arbitrary hosts (without declaration)
|
||||
|
||||
## Sandboxing Tiers
|
||||
|
||||
### Tier 1: Trusted (built-in, official)
|
||||
Run in main thread with full API access. AI adapters, core renderers, crypto providers.
|
||||
|
||||
### Tier 2: Community
|
||||
Run in sandboxed iframes with `postMessage` API. Custom renderers, themes, visual extensions. Cannot access host DOM, file system, or network directly.
|
||||
|
||||
### Tier 3: External Processes
|
||||
MCP servers, local AI runners. Run as separate processes (Tauri IPC) or connect via HTTP. Isolated by OS process boundary.
|
||||
|
||||
## Plugin Lifecycle
|
||||
1. `register()` — declare plugin to registry
|
||||
2. `init()` — plugin sets up, connects to services
|
||||
3. Active — plugin responds to requests
|
||||
4. `destroy()` — cleanup on disable/uninstall
|
||||
|
||||
## Registration
|
||||
```typescript
|
||||
import { registerPlugin } from '@aiui/core'
|
||||
|
||||
registerPlugin({
|
||||
id: 'ai-openrouter',
|
||||
name: 'OpenRouter',
|
||||
type: 'ai-provider',
|
||||
version: '1.0.0',
|
||||
async init(ctx) { /* setup */ },
|
||||
async destroy() { /* cleanup */ },
|
||||
// ... adapter methods
|
||||
})
|
||||
```
|
||||
|
||||
## Plugin Settings
|
||||
Each plugin can declare settings schema. Settings are stored encrypted and exposed through a standard settings UI.
|
||||
|
||||
## Rules
|
||||
- Every plugin must declare its type
|
||||
- Every plugin must implement `init()` and `destroy()`
|
||||
- Every plugin must implement `isAvailable()` to report its status
|
||||
- Plugins must handle errors gracefully — never crash the host
|
||||
- Community plugins must not load external scripts
|
||||
- All network requests must go through the plugin context (for privacy/proxy control)
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
description: AI adapter patterns, streaming, tool calling, context injection
|
||||
globs: "**/ai/**,**/plugins/ai-*/**"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# AI Integration
|
||||
|
||||
## Universal AI Adapter
|
||||
All AI providers connect through the `AIProviderAdapter` interface:
|
||||
|
||||
```typescript
|
||||
interface AIProviderAdapter extends AIUIPlugin {
|
||||
type: 'ai-provider'
|
||||
chat(messages: Message[], options: ChatOptions): AsyncIterable<ChatChunk>
|
||||
models(): Promise<Model[]>
|
||||
supportsStreaming: boolean
|
||||
supportsVision: boolean
|
||||
supportsTools: boolean
|
||||
supportsMultimodal: boolean
|
||||
}
|
||||
```
|
||||
|
||||
## Provider Hierarchy
|
||||
1. **OpenAI-Compatible Adapter** — covers OpenRouter, Ollama, vLLM, llama.cpp, LocalAI, Mistral, DeepSeek, xAI, Qwen. Just change `baseURL` + API key.
|
||||
2. **Anthropic Adapter** — Claude. Different tool_use format (content blocks vs tool_calls).
|
||||
3. **Gemini Adapter** — Google. Different multimodal format.
|
||||
4. **MCP Client** — connects to any MCP server for tools, resources, prompts.
|
||||
|
||||
## Streaming
|
||||
- All AI responses use Server-Sent Events (SSE) over HTTP
|
||||
- Pattern: `data: {"token": "Hello"}\n\n` with `data: [DONE]\n\n` termination
|
||||
- Client: parse SSE stream, feed tokens to `StreamingTextRenderer`
|
||||
- Always show a typing indicator while waiting for first token
|
||||
- Handle connection drops gracefully (show error, offer retry)
|
||||
|
||||
## Tool Calling
|
||||
AI can invoke tools. The adapter normalizes tool call formats:
|
||||
```typescript
|
||||
interface ToolCall {
|
||||
id: string
|
||||
name: string
|
||||
arguments: Record<string, unknown>
|
||||
}
|
||||
|
||||
interface ToolResult {
|
||||
toolCallId: string
|
||||
content: string | StructuredContent
|
||||
isError: boolean
|
||||
}
|
||||
```
|
||||
|
||||
Normalize across providers:
|
||||
- OpenAI: `tool_calls` in assistant message → `role: "tool"` result
|
||||
- Claude: `type: "tool_use"` content block → `tool_result` in user message
|
||||
- Map both to AIUI's unified `ToolCall` / `ToolResult` types
|
||||
|
||||
## Context Injection
|
||||
The system prompt includes context about the user's environment:
|
||||
- Connected media sources and their capabilities
|
||||
- Available tools and plugins
|
||||
- User preferences (language, theme, preferred wallet)
|
||||
- In dev mode: mock data summaries
|
||||
|
||||
Never include sensitive data (API keys, passwords) in system prompts.
|
||||
|
||||
## Model Selection
|
||||
Users can switch models within a conversation. The UI shows:
|
||||
- Available models from all connected providers
|
||||
- Model capabilities (vision, tools, streaming)
|
||||
- Cost per token in sats (if applicable)
|
||||
|
||||
## Dev Mode
|
||||
- `VITE_OPENROUTER_API_KEY` in `.env.local`
|
||||
- Free models available (Llama, Mistral via OpenRouter)
|
||||
- Mock tool responses available via dev fixtures
|
||||
- Debug panel shows: raw messages, token count, latency
|
||||
|
||||
## Error Handling
|
||||
- Rate limits: show user-friendly message, auto-retry with backoff
|
||||
- Auth errors: prompt to check API key in settings
|
||||
- Network errors: show offline indicator, queue message for retry
|
||||
- Model errors: show error in chat, suggest alternative model
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
description: How to build content renderers - interfaces, lazy loading, accessibility
|
||||
globs: "**/renderers/**"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Renderer Development
|
||||
|
||||
## What is a Renderer?
|
||||
A renderer is a set of Vue components that know how to display a specific content type across the five content surfaces (chat-preview, chat-play, panel-preview, panel-play, panel-edit).
|
||||
|
||||
## Renderer Registration
|
||||
```typescript
|
||||
import { registerRenderer } from '@aiui/core'
|
||||
|
||||
registerRenderer({
|
||||
id: 'film',
|
||||
name: 'Film',
|
||||
contentType: 'application/x-aiui-film',
|
||||
surfaces: ['chat-preview', 'panel-preview', 'panel-play'],
|
||||
chatPreview: () => import('./FilmChatPreview.vue'),
|
||||
panelPreview: () => import('./FilmGrid.vue'),
|
||||
panelPlay: () => import('./FilmDetail.vue'),
|
||||
})
|
||||
```
|
||||
|
||||
## Content Type Detection
|
||||
Renderers are matched to content by `contentType` field in the message data:
|
||||
```typescript
|
||||
interface ContentBlock {
|
||||
contentType: string // e.g., 'application/x-aiui-film'
|
||||
data: Record<string, unknown> // renderer-specific data
|
||||
title?: string // human-readable title for panel tab
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Rules
|
||||
1. Chat surfaces (preview, play) must render with ZERO lazy-loaded heavy dependencies
|
||||
2. Panel surfaces may lazy-load libraries (CodeMirror, pdf.js, etc.)
|
||||
3. Use `defineAsyncComponent` for panel components
|
||||
4. Show skeleton/placeholder while loading
|
||||
5. Never block the main thread — use Web Workers for heavy parsing
|
||||
|
||||
## Data Contracts
|
||||
Each renderer defines its expected data shape as a TypeScript interface:
|
||||
```typescript
|
||||
interface FilmRendererData {
|
||||
films: Film[]
|
||||
query?: string
|
||||
filters?: FilmFilters
|
||||
}
|
||||
```
|
||||
Document the interface. Validate incoming data. Show graceful error if data is malformed.
|
||||
|
||||
## Accessibility Requirements
|
||||
- All renderers must be keyboard navigable
|
||||
- Images need alt text
|
||||
- Interactive elements need ARIA labels
|
||||
- Media players need captions/transcripts when available
|
||||
- Focus management when transitioning between surfaces
|
||||
|
||||
## Mobile Behavior
|
||||
- Chat Preview: constrained to message bubble width
|
||||
- Chat Play: full message width, max 200px height
|
||||
- Panel surfaces on mobile: full-screen overlay with back gesture
|
||||
- Touch targets: minimum 44x44px
|
||||
- Swipe gestures where appropriate (image gallery, film cards)
|
||||
|
||||
## Renderer Checklist
|
||||
- [ ] TypeScript data interface defined and exported
|
||||
- [ ] All applicable surfaces implemented
|
||||
- [ ] Lazy loading for heavy dependencies
|
||||
- [ ] Skeleton/placeholder states
|
||||
- [ ] Error state (malformed data)
|
||||
- [ ] Empty state (no data)
|
||||
- [ ] Keyboard navigation
|
||||
- [ ] ARIA labels on interactive elements
|
||||
- [ ] Mobile responsive
|
||||
- [ ] Dark mode compatible
|
||||
- [ ] Transition animations (per motion design rules)
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
description: Cryptography and security rules - E2E encryption, key management, storage
|
||||
globs: "**/crypto/**,**/*.ts"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Security & Cryptography
|
||||
|
||||
## Principles
|
||||
- Privacy is a requirement, not a feature
|
||||
- Zero telemetry, zero analytics unless user explicitly opts in
|
||||
- Never transmit unencrypted sensitive data
|
||||
- Never store plaintext credentials
|
||||
- Minimal data collection — store only what's needed
|
||||
|
||||
## Encryption Stack
|
||||
|
||||
### E2E Message Encryption
|
||||
- Library: **tweetnacl.js** (6KB, audited by Cure53)
|
||||
- Algorithm: XSalsa20-Poly1305 via NaCl `box` (public-key authenticated encryption)
|
||||
- Each conversation has a shared secret derived from key exchange
|
||||
|
||||
### Local Storage Encryption
|
||||
- Library: **Web Crypto API** (native, zero bundle cost)
|
||||
- Algorithm: AES-256-GCM for encrypting IndexedDB values
|
||||
- Key derived from user's master password via PBKDF2 (100K+ iterations)
|
||||
|
||||
### Key Management
|
||||
- **Desktop (Tauri)**: OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service)
|
||||
- **Web**: Encrypted IndexedDB with user-derived key
|
||||
- **Nostr compatibility**: secp256k1 keys via @noble/curves, NIP-07 browser extension support
|
||||
- **Passkeys/WebAuthn**: For passwordless authentication
|
||||
|
||||
### Credential Storage
|
||||
- API keys encrypted at rest using AES-256-GCM
|
||||
- Never stored in localStorage (use encrypted IndexedDB or OS keychain)
|
||||
- Never included in logs, error reports, or system prompts
|
||||
- Display as masked values in settings UI (show last 4 chars only)
|
||||
|
||||
## Dev Mode Bypass
|
||||
When `VITE_DISABLE_CRYPTO=true` (dev only):
|
||||
- Skip E2E encryption (messages stored in plain text)
|
||||
- Skip storage encryption (IndexedDB unencrypted)
|
||||
- API keys stored in `.env.local` (gitignored)
|
||||
- This flag must NEVER exist in production builds
|
||||
|
||||
## Security Rules for Code
|
||||
- Never log sensitive data (keys, tokens, passwords, message content)
|
||||
- Never include secrets in error messages
|
||||
- Sanitize all user input before rendering (XSS prevention)
|
||||
- Use Content Security Policy headers
|
||||
- Validate all data from plugins before rendering
|
||||
- Community plugins run in sandboxed iframes (no direct DOM access)
|
||||
- Never eval() or innerHTML with untrusted content
|
||||
|
||||
## Network Security
|
||||
- All external requests over HTTPS only
|
||||
- Certificate pinning for known services (Tauri)
|
||||
- Proxy social media fetches to avoid leaking user IP
|
||||
- No third-party tracking scripts, analytics, or telemetry SDKs
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
description: Bitcoin-only payment and monetary policy - on-chain, Lightning, ecash
|
||||
globs: "**/*"
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Bitcoin Only
|
||||
|
||||
## Core Rule
|
||||
Bitcoin is the only monetary unit in AIUI. This applies everywhere — UI labels, data models, API responses, documentation, and conversation context.
|
||||
|
||||
## Supported Payment Protocols
|
||||
- **On-chain Bitcoin**: BIP21 URI scheme (`bitcoin:bc1q...?amount=0.001`)
|
||||
- **Lightning Network**: BOLT11 invoices, LNURL-pay, LNURL-withdraw, keysend
|
||||
- **Cashu ecash**: Cashu tokens, mint interactions (`cashu:` URI, `web+cashu:`)
|
||||
- **Fedimint/Fedi**: Federation ecash (`fedi:` URI)
|
||||
- **Nostr Zaps**: NIP-57 Lightning zaps (social tipping)
|
||||
|
||||
## AIUI is NEVER a Wallet
|
||||
|
||||
### Never Do
|
||||
- Store private keys or seed phrases
|
||||
- Sign Bitcoin transactions
|
||||
- Build or broadcast transactions
|
||||
- Track wallet balances
|
||||
- Display transaction history
|
||||
- Create send/receive screens
|
||||
- Implement payment processing logic
|
||||
- Hold funds in custody
|
||||
|
||||
### Always Do
|
||||
- Construct deep-link URIs and hand off to external wallet apps
|
||||
- Detect installed wallet apps (via URI scheme probing or Tauri app detection)
|
||||
- Let users configure preferred wallets in settings
|
||||
- Display payment requests as QR codes with "Open in Wallet" buttons
|
||||
- Show invoice/address details (amount, memo, expiry) as read-only information
|
||||
|
||||
## Wallet Deep-Linking
|
||||
```typescript
|
||||
// Construct URI, open external wallet — that's it
|
||||
const uri = `lightning:${bolt11Invoice}`
|
||||
window.open(uri) // or Tauri shell.open(uri)
|
||||
```
|
||||
|
||||
Supported wallet URI schemes:
|
||||
- `bitcoin:` — BIP21 (any on-chain wallet)
|
||||
- `lightning:` — BOLT11 (any Lightning wallet)
|
||||
- `cashu:` — Cashu tokens
|
||||
- `fedi:` — Fedimint
|
||||
- Wallet-specific: `phoenix://`, `zeus://`, `mutiny://`, `alby://`
|
||||
|
||||
## Denomination
|
||||
- Primary unit: **sats** (1 BTC = 100,000,000 sats)
|
||||
- Display: `1,234 sats` or `₿0.00001234`
|
||||
- User preference: sats or BTC (configurable in settings)
|
||||
- AI cost tracking: show token costs in sats
|
||||
|
||||
## Prohibited
|
||||
- No fiat currencies (USD, EUR, etc.) — not in UI, not in code, not in variable names
|
||||
- No altcoins or tokens
|
||||
- No stablecoins (USDT, USDC, etc.)
|
||||
- No fiat-denominated pricing
|
||||
- No payment processor integrations (Stripe, PayPal, etc.)
|
||||
- No KYC/AML flows
|
||||
|
||||
## Renderer Components
|
||||
- `LightningInvoiceRenderer` — BOLT11 QR + amount + memo + "Open in Wallet"
|
||||
- `BitcoinAddressRenderer` — BIP21 QR + "Open in Wallet"
|
||||
- `CashuTokenRenderer` — ecash token + mint info + "Redeem in Wallet"
|
||||
- `FedimintRenderer` — federation ecash + "Open in Fedi"
|
||||
- `PaymentRequestRenderer` — unified card with payment method options
|
||||
- `ZapRenderer` — Nostr zap display (NIP-57)
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
description: Development vs production configuration, feature flags, mock data patterns
|
||||
globs: "**/*"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Dev & Prod Modes
|
||||
|
||||
## Development Mode
|
||||
|
||||
### Environment
|
||||
```env
|
||||
# .env.local (gitignored)
|
||||
VITE_OPENROUTER_API_KEY=sk-or-...
|
||||
VITE_TMDB_API_KEY=...
|
||||
VITE_DEV_MODE=true
|
||||
VITE_MOCK_MEDIA_SOURCES=true
|
||||
VITE_DISABLE_CRYPTO=true
|
||||
```
|
||||
|
||||
### What's Enabled
|
||||
- Hot reload via Vite HMR
|
||||
- Debug panel overlay (AI context, plugin status, renderer registry, message data)
|
||||
- Mock media source plugins (Plex, YouTube, Nextcloud from JSON fixtures)
|
||||
- OpenRouter AI connection (real API, free models available)
|
||||
- Component playground (Storybook/Histoire)
|
||||
- Verbose logging
|
||||
- TypeScript strict mode
|
||||
- All renderers available without lazy loading (for dev speed)
|
||||
|
||||
### What's Disabled
|
||||
- E2E encryption (plain text messages for debugging)
|
||||
- Storage encryption (plain IndexedDB)
|
||||
- Tauri features (dev runs as pure web app)
|
||||
- Production optimizations (tree-shaking, minification)
|
||||
- Service worker / offline mode
|
||||
|
||||
### Mock Data
|
||||
- Film fixtures: 50-100 films with real TMDB poster URLs
|
||||
- Media source mocks: JSON files returning fake Plex/YouTube/Nextcloud responses
|
||||
- Located in: `packages/app/src/mocks/`
|
||||
- Auto-loaded when `VITE_MOCK_MEDIA_SOURCES=true`
|
||||
- Mock data must match production data interfaces exactly
|
||||
|
||||
### Dev Scripts
|
||||
```
|
||||
pnpm dev # Web dev server
|
||||
pnpm dev:desktop # Tauri dev (when needed)
|
||||
pnpm storybook # Component playground
|
||||
pnpm test # Vitest
|
||||
pnpm lint # ESLint + Prettier
|
||||
pnpm typecheck # TypeScript
|
||||
pnpm build # Production build
|
||||
pnpm turbo build # Turborepo cached build
|
||||
```
|
||||
|
||||
## Production Mode
|
||||
|
||||
### What's Enabled
|
||||
- E2E encryption for all messages
|
||||
- Encrypted local storage
|
||||
- Key management via OS keychain (Tauri) or encrypted IndexedDB (web)
|
||||
- User-configured AI providers (settings page)
|
||||
- Real media source connections (Plex API, YouTube, etc.)
|
||||
- Optimized builds (tree-shaken, code-split, minified)
|
||||
- Lazy loading for all heavy renderers
|
||||
- Service worker for offline support
|
||||
- Auto-update (Tauri)
|
||||
|
||||
### What's Disabled
|
||||
- Debug panels
|
||||
- Mock data
|
||||
- Dev logging
|
||||
- Source maps (in distributed builds)
|
||||
- `VITE_DISABLE_CRYPTO` flag (must not exist)
|
||||
|
||||
### Build Targets
|
||||
- Web: Static SPA bundle (< 250KB initial gzipped)
|
||||
- Desktop: Tauri app (macOS .dmg, Windows .msi, Linux .AppImage)
|
||||
- Mobile: Tauri mobile (iOS .ipa, Android .apk)
|
||||
|
||||
## Feature Flags
|
||||
Use composable `useFeatureFlags()`:
|
||||
```typescript
|
||||
const { isDev, isTauri, isMobile, isCryptoEnabled, isMockData } = useFeatureFlags()
|
||||
```
|
||||
|
||||
Gate platform-specific features:
|
||||
```typescript
|
||||
if (isTauri()) {
|
||||
// Native file system access
|
||||
} else {
|
||||
// File System Access API or file picker
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variable Rules
|
||||
- All env vars prefixed with `VITE_` (Vite requirement)
|
||||
- Secrets only in `.env.local` (gitignored)
|
||||
- `.env.example` committed with placeholder values
|
||||
- Never read `process.env` directly — use typed config module
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
description: Accessibility standards - WCAG AA, keyboard navigation, screen readers
|
||||
globs: "**/*.vue"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Accessibility
|
||||
|
||||
## Standard
|
||||
WCAG AA compliance minimum. Target AAA where feasible.
|
||||
|
||||
## Color Contrast
|
||||
- Normal text: 4.5:1 minimum ratio
|
||||
- Large text (18px+ or 14px+ bold): 3:1 minimum
|
||||
- Interactive elements: 3:1 against adjacent colors
|
||||
- Test with browser DevTools accessibility panel
|
||||
|
||||
## Keyboard Navigation
|
||||
- All interactive elements focusable via Tab
|
||||
- Visible focus indicators on every focusable element (`focus:ring-2`)
|
||||
- Escape closes modals, drawers, dropdowns
|
||||
- Arrow keys navigate within lists, grids, tabs
|
||||
- Enter/Space activates buttons and controls
|
||||
- Focus trap inside modals (Tab cycles within modal)
|
||||
|
||||
## Semantic HTML
|
||||
```html
|
||||
<header>, <nav>, <main>, <article>, <aside>, <footer>
|
||||
```
|
||||
Never `<div class="header">`. Use semantic elements.
|
||||
|
||||
## ARIA
|
||||
- Icon-only buttons: `aria-label="Close modal"`
|
||||
- Dynamic content: `aria-live="polite"` for updates
|
||||
- Screen reader only text: `class="sr-only"`
|
||||
- Expandable sections: `aria-expanded="true/false"`
|
||||
- Form fields: `aria-describedby` for help text, `aria-invalid` for errors
|
||||
|
||||
## Images
|
||||
- All `<img>` tags need `alt` text
|
||||
- Decorative images: `alt=""`
|
||||
- Complex images: `aria-describedby` pointing to description
|
||||
|
||||
## Media
|
||||
- Audio/video players: keyboard-accessible controls
|
||||
- Provide transcripts/captions when available
|
||||
- Respect `prefers-reduced-motion` for animations
|
||||
|
||||
## Touch Targets
|
||||
- Minimum: 44x44px (Apple HIG)
|
||||
- Recommended: 48x48px (Material Design)
|
||||
- Minimum 8px gap between adjacent targets
|
||||
|
||||
## Reduced Motion
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
Check in JS: `window.matchMedia('(prefers-reduced-motion: reduce)').matches`
|
||||
|
||||
## Testing
|
||||
- VoiceOver (macOS), TalkBack (Android), NVDA (Windows)
|
||||
- Keyboard-only navigation test
|
||||
- axe DevTools or Lighthouse accessibility audit
|
||||
- High contrast mode test
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
description: Performance optimization - bundle budget, lazy loading, virtual scrolling
|
||||
globs: "**/*"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Performance
|
||||
|
||||
## Bundle Budget
|
||||
- Initial load: **< 250KB gzipped**
|
||||
- Core (Vue + Tailwind + Pinia + Router + chat UI): ~150KB
|
||||
- First renderer batch (markdown, streaming text): ~50KB
|
||||
- Everything else: lazy-loaded on demand
|
||||
|
||||
## Lazy Loading Strategy
|
||||
- Route-based code splitting via Vue Router `() => import(...)`
|
||||
- Renderer components via `defineAsyncComponent`
|
||||
- Heavy libraries loaded only when their renderer is activated:
|
||||
- CodeMirror 6: ~300KB (on code edit)
|
||||
- Monaco: ~5MB (on IDE panel open)
|
||||
- pdf.js: ~400KB (on PDF view)
|
||||
- KaTeX: ~300KB (on math render)
|
||||
- Mermaid: ~200KB (on diagram render)
|
||||
- Leaflet: ~40KB (on map render)
|
||||
- Whisper WASM: ~50MB (on STT activation, cached)
|
||||
- Piper TTS: ~100MB (on TTS activation, cached)
|
||||
|
||||
## Virtual Scrolling
|
||||
- Chat message list uses TanStack Virtual
|
||||
- Dynamic row heights (messages vary in size)
|
||||
- Inverted scroll (newest at bottom, load older on scroll up)
|
||||
- Buffer: render 5 items above and below viewport
|
||||
- Recycle DOM nodes for off-screen messages
|
||||
|
||||
## GPU Acceleration
|
||||
Only animate `transform` and `opacity` — never `width`, `height`, `top`, `left`.
|
||||
Use `will-change` sparingly and remove after animation.
|
||||
|
||||
## Image Optimization
|
||||
- Use `loading="lazy"` on all non-critical images
|
||||
- Provide `srcset` with multiple sizes
|
||||
- Use WebP/AVIF where supported
|
||||
- Skeleton placeholders while loading
|
||||
|
||||
## Network
|
||||
- Preconnect to known API hosts
|
||||
- Preload critical resources
|
||||
- Debounce scroll and resize handlers (100ms)
|
||||
- Batch API requests where possible
|
||||
|
||||
## Memory
|
||||
- Clean up event listeners in `onUnmounted`
|
||||
- Use `shallowRef` for large data sets
|
||||
- Dispose heavy library instances when panel closes
|
||||
- Monitor memory with browser DevTools
|
||||
|
||||
## Core Web Vitals Targets
|
||||
- LCP (Largest Contentful Paint): < 2.5s
|
||||
- FID (First Input Delay): < 100ms
|
||||
- CLS (Cumulative Layout Shift): < 0.1
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
description: Animation principles - timing, easing, stagger, reduced motion
|
||||
globs: "**/*.vue,**/*.css"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Animation & Motion Design
|
||||
|
||||
## Philosophy
|
||||
Every animation serves a purpose: guide attention, provide feedback, show relationships, enhance perceived performance, or add delight. Never animate for decoration alone.
|
||||
|
||||
## Duration Scale
|
||||
```
|
||||
100ms - Instant: micro-feedback (hover states, button press)
|
||||
200ms - Fast: small elements (tooltips, dropdowns)
|
||||
300ms - Moderate: standard UI transitions (modals, cards)
|
||||
500ms - Normal: page sections, complex components
|
||||
600ms - Slow: hero animations, page transitions (max for UI)
|
||||
```
|
||||
Never exceed 600ms for UI element animations.
|
||||
|
||||
## Easing Functions
|
||||
- **ease-out** (90% of animations): elements entering viewport
|
||||
- **ease-in**: elements exiting viewport
|
||||
- **ease-in-out**: elements moving within viewport
|
||||
- **spring**: playful interactions (button press, drag-and-drop)
|
||||
- **linear**: progress bars, loading spinners only
|
||||
|
||||
Custom smooth deceleration: `cubic-bezier(0.16, 1, 0.3, 1)`
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Fade & Slide Up (entrance)
|
||||
```css
|
||||
@keyframes fadeSlideUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
```
|
||||
|
||||
### Scale & Fade (emphasis)
|
||||
```css
|
||||
@keyframes scaleIn {
|
||||
from { opacity: 0; transform: scale(0.8); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
```
|
||||
|
||||
### Hover feedback
|
||||
```css
|
||||
.interactive {
|
||||
transition: transform 0.1s ease, opacity 0.1s ease;
|
||||
}
|
||||
.interactive:active {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
```
|
||||
|
||||
## Staggered Animations
|
||||
When animating multiple elements, stagger by 50-150ms per item:
|
||||
```css
|
||||
.card { animation-delay: calc(var(--index) * 0.1s); }
|
||||
```
|
||||
Max items in a stagger cascade: 6-8. Total cascade: under 1 second.
|
||||
|
||||
## Reduced Motion
|
||||
Always respect `prefers-reduced-motion`. Provide instant transitions as fallback.
|
||||
|
||||
## Performance
|
||||
- Only animate `transform` and `opacity` (GPU-composited)
|
||||
- Use `will-change` sparingly, remove after animation
|
||||
- Limit simultaneous animations
|
||||
- Use `requestAnimationFrame` for JS animations
|
||||
|
||||
## Loading States
|
||||
- Skeleton shimmer: 2s infinite, `linear-gradient` sweep
|
||||
- Pulse: 2s infinite, opacity 1 → 0.5 → 1
|
||||
- Spinner: 1s infinite linear rotation
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
description: Mobile UX patterns - touch targets, gestures, safe areas, viewport
|
||||
globs: "**/*.vue,**/*.css"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Mobile UX
|
||||
|
||||
## Philosophy
|
||||
Design for mobile first, enhance for desktop. Mobile constraints force focus on essential features.
|
||||
|
||||
## Touch Targets
|
||||
- Minimum: 44x44px (Apple HIG)
|
||||
- Optimal: 48x48px (Material Design)
|
||||
- Minimum gap between targets: 8px
|
||||
- Icon buttons: use padding to reach target size (`p-3` for 20px icon = 44px total)
|
||||
|
||||
## Thumb-Friendly Zones
|
||||
```
|
||||
Top 20%: Hard to reach — header, info
|
||||
Middle 60%: Easy reach — main content
|
||||
Bottom 20%: Natural thumb zone — primary actions
|
||||
```
|
||||
Place primary actions (send, approve, play) in the bottom zone. Use FABs at bottom-center on mobile, bottom-right on desktop.
|
||||
|
||||
## Gestures
|
||||
- Swipe left/right: gallery navigation, dismiss
|
||||
- Swipe down: close overlay/bottom sheet, pull-to-refresh
|
||||
- Long press: context menu, selection
|
||||
- Pinch: zoom on images
|
||||
- Minimum swipe distance: 50px before triggering
|
||||
|
||||
## Viewport & Safe Areas
|
||||
```css
|
||||
/* Dynamic viewport height (avoids iOS address bar issue) */
|
||||
height: 100dvh;
|
||||
|
||||
/* Safe area insets for notched devices */
|
||||
padding-top: env(safe-area-inset-top);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
```
|
||||
|
||||
## Scroll Behavior
|
||||
- Lock body scroll when modal/drawer is open
|
||||
- `overscroll-behavior: contain` on modal content
|
||||
- `touch-action: manipulation` to prevent zoom on double-tap
|
||||
- `-webkit-overflow-scrolling: touch` for smooth iOS scroll
|
||||
|
||||
## Form Inputs
|
||||
- Font size minimum 16px on inputs (prevents iOS zoom on focus)
|
||||
- Use appropriate `inputmode` (numeric, email, tel, url)
|
||||
- Use `autocomplete` attributes
|
||||
- Auto-focus next input on completion (OTP pattern)
|
||||
|
||||
## Content Panel on Mobile
|
||||
- No side-by-side layout (chat + panel)
|
||||
- Panel opens as full-screen overlay or bottom sheet
|
||||
- Bottom sheet: drag handle at top, swipe down to dismiss
|
||||
- Back gesture or button returns to chat
|
||||
- Maintain scroll position when returning
|
||||
|
||||
## Orientation
|
||||
Support both portrait and landscape. Adjust layout with:
|
||||
```css
|
||||
@media (orientation: landscape) { /* landscape overrides */ }
|
||||
```
|
||||
|
||||
## Performance on Mobile
|
||||
- Test on real devices, not just emulators
|
||||
- Test on 3G/4G connections
|
||||
- Debounce scroll handlers
|
||||
- Lazy load images with `loading="lazy"`
|
||||
- Critical CSS inlined, rest loaded async
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
description: Git workflow - commit conventions, branching, PR process
|
||||
globs: "**/*"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Git Workflow
|
||||
|
||||
## Commit Messages
|
||||
Format: `type(scope): description`
|
||||
|
||||
Types:
|
||||
- `feat`: new feature
|
||||
- `fix`: bug fix
|
||||
- `refactor`: code restructuring (no behavior change)
|
||||
- `style`: formatting, whitespace (no code change)
|
||||
- `docs`: documentation
|
||||
- `test`: adding/updating tests
|
||||
- `chore`: build, dependencies, tooling
|
||||
- `perf`: performance improvement
|
||||
|
||||
Scope: the package or area (`core`, `app`, `plugin-x`, `renderer-film`, etc.)
|
||||
|
||||
Examples:
|
||||
```
|
||||
feat(core): add renderer registry with lazy loading
|
||||
fix(chat): prevent scroll jump on new message
|
||||
refactor(plugin-system): simplify adapter interface
|
||||
chore(deps): update Vue to 3.6
|
||||
```
|
||||
|
||||
## Branching
|
||||
- `main`: production-ready, always deployable
|
||||
- `dev`: integration branch for features
|
||||
- `feat/description`: feature branches (from dev)
|
||||
- `fix/description`: bug fix branches
|
||||
- `release/x.y.z`: release preparation
|
||||
|
||||
## Pull Requests
|
||||
- One feature per PR
|
||||
- Description: what changed, why, how to test
|
||||
- All tests pass
|
||||
- TypeScript strict mode passes
|
||||
- No linter errors
|
||||
- Reviewed before merge
|
||||
|
||||
## Rules
|
||||
- Never force push to `main` or `dev`
|
||||
- Never commit `.env.local` or any secrets
|
||||
- Never commit `node_modules`
|
||||
- Squash merge feature branches to keep history clean
|
||||
- Tag releases with semver: `v1.0.0`
|
||||
Reference in New Issue
Block a user