diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml new file mode 100644 index 0000000..c9faeb1 --- /dev/null +++ b/.github/workflows/tauri-build.yml @@ -0,0 +1,71 @@ +name: Build Desktop App + +on: + push: + tags: ['v*'] + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - platform: macos-latest + args: --target aarch64-apple-darwin + rust_target: aarch64-apple-darwin + - platform: macos-latest + args: --target x86_64-apple-darwin + rust_target: x86_64-apple-darwin + - platform: ubuntu-22.04 + args: '' + rust_target: '' + - platform: windows-latest + args: '' + rust_target: '' + + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: website/package-lock.json + + - working-directory: ./website + run: npm ci + + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.rust_target }} + + - uses: swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - name: Install Linux deps + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + + # macOS builds are ad-hoc signed (signingIdentity: "-") — not notarized, + # not distributable via Gatekeeper outside of dev/internal use. + # Pinned to v0 tag SHA to avoid floating-tag supply chain risk. + - uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_BUILD: '1' + with: + tagName: ${{ github.ref_name }} + releaseName: 'Agentic Coding ${{ github.ref_name }}' + releaseBody: 'Desktop build of Agentic Coding reference book.' + releaseDraft: true + prerelease: false + args: ${{ matrix.args }} diff --git a/.gitignore b/.gitignore index 52a4330..a8555ae 100644 --- a/.gitignore +++ b/.gitignore @@ -35,8 +35,17 @@ node_modules/ # Generated content scripts/output/ +# Noto Emoji SVG cache (fetched at runtime) +scripts/.noto-cache/ + # Screenshots *.png *.jpg *.jpeg -package-lock.json +!src-tauri/icons/*.png +/package-lock.json + +# Tauri +src-tauri/target/ +src-tauri/gen/ +# Cargo.lock is intentionally tracked (application binary, not library) diff --git a/ANIMATION_GUIDE.md b/ANIMATION_GUIDE.md new file mode 100644 index 0000000..e81f61a --- /dev/null +++ b/ANIMATION_GUIDE.md @@ -0,0 +1,1316 @@ +# Animation System Generation Guide for AI Agents + +Production animation system generation using perceptual timing research, easing curve psychophysics, spring mechanics, PAD-mapped motion personality, and scroll-driven reveal composition. Designed as agent-executable specification — every formula is code-ready. Calibrated for this project's specific design system, illustration vocabulary, and emotional targets. + +This guide is the motion counterpart to `COLOR_GUIDE.md` (color science, palette engineering), `SPATIAL_GUIDE.md` (spacing, curvature, proportions), and `ILLUSTRATION_GUIDE.md` (shape grammar, diagram construction). It takes three inputs — the validated color palette, spatial token system, and brand PAD emotional profile — and produces a complete, validated animation system: duration tokens, easing curves, spring presets, stagger algorithms, reveal sequences, and scroll-driven motion rules. + +Two phases: **Motion Strategy** (human-driven brand personality decisions grounded in perception science) and **Motion Engineering** (agent-executable math producing token values). The first phase establishes the kinetic personality of the brand. The second phase computes every token from that personality. + +**Prerequisite:** Complete `COLOR_GUIDE.md` Phases 1–3, `SPATIAL_GUIDE.md` Phases 1–2, and `ILLUSTRATION_GUIDE.md` Phases 1–4 first. This guide references the PAD emotional model, shape vocabulary, spatial token architecture, and semantic hue roles defined there. + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherence!** + +--- + +## Perceptual Timing Theory + +The foundation for all animation token values. These thresholds are the most empirically robust numbers in HCI — grounded in two landmark papers (Miller 1968; Card, Robertson & Mackinlay 1991) and replicated across five decades of usability research. + +### Human Perception Time Constants + +| Threshold | Value | Perceptual Meaning | Source | +|-----------|-------|-------------------|--------| +| Motion perception floor | ~40 ms | Below this, motion registers as a jump cut. No transition perceived. | Physiological: rod photoreceptor response | +| Instantaneous perception | ≤ 100 ms | System feels like a direct extension of user action. Causal link preserved. | Miller (1968); Nielsen (1993) | +| Action–feedback causal window | 100 ms | Any animation triggered by user action **must begin** within 100 ms or causality breaks | Miller (1968) via NNGroup | +| Human visual perception cycle | ~230 ms | Model Human Processor average perceptual cycle. Below this, sequential events merge. | Card, Moran & Newell (1983) | +| Animation fatigue onset | ≥ 500 ms | Anything longer than 400–500 ms is a net negative for user-triggered UI motion | NNGroup usability observations | +| Flow-of-thought disruption | > 1,000 ms | User feels "waited on." Mental task model breaks. Progress indicator required. | Miller (1968); Nielsen (1993) | +| Frame rate ceiling | 60 Hz (16.7 ms/frame) | Humans cannot reliably perceive differences above 60 fps | Physiological: flicker fusion | + +**Critical insight:** The 100 ms threshold applies to when animation *begins*, not when it ends. An animation with a `200ms animation-delay` before it starts creates a 200 ms blank gap that reads as system latency, not intentional design. The first frame of motion must appear within 100 ms of the triggering event. + +### Enter vs. Exit Asymmetry + +NNGroup documents a consistent directional asymmetry: elements entering the screen should animate ~25% longer than elements exiting. The asymmetry reflects the user's mental state: + +- **Enter (25% longer):** The user is waiting for content to appear. A slightly longer entrance feels considered. +- **Exit (25% shorter):** The user has already acted to dismiss. A fast exit feels responsive. + +``` +exit_duration = round(enter_duration × 0.75) +``` + +This rule applies to all enter/exit pairs: modals, drawers, dropdowns, tooltips, and scroll-reveal elements. + +### Perceived Performance Effects + +**Study (Nebraska-Lincoln, cited by NNGroup):** Users who saw a continuous moving progress bar were willing to wait **3× longer** than users who saw nothing. Satisfaction was higher even when objective wait time was identical. + +**NNGroup slideshow case study:** A widget that took 8 seconds to download received only **1%** of eye-tracking attention when users waited. Users who saw the completed version immediately spent **20%** of their time in that area. Conclusion: animation that substitutes for content is severely penalized. Animation that accompanies content arrival is beneficial. + +**Operative rule:** Reveal animations are beneficial to perceived performance only when they begin *as content arrives* (paint-time), not after it. Stagger sequences must complete within the first paint window (< 500 ms from first paint). + +--- + +## Phase 1: Motion Strategy (Human Judgment) + +Before computing animation tokens, ground the motion decisions in PAD-congruent personality. The agent assists with PAD alignment checks; the human makes the judgment call on brand kinetic personality. + +### Kinetic Personality Framework + +Animation operates on all three PAD axes simultaneously: + +| Motion Property | Primary PAD Axis | Direction | Research Basis | +|----------------|-----------------|-----------|---------------| +| Duration (speed) | Arousal | Faster ↑ Arousal; Slower ↓ Arousal | NNGroup timing research; Card et al. 1983 | +| Easing curve type | Pleasure | Expressive (bouncy) ↑ Pleasure; Productive (precise) ↓ Pleasure | IBM Carbon expressive/productive distinction | +| Translate distance | Arousal | Larger offset ↑ Arousal | Practitioner convergence (Valhead, Comeau) | +| Spring bounce | Pleasure | Higher bounce ↑ Pleasure | Apple WWDC 2023 spring animation principles | +| Stagger spread | Arousal | Tighter stagger ↑ Arousal (compressed reveal) | Card/Moran/Newell perception cycle threshold | +| Animation direction | — | Semantic only — matches spatial content model | NNGroup directionality research | +| Scale amplitude | Arousal | Larger scale range ↑ Arousal | Practitioner observation | + +### Motion Style: Productive vs. Expressive + +IBM Carbon (the most precisely specified open design system) formalizes two motion styles. The distinction maps directly to brand PAD target: + +| Style | Easing Character | Duration Bias | PAD Target | Appropriate For | +|-------|-----------------|---------------|------------|----------------| +| **Productive** | Sharp ease-out, precise settle | Shorter (50–300 ms) | Low A, Med D | Task UIs, technical tools, documentation | +| **Expressive** | Broader arc, more dramatic | Longer (200–500 ms) | High P, High A | Marketing, onboarding, consumer apps | + +**Design rule:** Select one style as the system default. Use the other sparingly — at most 1–2 "expressive moments" per page (hero entrance, primary CTA activation). Mixing styles without a hierarchy creates visual incoherence. + +### Kinetic Personality by Brand PAD Profile + +| Target PAD | Duration Bias | Stagger | Easing | Spring Bounce | Translate | +|-----------|--------------|---------|--------|---------------|-----------| +| Low A, Low D, High P (calm, warm) | Longer (200–350 ms) | 80–100 ms | Expressive entrance | 0.10–0.15 | 8–12px | +| Low A, Med D (technical, precise) | Moderate (150–250 ms) | 60–80 ms | Productive entrance | 0.0 | 8px | +| Med A, Med D (balanced, professional) | Moderate (200–300 ms) | 70–90 ms | Productive standard | 0.0–0.10 | 8–12px | +| High A, High D (energetic, urgent) | Shorter (100–200 ms) | 40–60 ms | Productive exit-biased | 0.0 | 4–8px | + +### Input: Brand Motion Profile + +The only human judgment calls. Everything downstream is computable math. + +| Parameter | Value | Rationale | +|-----------|-------|-----------| +| Target Pleasure | Low / Medium / High | From brand PAD profile | +| Target Arousal | Low / Medium / High | From brand PAD profile | +| Target Dominance | Low / Medium / High | From brand PAD profile | +| Motion style | Productive / Expressive / Hybrid | From brand context (task vs. marketing) | +| Default entrance type | Fade-translate / Fade-only / Scale | From content type and diagram density | + +--- + +## Phase 2: Motion Engineering (Agent Math) + +From this point forward, the agent generates the animation token system autonomously. The human provides perceptual feedback during validation. + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherence!** + +### Duration Token Generation + +#### Base Duration Formula + +```python +def compute_duration_tokens(arousal_level: str) -> dict: + """ + Generate duration tokens from brand arousal target. + + arousal_level: 'low' | 'medium' | 'high' + + Anchored to IBM Carbon's duration system (fast-01 through slow-02) + with arousal-based scaling. + + Research basis: + - 70ms floor: IBM Carbon fast-01 (toggle/button state) + - 400ms ceiling: NNGroup animation ceiling for user-triggered transitions + - 700ms ambient: IBM Carbon slow-02 (background overlay only) + """ + + # Base values from IBM Carbon, validated against NNGroup thresholds + BASE_TOKENS = { + 'instant': 70, # toggle, checkbox, button state — at perception floor + 'fast': 110, # opacity/color — no spatial displacement + 'subtle': 150, # small spatial move (<= 16px), icon swap + 'moderate': 240, # modal, drawer, dropdown, notification + 'deliberate': 400, # large panel, hero entrance, full-section reveal + 'ambient': 700, # background overlay, dimming — not user-triggered + } + + # Arousal scaling: lower arousal = slightly longer (more deliberate) + # Upper-bounded so we never exceed 500ms for user-triggered actions + AROUSAL_SCALE = { + 'low': 1.15, # calm, deliberate — extend by 15% + 'medium': 1.00, # balanced — no adjustment + 'high': 0.85, # energetic — compress by 15% + } + + scale = AROUSAL_SCALE[arousal_level] + tokens = {} + + for name, base_ms in BASE_TOKENS.items(): + if name == 'ambient': + # Ambient is always fixed — not user-triggered + tokens[name] = base_ms + else: + raw = base_ms * scale + # Round to nearest 10ms for legibility + tokens[name] = round(raw / 10) * 10 + # Enforce perception floor: never below 40ms + tokens[name] = max(40, tokens[name]) + # Enforce user-trigger ceiling: never above 500ms + if name != 'deliberate': + tokens[name] = min(500, tokens[name]) + + # Compute exit variants (75% of enter duration per NNGroup asymmetry rule) + for name in list(tokens.keys()): + if name not in ('instant', 'ambient'): + exit_ms = round(tokens[name] * 0.75 / 10) * 10 + tokens[f'{name}_exit'] = max(40, exit_ms) + + return tokens + +# Example execution for medium arousal (technical documentation brand): +# tokens = compute_duration_tokens('medium') +# Output: +# instant: 70ms (exit: n/a — state changes have no exit) +# fast: 110ms (exit: 80ms) +# subtle: 150ms (exit: 110ms) +# moderate: 240ms (exit: 180ms) +# deliberate: 400ms (exit: 300ms) +# ambient: 700ms (exit: n/a — ambient is always fade, no exit) +``` + +#### Duration Token CSS Output + +```css +/* Animation duration tokens + Generated from: compute_duration_tokens(arousal_level) + Research: IBM Carbon duration scale; NNGroup 100–500ms window; Miller (1968) */ + +--duration-instant: 70ms; /* toggle, checkbox, button state */ +--duration-fast: 110ms; /* opacity/color, badge update */ +--duration-subtle: 150ms; /* small spatial move, icon swap */ +--duration-moderate: 240ms; /* modal, drawer, dropdown */ +--duration-deliberate: 400ms; /* large panel, hero, section reveal */ +--duration-ambient: 700ms; /* background overlay only — not user-triggered */ + +/* Exit variants — 75% of enter (NNGroup asymmetry rule) */ +--duration-fast-exit: 80ms; +--duration-subtle-exit: 110ms; +--duration-moderate-exit: 180ms; +--duration-deliberate-exit: 300ms; +``` + +--- + +### Easing Curve Generation + +#### Easing Semantics + +All design system authorities converge on three mandatory custom curves. The CSS default `ease` keyword must NOT be used — it is tuned for neither enter nor exit and produces a sluggish feeling. Use `cubic-bezier()` always. + +| Direction | Curve Behavior | Semantic Role | +|-----------|---------------|---------------| +| **Enter (ease-out)** | Fast start, slow finish | Elements arriving — decelerate to rest naturally | +| **Exit (ease-in)** | Slow start, fast finish | Elements departing — accelerate, feel intentionally leaving | +| **Reposition (ease-in-out)** | Slow–fast–slow | Elements traversing from visible A to visible B | +| **Linear** | Constant velocity | Spinners, progress bars, video scrubbing only | + +**Polaris (Shopify) principle:** "A snappy animation starts rapidly and slows down toward the end." This describes ease-out as the default for most UI motion — content decelerates into its final position so it becomes readable as quickly as possible. + +#### Easing Curve Formula + +```python +def compute_easing_tokens(motion_style: str, pleasure_level: str) -> dict: + """ + Generate cubic-bezier easing tokens. + + motion_style: 'productive' | 'expressive' + pleasure_level: 'low' | 'medium' | 'high' + + Productive curves: sharper arcs, tighter control points — task focus + Expressive curves: broader arcs, more dramatic deceleration — emotional moments + + Source: IBM Carbon easing system; validated against Material Design M3 easing + """ + + CURVES = { + 'productive': { + # IBM Carbon productive style + 'enter': (0.00, 0.00, 0.38, 0.9), # steep entry, long ease-out tail + 'exit': (0.20, 0.00, 1.00, 0.9), # slow start, sharp acceleration out + 'standard': (0.20, 0.00, 0.38, 0.9), # within-viewport repositioning + }, + 'expressive': { + # IBM Carbon expressive style — broader arc, more dramatic + 'enter': (0.00, 0.00, 0.30, 1.0), # very fast entry, long ease-out + 'exit': (0.40, 0.14, 1.00, 1.0), # slow start, high-energy exit + 'standard': (0.40, 0.14, 0.30, 1.0), # dramatic repositioning + } + } + + # Pleasure interpolation: high pleasure → blend toward expressive even in productive systems + # This adds subtle warmth to the productive curves without full expressive adoption + curves = CURVES[motion_style].copy() + + if motion_style == 'productive' and pleasure_level == 'high': + # Soften the productive enter curve slightly toward expressive + curves['enter'] = (0.00, 0.00, 0.32, 0.95) + + # Material Design M3 equivalents for reference validation + # enter (ease-out): cubic-bezier(0.05, 0.7, 0.1, 1.0) ← M3 standard + # exit (ease-in): cubic-bezier(0.3, 0.0, 1.0, 1.0) ← M3 standard + # symmetric (ease-in-out): cubic-bezier(0.2, 0.0, 0.0, 1.0) ← M3 standard + + return { + 'enter': f'cubic-bezier{curves["enter"]}', + 'exit': f'cubic-bezier{curves["exit"]}', + 'standard': f'cubic-bezier{curves["standard"]}', + 'linear': 'linear', + } + +# For productive motion style (technical documentation, medium pleasure): +# enter: cubic-bezier(0.00, 0.00, 0.38, 0.9) +# exit: cubic-bezier(0.20, 0.00, 1.00, 0.9) +# standard: cubic-bezier(0.20, 0.00, 0.38, 0.9) +# linear: linear +``` + +#### Easing Token CSS Output + +```css +/* Easing curve tokens + Generated from: compute_easing_tokens(motion_style, pleasure_level) + Research: IBM Carbon productive/expressive system; Shopify Polaris "snappy" principle */ + +--ease-enter: cubic-bezier(0.00, 0.00, 0.38, 0.9); /* entrance: decelerate to rest */ +--ease-exit: cubic-bezier(0.20, 0.00, 1.00, 0.9); /* exit: accelerate away */ +--ease-standard: cubic-bezier(0.20, 0.00, 0.38, 0.9); /* reposition within viewport */ +--ease-linear: linear; /* spinners, progress, video only */ +``` + +--- + +### Spring Preset Generation + +Springs are for gesture-continuation and physics-based interactions only. They maintain velocity continuity: if a user releases a drag at speed, a spring correctly inherits that velocity and continues from it. A `cubic-bezier` animation cannot do this — it always starts from zero velocity. + +#### When to Use Springs vs. Cubic-Bezier + +| Trigger | Use | Rationale | +|---------|-----|-----------| +| User gesture release (swipe, drag) | Spring | Velocity continuity is required | +| Button click / toggle / keyboard | Cubic-bezier | Zero initial velocity; spring adds no value | +| Multi-property animation via gesture | Spring | Each property settles naturally at different rates | +| Modal open (no gesture velocity) | Cubic-bezier | Simpler; predictable duration | +| Pull-to-refresh, card flick | Spring | Physical continuation of gesture | +| Page-load reveal | Cubic-bezier | No user velocity to continue | + +#### Spring Parameter Formula + +```python +import math + +def compute_spring_presets(pleasure_level: str, arousal_level: str) -> dict: + """ + Generate spring animation presets from PAD targets. + + Returns parameters for both: + - Framer Motion / react-spring (stiffness, damping, mass) + - Apple SwiftUI perceptual model (duration, bounce) + + Research basis: + - Apple WWDC 2023: bounce=0.0 (critically damped) for standard UI + - Apple: bounce=0.1-0.2 only for gesture-driven physical continuation + - Framer Motion snappy: stiffness 300, damping 25 (designer docs) + + Critical damping coefficient: ζ = damping / (2 × sqrt(stiffness × mass)) + ζ = 1.0 → no overshoot (critically damped) + ζ < 1.0 → underdamped (bouncy) + ζ > 1.0 → overdamped (sluggish) + """ + + # Apple perceptual bounce by pleasure level + # bounce=0 = critically damped (pleasure-neutral, precise) + # bounce>0 = underdamped (warm, playful) + BOUNCE_BY_PLEASURE = { + 'low': 0.00, # no overshoot — clinical, precise + 'medium': 0.05, # barely perceptible tail — hint of warmth + 'high': 0.15, # gentle follow-through — approachable + } + + # Duration modifier by arousal (perceptual duration, not hard TTL) + DURATION_BY_AROUSAL = { + 'low': 350, # deliberate, calm + 'medium': 280, # balanced + 'high': 200, # snappy + } + + bounce = BOUNCE_BY_PLEASURE[pleasure_level] + perceptual_duration = DURATION_BY_AROUSAL[arousal_level] + + # Convert to Framer Motion physics parameters + # stiffness = (2π / period)² × mass, simplified for UI: + # period ≈ perceptual_duration / 1000 (in seconds) + # stiffness ≈ (2π / (period * 0.9))² * mass + mass = 1.0 + period = perceptual_duration / 1000.0 * 0.9 # 0.9 factor: spring settles faster than full period + stiffness = round((2 * math.pi / period) ** 2 * mass) + + # Damping from bounce level: + # ζ = 1 - bounce (roughly; Apple's model) + # damping = 2 × ζ × sqrt(stiffness × mass) + zeta = max(0.5, 1.0 - bounce) # floor at 0.5 — never oscillate more than half-cycle in UI + damping = round(2 * zeta * math.sqrt(stiffness * mass)) + + return { + # Standard spring: no overshoot — for non-gesture UI elements + 'ui': { + 'stiffness': stiffness, + 'damping': damping, + 'mass': mass, + 'apple_duration': perceptual_duration, + 'apple_bounce': bounce, + }, + # Gesture spring: slight follow-through — for drag-release + 'gesture': { + 'stiffness': round(stiffness * 0.65), # softer = more travel after release + 'damping': round(damping * 0.48), # less damping = more oscillation + 'mass': mass, + 'apple_duration': round(perceptual_duration * 1.1), + 'apple_bounce': min(bounce + 0.15, 0.30), # add follow-through; cap at 0.30 + }, + # Snappy spring: for toggles and confirmations that need physicality + 'snappy': { + 'stiffness': round(stiffness * 1.5), # stiffer = faster + 'damping': round(damping * 1.1), # slightly overdamped = no bounce + 'mass': mass, + 'apple_duration': round(perceptual_duration * 0.7), + 'apple_bounce': 0.00, + }, + } + +# Validate critical damping ratio for each preset: +def validate_damping_ratio(stiffness: float, damping: float, mass: float) -> float: + """Returns ζ — should be >= 1.0 for standard UI, >= 0.7 for gesture springs.""" + return damping / (2 * math.sqrt(stiffness * mass)) +``` + +--- + +### Stagger Algorithm + +#### Cognitive Basis for Stagger + +Stagger forces sequential visual scanning by exploiting rod photoreceptor motion sensitivity: each newly appearing element captures attention briefly before the next appears. This creates a directed reading path that would not exist if all elements appeared simultaneously. + +**Critical constraint:** The stagger must complete within the human visual perception cycle (230 ms) multiplied by a manageable count. If the total stagger duration exceeds 400–500 ms, the sequence reads as slow loading rather than intentional choreography. + +```python +def compute_stagger_parameters( + item_count: int, + arousal_level: str, + total_budget_ms: int = 400 +) -> dict: + """ + Compute stagger delay per item and validate sequence budget. + + item_count: number of items to stagger + arousal_level: 'low' | 'medium' | 'high' + total_budget_ms: max total sequence time (default 400ms) + + Research basis: + - 230ms: Model Human Processor visual perception cycle (Card/Moran/Newell) + - < 50ms per step: stagger collapses — reads as simultaneous + - > 100ms per step: reads as loading, not choreography + - Total cap: 400ms — beyond this, sequence reads as slow + + From perception cycle: if steps > 230ms apart, each reads as fully independent event. + Sweet spot for readable sequencing: 60–100ms per step. + """ + + # Base delay range by arousal + DELAY_RANGE = { + 'low': (80, 100), # deliberate stagger — reading is part of the experience + 'medium': (60, 80), # balanced + 'high': (40, 60), # compressed — snappy reveal + } + + min_delay, max_delay = DELAY_RANGE[arousal_level] + + # Budget-constrained delay: scale down if n × delay > budget + # Use item_count - 1 because first item has delay 0 + effective_count = max(1, item_count - 1) + budget_per_step = total_budget_ms / effective_count if effective_count > 0 else max_delay + + # Select delay: use max_delay if budget allows, else constrain + delay = min(max_delay, budget_per_step) + # Enforce minimum (below 40ms stagger collapses perceptually) + delay = max(40, delay) + # Round to nearest 10ms for clean CSS values + delay = round(delay / 10) * 10 + + total_duration = delay * effective_count + + return { + 'delay_per_item': delay, + 'total_sequence_ms': total_duration, + 'within_budget': total_duration <= total_budget_ms, + # CSS: animation-delay = index × delay_per_item + 'css_pattern': f'animation-delay: calc(var(--stagger-index) * {delay}ms)', + } + +# Practical output table (medium arousal, 400ms budget): +# 2 items: delay = 80ms, total = 80ms ✓ +# 3 items: delay = 80ms, total = 160ms ✓ +# 4 items: delay = 80ms, total = 240ms ✓ +# 5 items: delay = 80ms, total = 320ms ✓ +# 6 items: delay = 80ms, total = 400ms ✓ (at budget) +# 7 items: delay = 70ms, total = 420ms (compressed to fit) +# 10 items: delay = 40ms, total = 360ms (compressed to 40ms floor) +``` + +#### Stagger CSS Custom Properties Pattern + +```css +/* Stagger pattern: set --stagger-index on each child via JS or nth-child */ +.stagger-parent > * { + --stagger-index: 0; /* override per child */ + animation-delay: calc(var(--stagger-index) * var(--motion-stagger)); +} + +/* Duration tokens (medium arousal, 400ms budget) */ +--motion-stagger-sm: 60ms; /* 5+ items */ +--motion-stagger-md: 80ms; /* 3–4 items */ +--motion-stagger-lg: 100ms; /* 2 items */ +``` + +--- + +## Reveal Animation System (Page Load) + +### Entrance Type Selection + +Three canonical entrance patterns, ordered by cognitive overhead: + +| Type | CSS Properties | Cognitive Load | Best For | +|------|---------------|---------------|---------| +| **Fade + translate** (recommended) | `opacity`, `transform: translateY()` | Lowest | All page-load stagger, list reveals, section entrances | +| **Fade only** | `opacity` | Lowest | Content-heavy pages; when spatial context is unambiguous | +| **Scale + fade** | `opacity`, `transform: scale()` | Highest | Modal/dialog only; single focal element; never for lists | + +**Research basis:** Fade + small translate achieves the highest practitioner consensus for page-load context because: +1. The translate provides a subtle directional cue (spatial context) without imposing navigational meaning +2. 8–12px is below the threshold where the brain assigns "this came from outside the viewport" +3. The fade prevents a "pop" artifact that would read as a rendering glitch + +**Scale-in at page load:** Never use for lists or sequential stagger. Scaling multiple elements simultaneously reads as chaotic. Reserved for single focal elements (hero, dialog). + +### Entrance Motion Formulas + +```python +def compute_entrance_motion( + context: str, # 'page_load' | 'scroll_reveal' | 'modal' | 'navigation_forward' | 'navigation_back' + arousal_level: str, # 'low' | 'medium' | 'high' +) -> dict: + """ + Compute entrance animation properties by context and brand arousal. + + Direction rules (NNGroup spatial cognition research): + - page_load: top-to-bottom (elements settle downward — matches F-pattern reading) + - scroll_reveal: upward translate (elements rise from below — matches scroll direction) + - modal: scale from center (no spatial origin in reading flow) + - navigation_forward: left-to-right entry (spatial convention: forward = right) + - navigation_back: right-to-left entry (spatial convention: back = left) + + Research: Arrows on the right preferred for "forward" (Casasanto & Bottini 2022). + NNGroup: direction encodes spatial contract — breaking it causes user error. + """ + + # Translate distance by arousal (larger = more energetic, more attention) + # Practitioner consensus: 8–12px for page-load; 12–16px for scroll-reveal + # Cap: beyond 20px reads as theatrical / outside-viewport origin + TRANSLATE_BY_AROUSAL = { + 'low': 12, # deliberate settle + 'medium': 8, # standard + 'high': 6, # minimal — snappy appearance + } + + translate_px = TRANSLATE_BY_AROUSAL[arousal_level] + + CONTEXTS = { + 'page_load': { + # Elements settle downward from above: translateY(-px → 0) + # This is OPPOSITE of scroll reveal (which rises upward) + # Rationale: page load starts at top, reading order is downward + 'from': f'opacity: 0; transform: translateY(-{translate_px}px)', + 'to': 'opacity: 1; transform: translateY(0)', + 'easing': '--ease-enter', + }, + 'scroll_reveal': { + # Elements rise upward into viewport: translateY(px → 0) + # Matches scroll direction — content rises as user scrolls down + 'from': f'opacity: 0; transform: translateY({translate_px + 4}px)', + 'to': 'opacity: 1; transform: translateY(0)', + 'easing': '--ease-enter', + }, + 'modal': { + # Scale from center — no directional origin + # 0.96 scale: subtle enough to not feel theatrical + 'from': 'opacity: 0; transform: scale(0.96)', + 'to': 'opacity: 1; transform: scale(1)', + 'easing': '--ease-enter', + }, + 'navigation_forward': { + # Enter from left (left → right = forward in LTR reading cultures) + 'from': f'opacity: 0; transform: translateX(-{translate_px + 8}px)', + 'to': 'opacity: 1; transform: translateX(0)', + 'easing': '--ease-enter', + }, + 'navigation_back': { + # Enter from right (right → left = backward) + 'from': f'opacity: 0; transform: translateX({translate_px + 8}px)', + 'to': 'opacity: 1; transform: translateX(0)', + 'easing': '--ease-enter', + }, + } + + return CONTEXTS[context] +``` + +### Page Load Reveal: Sequencing Rules + +```python +def plan_page_reveal_sequence(sections: list[dict]) -> list[dict]: + """ + Plan the stagger sequence for a page load reveal. + + Stagger order must encode information hierarchy (NNGroup progressive disclosure): + "If stagger order does not match information priority, it actively misdirects attention." + + Rules: + 1. First element has delay=0 (no gap between paint and motion start) + 2. Hero/heading animates before body content + 3. Navigation/chrome animates simultaneously with or before hero + 4. Secondary content (sidebar, related links) animates last + 5. Decorative elements (dividers, background shapes) use fastest duration, last delay + + sections: list of dicts with 'role': 'nav' | 'hero' | 'body' | 'secondary' | 'decorative' + Returns same list with added 'delay_ms' and 'duration_token' fields. + """ + + PRIORITY_ORDER = ['nav', 'hero', 'body', 'secondary', 'decorative'] + STAGGER_MS = 80 # medium arousal default + + # Sort by priority, preserving relative order within same priority + sorted_sections = sorted(sections, key=lambda s: PRIORITY_ORDER.index(s.get('role', 'body'))) + + delay = 0 + for section in sorted_sections: + section['delay_ms'] = delay + section['duration_token'] = '--duration-moderate' if section.get('role') == 'hero' else '--duration-subtle' + delay += STAGGER_MS + + # Validate total sequence is within budget + total_ms = sorted_sections[-1]['delay_ms'] + 240 # last delay + moderate duration + assert total_ms <= 800, f"Reveal sequence too long: {total_ms}ms. Compress stagger." + + return sorted_sections +``` + +### Reveal CSS Keyframe Template + +```css +/* Page load reveal — hero entrance */ +@keyframes reveal-from-top { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Scroll-driven reveal — content rises into view */ +@keyframes reveal-from-bottom { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Modal entrance — scale from center */ +@keyframes reveal-modal { + from { + opacity: 0; + transform: scale(0.96); + } + to { + opacity: 1; + transform: scale(1); + } +} + +/* Shared reveal class */ +.reveal { + animation-fill-mode: both; /* hold from-state before start, to-state after end */ + animation-timing-function: var(--ease-enter); + animation-duration: var(--duration-subtle); +} + +.reveal-hero { + animation-name: reveal-from-top; + animation-duration: var(--duration-moderate); +} + +.reveal-content { + animation-name: reveal-from-bottom; + animation-duration: var(--duration-subtle); +} + +/* Stagger via custom property */ +.stagger-item { + animation-delay: calc(var(--stagger-index, 0) * var(--motion-stagger-md, 80ms)); +} +``` + +--- + +## Scroll-Driven Animation System + +### Scroll Reveal: When to Animate + +**Operational rule:** Animate scroll-revealed content only when it is below the fold on first paint. Never animate content that is already visible on page load — this creates the appearance of elements "jumping" after the user has already read them. + +**Performance constraint:** Use the CSS `@scroll-timeline` API or `IntersectionObserver` — never `scroll` event listeners, which block the main thread and cause jank. + +```python +def compute_scroll_reveal_threshold( + element_height_px: int, + viewport_height_px: int = 900, +) -> dict: + """ + Compute IntersectionObserver threshold for scroll-reveal timing. + + Optimal reveal point: when 20% of the element is visible. + This gives the animation time to complete before the element + is fully in view — content is readable at animation end. + + If element is taller than viewport (e.g., full-screen sections), + reveal on first pixel entering viewport (threshold = 0). + """ + + if element_height_px >= viewport_height_px: + # Tall element: reveal on first pixel + threshold = 0.0 + rootMargin = '0px 0px -50px 0px' # 50px early trigger + else: + # Standard element: reveal when 20% is in view + threshold = 0.1 # 10% visible = trigger (conservative for fast scrollers) + rootMargin = '0px 0px -80px 0px' # 80px above bottom = reveal early + + return { + 'threshold': threshold, + 'rootMargin': rootMargin, + 'note': 'Apply reveal-content class on intersection; remove on disconnect for repeat reveals', + } +``` + +### Scroll Direction Awareness + +Elements that appear on scroll should animate in the direction consistent with scroll motion: + +| Scroll Direction | Element Enters From | Transform Start | +|-----------------|--------------------|-----------------| +| Scrolling down | Below viewport | `translateY(12px)` → `translateY(0)` | +| Scrolling up | Above viewport | `translateY(-12px)` → `translateY(0)` | +| Horizontal scroll | Right side | `translateX(12px)` → `translateX(0)` | + +**Research basis:** NNGroup directionality research — direction encodes a spatial contract. An element "rising to meet you" as you scroll down is spatially coherent. An element sliding down-to-up would imply the user is scrolling upward. + +### Parallax and Scroll-Linked Motion + +Parallax (where elements move at different rates from the scroll) is a high-arousal, high-attention-demand technique. Apply the following constraints: + +```python +def compute_parallax_parameters( + element_role: str, # 'hero_bg' | 'decorative' | 'content' + brand_arousal: str, # 'low' | 'medium' | 'high' +) -> dict: + """ + Compute parallax scroll rate multiplier. + + Parallax triggers vestibular sensitivity in ~10M Americans (vestibular.org). + WCAG 2.1 SC 2.3.3 requires prefers-reduced-motion compliance. + + Safe parallax range: 0.05–0.15× scroll rate. + Beyond 0.20×: motion sickness risk increases substantially. + Never apply to: content text, interactive elements. + Only apply to: decorative backgrounds, illustration elements. + """ + + if element_role == 'content': + # NEVER parallax content — it disrupts reading + return {'multiplier': 0.0, 'warning': 'Content elements must not use parallax'} + + MAX_MULTIPLIER = { + 'low': 0.05, # barely perceptible — calm brand + 'medium': 0.10, # gentle depth — balanced brand + 'high': 0.15, # noticeable depth — energetic brand (ceiling) + } + + multiplier = MAX_MULTIPLIER[brand_arousal] + + return { + 'multiplier': multiplier, + 'css': f'transform: translateY(calc(var(--scroll-y) * {multiplier}px))', + 'warning': 'Requires prefers-reduced-motion: reduce override → multiplier: 0', + } +``` + +--- + +## Diagram Animation System + +This section specifies how SVG diagram components are animated in this codebase. It consumes the motion tokens defined above; read it in tandem with `DESIGN_SYSTEM.md §Visual Elements`. + +### Figure Coherence Principles + +Three principles govern all animated figures. Every figure must satisfy all three before any animation is added. + +**1. Static Completeness (design target)** +The final settled state (phase=1) is the primary design artifact. Animation is progressive disclosure — it reveals this state; it does not create meaning. Design the phase=1 layout first. A reader who never scrolls must still understand the concept. If the figure requires motion to be comprehensible, the static design has failed. + +**2. Act-State Integrity (every phase is a valid composition)** +Every scroll position must yield a visually complete, balanced composition. Elements that have not arrived yet must have placeholder mass — ghost geometry or neutral fill — that preserves spatial balance. A diagram that looks broken or unbalanced at any act boundary violates this principle. Verify compositional balance at each act boundary before adding transitions. + +**3. Semantic Stagger (appearance order = causal order)** +The sequence in which elements appear must match the information hierarchy and causal order of the concept being illustrated. This is a hard rule, not a preference: +- Causal upstream elements must appear before downstream elements. +- Labels must appear after the elements they describe. +- Parallel, equal-weight elements must appear simultaneously — stagger imposes false hierarchy. + +### ScrollDrivenFigure: the wrapper contract + +`ScrollDrivenFigure` is the required wrapper for any animated diagram. It manages three-tier fallback based on browser capability and user preference: + +| Tier | Mechanism | Condition | +|------|-----------|-----------| +| Primary | CSS `animation-timeline: view()` via `@supports` | Modern browsers | +| Mirror | JS `scroll` listener → React context (`useAnimationPhase()`) | When CSS tier active — provides child component read access to phase | +| Fallback | IntersectionObserver one-shot reveal (`threshold: 0.15`) | CSS unsupported | +| Floor | `phase = 1`, `revealed = true` immediately | `prefers-reduced-motion: reduce` | + +**`phaseEnd` prop** (default `0.5`): controls when phase completes. With `phaseEnd={0.5}`, phase reaches 1.0 when the figure's bottom edge is at 50% viewport height. Complex narrative diagrams (IntroHookDiagram) use the default. Simple diagrams can use `phaseEnd={0.3}` for faster full-phase completion. + +**`useAnimationPhase()`**: the hook child components call to read phase (0–1). Cannot be called outside a `` tree. + +### The Act System + +The Act System maps scroll phase thresholds to named animation events. Define an `ACTS` array: + +```ts +const ACTS = [ + { id: 'arc', threshold: 0 }, // fires immediately on figure entry + { id: 'composing', threshold: 0.5 }, // fires at half-scroll + { id: 'dispatch', threshold: 0.80 }, +] as const; +``` + +Pass to `useActs(ACTS, phase)` which returns `{ wasReached, isCurrentAct }`: + +- **`wasReached(id)`** → `boolean`: fires the moment threshold is crossed. Use for **one-shot CSS class reveals** (adding `.entered` to nodes). Reverses when the user scrolls up — the phase is always reactive. +- **`isCurrentAct(id)`** → `boolean`: true for the topmost reached act. Use for **idle-state indicators** (`idle-ready-breathe`). +- **Monotonicity:** acts are not latched by default. When one-way semantics are required (e.g., worker nodes that should not un-bloom on scroll-back), add a `useRef` guard at the call site. + +**Threshold 0 acts** fire as soon as the figure enters the viewport. Use them for structural elements that should be present from the start rather than arriving mid-scroll. + +### Figure Entrance: Canonical Parameters + +`OperatorNode` and `AgentNode` at equivalent size roles use the **same entrance keyframe** with parameters scaled by size role: + +| Node type | Size | Role | Duration | Translate | Easing | +|-----------|------|------|----------|-----------|--------| +| `OperatorNode` | S=40 | Primary (human) | 300ms | `translateY(12px → 0)` | `var(--ease-enter)` | +| `AgentNode` | S=40 | Primary (orchestrator) | 300ms | `translateY(12px → 0)` | `var(--ease-enter)` | +| `AgentNode` | S=32 | Secondary (worker) | 250ms | `translateY(8px → 0)` | `var(--ease-enter)` | +| `OperatorNode` | S=32 | Secondary | 250ms | `translateY(8px → 0)` | `var(--ease-enter)` | + +**Semantic symmetry rule:** `OperatorNode` and `AgentNode` at equivalent size roles animate identically. Color (neutral vs. violet) carries the semantic distinction; motion does not layer on additional differentiation. + +Canonical keyframe (reuse in every diagram module CSS — do not redeclare with different values): + +```css +@keyframes actEnter { + from { opacity: 0; transform: translateY(12px); } + to { opacity: 1; transform: translateY(0); } +} +``` + +Base/entered pattern: + +```css +.actorNode { opacity: 0; transform: translateY(12px); } /* hidden until act */ +.actorNode.entered { animation: actEnter 300ms var(--ease-enter) both; } +``` + +### Idle State: idle-ready-breathe + +The global `idle-ready-breathe` class (defined in `custom.css`) signals that an agent node is active and waiting for input. Parameters: `4000ms ease-in-out infinite; scale(1 → 1.02)`. + +Apply via `isCurrentAct()`: + +```tsx +className={clsx(styles.actorNode, wasReached('orchestrator') && styles.entered, + isCurrentAct('orchestrator') && 'idle-ready-breathe')} +``` + +**Rule:** Apply to `AgentNode` only, never `OperatorNode`. Apply only to the node whose semantic role is "waiting for the next input" — typically the orchestrator during the phase between receiving a prompt and dispatching workers. Remove (by transitioning away from the current act) when the node dispatches or completes. + +### Ghost Placeholder Pattern + +Ghost placeholders provide visual mass for nodes that are anticipated but not yet revealed, preventing the diagram from feeling unbalanced before dispatch. + +Geometry: match the head squircle of the target `AgentNode` size exactly. For S=32: `x+2.4, y+2.4, width=27.2, height=27.2, rx=6.8`. + +Three CSS states — apply via mount guard + dispatch state: + +```css +.ghostWorker { opacity: 0; } +.ghostWorkerShown { opacity: 0.15; transition: opacity 300ms var(--ease-enter); } +.ghostWorkerHidden { opacity: 0; transition: opacity 200ms var(--ease-exit); } +``` + +Fade-out on dispatch (200ms) must complete before worker entrance (first worker delay: 200ms). Ghost workers are hidden in `prefers-reduced-motion: reduce` (`opacity: 0 !important`). + +### SVG Path Animation: Two Modes + +**Mode A — Act-gated CSS keyframe (guide arcs, one-shot):** + +``` +mount: dasharray = dashoffset = getTotalLength() +act reached → add .arcDraw class → CSS plays drawPath once +``` + +Use when the path should draw in one smooth motion at a specific scroll threshold, then remain drawn. Duration: 500ms `var(--ease-enter)`. + +**Mode B — JS scroll-driven continuous (fan arcs):** + +``` +useEffect([phase]) → dashoffset = length * (1 - t) +opacity toggled by phase threshold +``` + +Use when draw speed should track scroll velocity — the reader controls the pace. + +Fan arc stagger formula (per-arc phase offset): + +```ts +const fanT = (i: number) => { + const start = DISPATCH_START + i * PHASE_STAGGER; // e.g. 0.80 + i * 0.04 + return clamp((phase - start) / (DISPATCH_END - start), 0, 1); +}; +``` + +`PHASE_STAGGER = 0.04` produces ~80ms of scroll-equivalent stagger between arcs at typical scroll velocity. This is the phase-space equivalent of the ms-stagger in §Stagger Algorithm. + +### Artifact Travel: PromptIcon vs TravelingPromptCard + +Two distinct approaches; choice depends on trigger type: + +| Component | Animation mechanism | Trigger | Easing | +|-----------|---------------------|---------|--------| +| `PromptIcon` | Parent `transform: translate(pt.x, pt.y)` via `getPointAtLength` | Scroll phase (continuous) | Follows path geometry | +| `TravelingPromptCard` | SMIL `` with `begin="indefinite"` | Imperative: `motionRef.current.beginElement()` | `keySplines="0.20 0 0.38 0.9"` | + +**Use `PromptIcon` for scroll-driven diagrams.** The parent reads phase, queries the invisible `` path via `getPointAtLength(t * totalLength)`, and applies a `translate` transform. + +**Use `TravelingPromptCard` for trigger-based diagrams** (fixed-timeline or hover-activated). SMIL `animateMotion` does not work in ``-embedded SVG — only in inline React SVG. + +Opacity fade at arc end (prevents visual collision with destination node): + +```ts +const opacity = t < 0.7 ? 1 : 1 - (t - 0.7) / 0.3; +// Artifact is fully visible for first 70% of arc travel, fades over final 30% +``` + +Invisible `` path pattern: + +```tsx + + + {/* no stroke, no fill — pure geometry reference */} + +``` + +### Mount Guard (SSR Hydration Safety) + +Any element whose initial CSS class depends on a phase value (which cannot be computed server-side) needs a mount guard to prevent hydration mismatch: + +```tsx +const [mounted, setMounted] = useState(false); +useEffect(() => { setMounted(true); }, []); + +// In JSX: +className={clsx( + styles.ghostWorker, + mounted && !dispatched && styles.ghostWorkerShown, + dispatched && styles.ghostWorkerHidden, +)} +``` + +Without this, the server renders `mounted=false` (hidden), hydration matches, then the immediate `mounted=true` flip on client produces a one-frame flash. Apply to: ghost workers, idea lightbulb, any pre-phase element with a conditional show class. + +### CSS Module Scoping and Static Fallback Card + +Each diagram gets its own `.module.css`. Keyframes are module-scoped — do not import or reuse keyframe names from other modules (they'll be renamed by the bundler). + +**Static fallback card** (required for any diagram with traveling or scroll-driven animated elements): + +```css +.staticCard { display: none; } + +@media (prefers-reduced-motion: reduce) { + .staticCard { display: block; } /* static prompt shape at semantic midpoint of arc */ + .promptIcon { display: none; } /* hides the animated version */ +} +``` + +Placement rule: position the static card at the **semantic midpoint** of the main arc (where the concept is clearest), not at start or end state. Use dimmed opacity (0.35) for guide arcs in reduced-motion to preserve their "guide" semantic without implying motion. + +--- + +## Stagger Order and Information Hierarchy + +**Critical principle from NNGroup progressive disclosure research:** Animation-driven stagger creates a directed reading path by exploiting motion-attention capture. This benefit inverts to harm if the stagger order contradicts information hierarchy. + +### Stagger Order Rules + +| Content Type | Stagger Order Rule | +|-------------|-------------------| +| List items (sequential, ordered) | Top-to-bottom, left-to-right — matches reading order | +| Grid items (parallel, equal priority) | Simultaneous fade — stagger imposes false hierarchy | +| Form fields | Top-to-bottom — mirrors completion sequence | +| Navigation items | Simultaneously or as a unit — they are parallel | +| Diagram nodes | Animate in data-flow order (source first, sinks last) | +| Hero → body → secondary | Hero first; stagger each section as a unit | + +**Parallel content rule:** For content that is spatially parallel and equal-priority (e.g., a 3-column feature grid where all columns are equivalent), stagger imposes a false hierarchy. Use simultaneous fade. Reserve stagger for genuinely sequential content (steps, timelines, bullet points). + +```python +def select_stagger_strategy( + content_structure: str, # 'sequential' | 'parallel' | 'hierarchical' + item_count: int, +) -> str: + """ + Returns: 'stagger' | 'simultaneous' | 'grouped' + """ + if content_structure == 'parallel' and item_count <= 4: + # Equal-weight items: no stagger (would impose false hierarchy) + return 'simultaneous' + elif content_structure == 'sequential': + # Steps, bullet points, timelines: stagger in reading order + return 'stagger' + elif content_structure == 'hierarchical': + # Groups: animate each group as a unit, stagger between groups + return 'grouped' + elif item_count > 8: + # Too many individual staggers: group into buckets of 3–4 + return 'grouped' + else: + return 'stagger' +``` + +--- + +## Shape Vocabulary and Animation Congruence + +Illustration shape vocabulary (Smooth Circuit vs. Terminal Geometry from `ILLUSTRATION_GUIDE.md`) must be congruent with animation character. The same psychophysical principle that maps curved shapes to warmth maps curved easing to warmth. + +### Shape × Easing Congruence Table + +| Shape Family | Easing Style | Spring Bounce | Duration Bias | +|-------------|-------------|---------------|--------------| +| **Smooth Circuit** (circles, squircles, Bezier connectors) | Expressive entrance, gradual ease-out | 0.05–0.15 | Standard to deliberate | +| **Terminal Geometry** (diamonds, sharp rects, angular paths) | Productive, fast snap | 0.00 | Instant to subtle | +| **Positive valence** (success, AI, system, knowledge) | Ease-out with gentle tail | 0.05–0.10 | Standard | +| **High-arousal** (error, warning, code structure) | Near-linear or ease-in | 0.00 | Instant to fast | + +**Congruence rule:** An error dialog that bounces into view (spring bounce > 0) is semantically incoherent — it applies warm, playful motion to a negative-valence semantic state. Apply Terminal Geometry animation character to all error and warning elements: snap in, no overshoot, fast duration. + +```python +def get_animation_for_semantic(semantic_role: str) -> dict: + """ + Returns animation parameters congruent with semantic hue role. + + Maps semantic role → PAD axis emphasis → animation character. + Error and Warning use Terminal Geometry animation character regardless of brand default. + """ + + SEMANTIC_ANIMATION = { + 'error': {'duration': '--duration-fast', 'easing': '--ease-standard', 'bounce': 0.00, 'translate': '4px'}, + 'warning': {'duration': '--duration-fast', 'easing': '--ease-standard', 'bounce': 0.00, 'translate': '4px'}, + 'success': {'duration': '--duration-subtle', 'easing': '--ease-enter', 'bounce': 0.05, 'translate': '8px'}, + 'info': {'duration': '--duration-subtle', 'easing': '--ease-enter', 'bounce': 0.00, 'translate': '8px'}, + 'neutral': {'duration': '--duration-subtle', 'easing': '--ease-enter', 'bounce': 0.00, 'translate': '8px'}, + 'ai': {'duration': '--duration-moderate', 'easing': '--ease-enter', 'bounce': 0.05, 'translate': '8px'}, + 'system': {'duration': '--duration-subtle', 'easing': '--ease-standard', 'bounce': 0.00, 'translate': '6px'}, + } + + return SEMANTIC_ANIMATION.get(semantic_role, SEMANTIC_ANIMATION['neutral']) +``` + +--- + +## Brand-Specific Application: Agentic Coding + +This section applies the above system to the Agentic Coding design system specifically. + +### Brand PAD Profile + +| Axis | Level | Rationale | +|------|-------|-----------| +| Pleasure | Medium | Professional, clean, not cold. Smooth Circuit shapes as default. | +| Arousal | Low–Medium | Technical reference for focused work. Calm and efficient. | +| Dominance | Medium | Authoritative reference; not passive, not commanding. | + +**Motion style:** Productive. Technical reference documentation prioritizes task speed over emotional engagement. Expressive curves reserved for diagram reveals only. + +### Token Values for This Brand + +Execute these scripts to produce the final token set: + +```python +# Run to generate final token values for Agentic Coding brand +tokens = compute_duration_tokens(arousal_level='medium') +easing = compute_easing_tokens(motion_style='productive', pleasure_level='medium') +springs = compute_spring_presets(pleasure_level='medium', arousal_level='low') + +# Stagger for typical 4-item list +stagger_4 = compute_stagger_parameters(item_count=4, arousal_level='medium') +# → delay_per_item: 80ms, total: 240ms ✓ + +# Stagger for 8-item list +stagger_8 = compute_stagger_parameters(item_count=8, arousal_level='medium') +# → delay_per_item: 60ms (budget-compressed), total: 420ms → compress further to 50ms → 350ms ✓ +``` + +### Computed Token CSS for This Brand + +```css +/* ============================================================ + MOTION TOKENS — Agentic Coding Design System + Generated from: Motion Engineering Phase 2 + Brand: Productive style, medium arousal, medium pleasure + ============================================================ */ + +/* Duration */ +--duration-instant: 70ms; +--duration-fast: 110ms; +--duration-subtle: 150ms; +--duration-moderate: 240ms; +--duration-deliberate: 400ms; +--duration-ambient: 700ms; + +/* Exit variants (×0.75) */ +--duration-fast-exit: 80ms; +--duration-subtle-exit: 110ms; +--duration-moderate-exit: 180ms; +--duration-deliberate-exit: 300ms; + +/* Easing — productive style */ +--ease-enter: cubic-bezier(0.00, 0.00, 0.38, 0.9); +--ease-exit: cubic-bezier(0.20, 0.00, 1.00, 0.9); +--ease-standard: cubic-bezier(0.20, 0.00, 0.38, 0.9); +--ease-linear: linear; + +/* Stagger */ +--motion-stagger-sm: 60ms; /* 5–8 items */ +--motion-stagger-md: 80ms; /* 3–4 items */ +--motion-stagger-lg: 100ms; /* 2 items */ + +/* Reveal offsets */ +--motion-reveal-y-load: -8px; /* page load: elements settle downward into position */ +--motion-reveal-y-scroll: 12px; /* scroll reveal: elements rise upward into viewport */ +--motion-reveal-x-forward: -16px; /* navigation forward: slide right */ +--motion-reveal-x-back: 16px; /* navigation back: slide left */ +--motion-reveal-scale: 0.96; /* modal/dialog only */ +``` + +### Diagram Animation Rules + +> See [§ Diagram Animation System](#diagram-animation-system) for the complete specification. + +--- + +## Accessibility + +### prefers-reduced-motion + +**Reduced-motion renders the canonical representation.** The phase=1 settled state is the primary design artifact — animation is an enhancement layer for capable browsers, not the content itself. When `prefers-reduced-motion: reduce` is active, `ScrollDrivenFigure` sets `phase=1` immediately, rendering every figure in its fully-settled final state. This is not a degradation path; it is the baseline design. Animation is a progressive enhancement for browsers that support it and users who prefer it. + +WCAG 2.1 SC 2.3.3 (Level AAA) and SC 2.3.1 (Level A) cover motion sensitivity. Vestibular disorders affect approximately 10 million Americans. Large-scale motion can trigger nausea, headaches, and symptoms requiring bed rest. This is not aesthetic preference — ignoring it can cause physical harm. + +**Implementation requirement:** Every animation property must be neutralized by the following media query. This is not optional. + +```css +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-delay: 0ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + transition-delay: 0ms !important; + scroll-behavior: auto !important; + } + + /* Preserve opacity transitions for content that uses them for show/hide logic */ + .reveal, + .stagger-item { + opacity: 1 !important; + transform: none !important; + } +} +``` + +**Do NOT use `animation-duration: 0` (zero):** Some browsers and screen readers interpret zero-duration animations differently from no animation. Use `0.01ms` — effectively instantaneous but technically animated. + +### State Change Without Motion + +When `prefers-reduced-motion: reduce` is active: +- Content appears instantly (no fade, no translate) +- Stagger is eliminated — all elements appear simultaneously +- Scroll-driven reveals trigger immediately on entering viewport (no animation) +- Progress indicators use color change only (no spinning, no sweeping) +- Diagrams render at final state — no draw-on animation + +### Minimum Animation Duration for Screen Readers + +Some screen reader users rely on transition events for navigation cues. Never set `animation-duration` below 40ms (even before reduced-motion override) — below 40ms, the browser may not fire `animationend` events reliably. + +--- + +## Validation Checklist + +For every generated animation sequence, verify: + +**Timing:** +- [ ] All user-triggered animations begin within 100 ms of trigger (Miller 1968) +- [ ] No user-triggered animation exceeds 500 ms duration +- [ ] Exit animations are ≤ 75% of corresponding enter duration +- [ ] No `animation-delay` on first element in any sequence + +**Easing:** +- [ ] No CSS `ease` keyword used — only custom `cubic-bezier()` or named tokens +- [ ] `linear` easing used only for spinners, progress bars, and video +- [ ] Entrance uses ease-out (decelerating); exit uses ease-in (accelerating) +- [ ] Repositioning uses ease-in-out; not ease-out (ease-out implies arrival, not transit) + +**Stagger:** +- [ ] Total stagger sequence ≤ 400 ms (compress per-item delay if needed) +- [ ] Stagger per step ≥ 40 ms (below this collapses to simultaneous) +- [ ] Stagger order matches information hierarchy +- [ ] Parallel equal-priority items use simultaneous fade, not stagger + +**Reveal (page load):** +- [ ] Page-load elements use `translateY(-8px → 0)` (settle downward, not rise) +- [ ] Scroll-reveal elements use `translateY(12px → 0)` (rise upward with scroll) +- [ ] Scale-in (`scale(0.96)`) used only for modals/dialogs — never lists +- [ ] Navigation forward: enter from left; navigation back: enter from right +- [ ] `animation-fill-mode: both` set on all reveal animations + +**Springs:** +- [ ] Springs used only for gesture-continuation (drag release, pull-to-refresh) +- [ ] `bounce: 0.0` (critically damped) for all non-gesture UI elements +- [ ] No spring bounce on error, warning, or danger elements + +**Shape × Easing congruence:** +- [ ] Error and warning elements use fast, snap-in animation (no bounce, no deliberate) +- [ ] Success and positive-valence elements may use subtle bounce (≤ 0.10) +- [ ] Diagram connectors draw after nodes (data-flow order maintained) + +**Accessibility:** +- [ ] `@media (prefers-reduced-motion: reduce)` block present in CSS +- [ ] All animations use `0.01ms !important` in reduced-motion block (not 0) +- [ ] Reveal classes reset to final state (`opacity: 1`, `transform: none`) in reduced-motion +- [ ] No `animation-duration` below 40ms anywhere (browser event reliability) +- [ ] Parallax multiplier set to `0` in reduced-motion override + +**Performance:** +- [ ] All animated properties are `opacity` and `transform` only — no `width`, `height`, `left`, `top`, `margin`, `padding` +- [ ] No `scroll` event listeners for animation — use `IntersectionObserver` or CSS `@scroll-timeline` +- [ ] `will-change: transform, opacity` applied only to actively animating elements (remove after animation ends) +- [ ] `animation-fill-mode: both` preferred over JavaScript state management for reveal + +**Diagram figures:** +- [ ] `OperatorNode` and `AgentNode` at equivalent size roles use identical `actEnter` animation +- [ ] `var(--ease-enter)` used, not `ease-out` keyword +- [ ] S=40 primary entrance: 300ms, `translateY(12px)`. S=32 secondary: 250ms, `translateY(8px)` +- [ ] `idle-ready-breathe` applied only to `AgentNode` during its "waiting for input" act +- [ ] Ghost placeholders geometrically match their target node's head squircle bounds +- [ ] Guide arcs (Mode A): CSS keyframe, 500ms, act-gated. Fan arcs (Mode B): JS scroll-driven +- [ ] `PromptIcon` + `getPointAtLength` for scroll-driven travel; `TravelingPromptCard` for triggers +- [ ] Mount guard applied to all elements with phase-conditional initial class +- [ ] Static fallback card present; placed at semantic arc midpoint, not start/end state +- [ ] Guide arcs in reduced-motion: `opacity: 0.35; stroke-dashoffset: 0 !important` + +--- + +## References + +### Perceptual Timing +- **Miller, G. A.** — "The magical number seven, plus or minus two" (*Psychological Review*, 1956). Foundation for the 1,000 ms flow threshold. +- **Miller, R. B.** — "Response time in man-computer conversational transactions" (*AFIPS Fall Joint Computer Conference*, 1968). Three response time thresholds: 100 ms, 1,000 ms, 10,000 ms. +- **Card, S. K., Moran, T. P., & Newell, A.** — *The Psychology of Human-Computer Interaction* (Lawrence Erlbaum, 1983). Model Human Processor: 230 ms visual perception cycle. +- **Card, S. K., Robertson, G. G., & Mackinlay, J. D.** — "The information visualizer" (*CHI '91*, 1991). Applied Miller thresholds to workstation interface timing. +- **Nielsen, J.** — "Response Times: The 3 Important Limits" (NNGroup, 1993; based on Miller 1968). 100 / 1,000 / 10,000 ms practitioner synthesis. +- **Nielsen Norman Group** — "The Ideal Duration and Easing for UI Animations" (2015, updated 2023). Practitioner synthesis of animation duration research. + +### Duration & Easing Systems +- **IBM Carbon Design System** — "Motion" documentation. Six-tier duration scale (fast-01 70 ms → slow-02 700 ms); productive vs. expressive easing curves. Published under Apache 2.0. +- **Apple Inc.** — WWDC 2023: "Wind down with SwiftUI animations." Spring animation perceptual model: `duration` + `bounce` parameters; bounce: 0.0 = critically damped; recommended 0.0–0.20 for UI. +- **Shopify Polaris** — "Motion" design system documentation. "Snappy" principle: fast start, slow end (ease-out default). +- **Material Design 3 (Google)** — "Motion" specification. Easing tokens: `cubic-bezier(0.05, 0.7, 0.1, 1.0)` standard ease-out. +- **MUI (Material UI)** — `theme.transitions` documentation. 225 ms entering, 195 ms leaving defaults. + +### Cognitive Effects of Animation +- **Pratt, J., et al.** — "Visual sudden-onset in peripheral vision triggers orienting" (*Psychological Science*, 2010, 21(12), 1724–1730). Motion anywhere in visual field triggers automatic attention reorientation. +- **Nielsen Norman Group** — "Animation for Attention and Comprehension" (2020). Animation directing attention to critical state changes; change blindness prevention. +- **Nielsen Norman Group** — "Skeleton Screens 101" (Mejtoft, Långström & Söderström 2018, referenced). Skeleton screens create "illusion of progress," reducing perceived loading time. +- **Nebraska-Lincoln study** (cited by NNGroup) — Users with animated progress bars tolerate **3× longer** wait vs. static/no indicator. Identical objective wait time. +- **Thomas, F., & Johnston, O.** — *The Illusion of Life: Disney Animation* (Hyperion, 1981). 12 principles of animation; timing, ease-in/ease-out, staging, follow-through operationalized by Carbon, Apple. + +### Perceived Performance +- **Nielsen Norman Group** — "Response Times: The 3 Important Limits." Slideshow case study: 1% vs. 20% eye-tracking attention differential. +- **WCAG 2.1 Success Criterion 2.3.3** — "Animation from Interactions" (Level AAA). Required: provide mechanism to disable motion. +- **vestibular.org** — Vestibular Disorders Association. ~10 million Americans affected by vestibular disorders; screen motion can trigger symptoms. + +### Spatial Cognition and Direction +- **Casasanto, D., & Bottini, R.** — "Mirror Reading Can Reverse the Flow of Time" and spatial metaphor research (*Frontiers in Psychology*, 2022). GOOD IS RIGHT; mental number line; cultural variation. +- **Nielsen Norman Group** — "Animation in UX: The Science of Motion and Spatial Cognition" (2022). Directionality encodes navigational contracts; zoom in = deeper, zoom out = broader. +- **Pratt et al. (2010)** — Peripheral motion capture (see above). Foundation for directional attention signaling. + +### Stagger and Progressive Disclosure +- **Card, S. K., Moran, T. P., & Newell, A.** — *The Psychology of Human-Computer Interaction* (1983). 230 ms perception cycle — interval below which sequential items merge perceptually. +- **Nielsen Norman Group** — "Progressive Disclosure" (2006, updated 2021). Sequential disclosure improves comprehension by forcing prioritization; working memory alignment. +- **Miller, G. A.** (1956) — 7 ± 2 chunks; modern revision: 4 ± 1 for complex information. Informs stagger item count limits. +- **Framer Motion documentation** — `stagger(0.3)` default for `whileInView` variants. 300 ms / item is starting point to override, not design recommendation. +- **Josh W. Comeau** — "Action-driven motion" (joshwcomeau.com). ~125 ms entrance for hover micro-interactions. Sequential item stagger practitioner reference. + +### Accessibility +- **WCAG 2.1 SC 2.3.3** — Animation from Interactions. Provides mechanism to disable motion triggered by interaction (Level AAA). +- **WCAG 2.1 SC 2.3.1** — Three Flashes or Below Threshold (Level A). No content flashes more than 3× per second. +- **MDN Web Docs** — `prefers-reduced-motion` media query. Implementation guidance. diff --git a/CLAUDE.md b/CLAUDE.md index 6e6b5b6..6e79637 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,10 +6,10 @@ You are an expert technical writer specializing in explaining complex topics to ## Project Overview -This is **Agentic Coding**, a course designed for Senior Software Engineers. The course teaches experienced developers how to effectively leverage AI coding assistants in production environments. +This is **Agentic Coding**, a technical reference for Senior Software Engineers. It teaches experienced developers how to effectively leverage AI coding assistants in production environments. **Target Audience:** Senior engineers with 3+ years of professional experience -**Estimated Course Duration:** 24-33 hours of hands-on training +**Estimated Reading Time:** 24-33 hours of hands-on training ## Technology Stack @@ -56,7 +56,7 @@ npm run deploy # Deploy to GitHub Pages - Concise explanations - Code examples that compile and run -- Clear learning objectives per lesson +- Clear objectives per chapter - Hands-on exercises with real scenarios ## Key Configuration Files @@ -72,3 +72,11 @@ npm run deploy # Deploy to GitHub Pages - **URL:** https://agenticoding.ai - **Trigger:** Automatic on push to main branch - **Base URL:** `/` + +## Design System + +Read DESIGN_SYSTEM.md whenever visual work is involved + +## Noto Emoji + +To add a new emoji figure: `node scripts/fetch-emoji.js `, then use `` from `ActorNodes.tsx`. SVGs land in `website/static/img/emoji/`. diff --git a/COLOR_GUIDE.md b/COLOR_GUIDE.md new file mode 100644 index 0000000..2a96d63 --- /dev/null +++ b/COLOR_GUIDE.md @@ -0,0 +1,1167 @@ +# Color Scheme Generation Guide for AI Agents + +Production color palette generation using OKLCH color science, color emotion psychophysics, WCAG/APCA contrast validation, three-tier token architecture, and typography-aware accessibility engineering. Designed as agent-executable specification — every formula is code-ready. Domain-agnostic: works for any brand category. + +This guide has three phases: **Brand Strategy** (human-driven research and hue selection), **Palette Engineering** (agent-executable math), and **Typography, Localization & Accessibility** (agent verification when applying the palette to content). The first phase grounds the human operator in marketing science, color emotion research, and competitor analysis before they commit to a hue. The second phase takes that hue and generates a complete, validated token system. The third phase verifies that the palette produces legible, accessible results across font sizes, weights, scripts, and user capabilities. + +--- + +## Phase 1: Brand Strategy (Human Judgment) + +Before picking a hue, ground the decision in marketing science, color emotion research, competitor landscape, and audience psychology. The agent assists with research retrieval; the human makes the judgment call. + +### Distinctive Brand Assets Framework + +**Source:** Jenni Romaniuk, *Building Distinctive Brand Assets* (Oxford University Press, 2018). Based on the Ehrenberg-Bass Institute for Marketing Science (University of South Australia) — decades of empirical data across thousands of brands. + +Brand colors are evaluated on two axes: + +``` + HIGH UNIQUENESS + │ + Signature │ Aspirational + (own it, use │ (build fame + it heavily) │ to match) + │ + LOW FAME ──────────────┼──────────────── HIGH FAME + │ + Avoid │ Cemetery + (no value, │ (everyone uses it, + retire it) │ no one owns it) + │ + LOW UNIQUENESS +``` + +- **Fame**: % of category buyers who associate the element with *any* brand +- **Uniqueness**: % who associate it with *only your* brand + +A color shared by many competitors sits in the **Cemetery quadrant** — high fame, zero uniqueness. It triggers category recall ("a fintech app") but not brand recall ("*your* brand"). The goal is to move toward **Signature**: high fame *and* high uniqueness. + +**Key formula:** + +``` +Brand Salience = Σ (Category Entry Points × DBA Linkage Strength) +``` + +Where DBA Linkage Strength is the probability that encountering the asset triggers recall of *your* brand specifically. A unique color maximizes this linkage; a shared color dilutes it across competitors. + +### Sharp's Laws of Brand Growth + +**Source:** Byron Sharp, *How Brands Grow* (Oxford University Press, 2010); *How Brands Grow Part 2* (2016). + +Core empirical findings relevant to color selection: + +1. **Growth comes from penetration, not loyalty.** Market share differences are explained almost entirely by *how many people know you*, not how often they engage. Implication: maximize recognizability across the broadest audience. + +2. **Consumers perceive weak differentiation between rivals.** In practice, 72% of Coke drinkers also buy Pepsi. Meaningful product differences matter less than being *noticed and remembered*. Implication: invest in sensory distinctiveness (color, shape, sound), not messaging differentiation. + +3. **Mental availability drives growth.** The easier it is for someone to think of your brand in a buying/learning situation, the more likely they are to choose it. Mental availability is built through consistent, unique sensory cues encountered across many touchpoints. + +> *"Rather than striving for meaningful, perceived differentiation, marketers should seek meaningless distinctiveness."* +> — Byron Sharp + +**Application:** Copying the dominant color in your category (e.g., blue in finance, purple in AI, green in sustainability) is textbook category conformity. It maximizes category association but destroys brand-level recall. A distinctive color builds a Distinctive Brand Asset that is exclusively yours. + +### Color Emotion Science + +Color triggers measurable physiological and psychological responses along three orthogonal dimensions: Pleasure, Arousal, and Dominance (PAD). Crucially, **saturation and lightness drive emotional response more than hue does** — a finding that overturns the popular "red = exciting, blue = calm" simplification. + +**Sources:** Valdez & Mehrabian (1994), *J. Experimental Psychology: General*; Wilms & Oberfeld (2018), *Psychological Research*; Mehrabian & Russell (1974), *An Approach to Environmental Psychology*, MIT Press. + +#### Emotional Response by Color Property + +| Color Property | Pleasure (Valence) | Arousal | Dominance | Evidence Strength | +|---|---|---|---|---| +| **High lightness** | Strong ↑ | Moderate ↑ (at high sat only) | ↓ (lighter = approachable) | Very strong, cross-cultural | +| **High saturation** | Curvilinear — medium optimal | Strong ↑ (η² = .693) | ↑ (commanding) | Very strong | +| **Hue (red vs blue)** | Weak / non-significant (p = .051) | Red > Blue (high sat only) | Weak | Moderate, context-dependent | + +Key findings: + +1. **Saturation is the dominant emotional lever.** Effect size η² = .693 for arousal (Wilms & Oberfeld 2018) — the largest of any color property. Two blues at different saturations produce more divergent emotional responses than red vs. blue at the same saturation. + +2. **Medium saturation maximizes pleasantness; high saturation maximizes arousal.** These are different goals. Backgrounds and surfaces want medium chroma (pleasant, non-fatiguing). Alerts and CTAs want high chroma (attention-grabbing). Valence peaks at medium saturation (M = 5.82) and drops slightly at high saturation (M = 5.52). + +3. **Lightness → pleasure is the most reliable cross-cultural finding.** Confirmed across 30 nations (Jonauskaite et al. 2020, r = .88 cross-country agreement). Lighter colors consistently evoke positive responses; darker colors evoke authority and dominance. + +4. **Hue effects on valence are non-significant** when saturation and brightness are properly controlled (Wilms & Oberfeld 2018, p = .051). The blue > green > red preference ranking only emerges at high saturation levels. + +5. **Color emotion is context-dependent.** Elliot & Maier's Color-in-Context Theory (2012) demonstrated that the same color produces opposite behavioral responses depending on context — red increased approach motivation in romantic contexts but avoidance in achievement contexts. Any hue→emotion mapping without context specification is incomplete. + +6. **Ecological valence explains 80% of color preference.** Palmer & Schloss (2010) showed that people prefer colors associated with objects they like (blue → clear sky/water) and dislike colors associated with negative objects (brown → waste). These associations are partially domain-specific: terminal green for developers, institutional blue for finance, clinical white for healthcare. + +#### Critical Interaction Effects + +These interactions are as large as main effects — you cannot reason about dimensions independently (Wilms & Oberfeld 2018): + +| Interaction | η² | Implication | +|---|---|---| +| Hue × Saturation on arousal | .637 | Red-is-arousing only shows up at high saturation. A desaturated red feels similar to a desaturated blue. | +| Brightness × Saturation on arousal | .543 | Brightness only increases arousal when saturation is also high. A bright but desaturated color is not arousing. | +| Brightness × Saturation on valence | — | At low saturation, valence strongly depends on brightness. At high saturation, brightness matters less. | + +#### Physiological vs. Subjective Responses + +Skin conductance (autonomic arousal) correlates only moderately with self-reported arousal (r = 0.42). Saturation significantly affected skin conductance (η² = .184); hue did not (p = .113). People's felt responses and their bodies' responses are related but not identical. + +### Color Associations by Domain + +Hue associations are **contextual tendencies** modulated by saturation and lightness (per Elliot & Maier 2012), not fixed properties. At low saturation, hue-based associations largely disappear. The table below maps general associations and shows how domain context shifts their signal. + +| Color Family | General Associations | Example Domain Signals | +|---|---|---| +| Purple (260–280°) | Authority, innovation, wisdom | Education: "advanced"; AI: saturated/low uniqueness; Luxury: "premium" | +| Blue (220–250°) | Trust, reliability, competence | Finance: "institutional trust"; Tech: generic; Healthcare: "clinical calm" | +| Cyan (185–210°) | Precision, clarity, digital | Dev tools: "terminal culture"; Science: "analytical"; Health: "clinical precision" | +| Green (130–160°) | Growth, freshness, progression | Education: "leveling up"; Finance: "prosperity"; Sustainability: "natural" | +| Red (0–30°) | Energy, urgency, danger | Conflicts with error semantic — avoid as primary in any domain | +| Orange (30–55°) | Energy, warmth, boldness | Media: "creative energy"; Food: "appetite"; conflicts with warning semantic | +| Fuchsia (310–340°) | Boldness, modernity, creativity | Maximum distinctiveness; Fashion: "avant-garde"; can feel less formal | + +**Source for personality mappings:** Labrecque & Milne (2012), *J. Academy of Marketing Science* — Red → Excitement, Blue → Competence, White → Sincerity, Black/Purple/Pink → Sophistication, Brown → Ruggedness. Saturation amplifies the existing hue-personality association; it does not create new ones. + +Quantitative findings on color and cognition (SHIFT eLearning): +- Intentional color schemes amplify learning by **55–78%** and comprehension by **up to 73%** +- Cool colors (blue, green, purple) set focused moods for concentrated study +- Cognitive overload from too many bright colors is a significant risk — minimalism matters +- Colors used to segment information act as mnemonic aids for complex topics + +### Typeface Personality Science + +Font classifications trigger measurable personality associations analogous to color +emotion. Like color, typeface personality operates in PAD-adjacent space — but font +weight primarily modulates Dominance while color primarily modulates Pleasure/Arousal, +making them partially orthogonal. They should be selected jointly, not independently. + +#### Typeface Personality Dimensions + +Three dimensions (Brumberger 2003, factor analysis of 15 typefaces rated on 20 +adjective pairs): + +| Font Dimension | PAD Mapping | Description | +|---|---|---| +| Elegance | ≈ Arousal | Refined, sophisticated, distinguished | +| Directness | ≈ Dominance | Professional, stable, assertive | +| Friendliness | ≈ Pleasure | Warm, playful, approachable | + +#### Font Classification → PAD Profile + +(Synthesized from Brumberger 2003, Shaikh et al. 2006, Henderson et al. 2004, +Monotype/Neurons 2023) + +| Classification | Pleasure | Arousal | Dominance | Personality Keywords | +|---|---|---|---|---| +| Serif (Old Style) | Med | Low | Med-High | Trustworthy, traditional, scholarly | +| Serif (Transitional) | Med | Low-Med | High | Authoritative, refined, professional | +| Serif (Didone/Modern) | Med-High | Med | High | Elegant, dramatic, fashionable | +| Sans (Geometric) | Low-Med | Med | Med | Precise, cold, modern, innovative | +| Sans (Humanist) | High | Med | Med | Warm, friendly, accessible, readable | +| Sans (Neo-Grotesque) | Med | Low | Med | Neutral, corporate, invisible | +| Slab Serif | Low-Med | High | High | Bold, industrial, confident | +| Monospace | Low | Low | Med | Technical, precise, honest | +| Script (Formal) | High | Low | Low | Elegant, personal, feminine | +| Script (Casual) | High | Med | Low | Playful, creative, youthful | +| Display | Variable | High | High | Unique, bold, attention-grabbing | + +#### Font Weight as Independent Emotional Lever + +Weight operates on the Dominance axis independently of hue: + +| Weight Range | Dominance | Personality | +|---|---|---| +| 100–200 (Thin) | Low | Exclusive, delicate, luxury, minimal | +| 300 (Light) | Low-Med | Elegant, refined, airy | +| 400 (Regular) | Med | Neutral, readable, default | +| 500–600 (Medium) | Med-High | Confident, structured, clear | +| 700 (Bold) | High | Authoritative, urgent, confident | +| 800–900 (Black) | Very High | Maximum impact, commanding | + +Color saturation is the dominant arousal lever (η² = .693). Font weight is the +dominant dominance lever. They are partially orthogonal — bold + high chroma = +maximum arousal AND dominance (not merely additive). + +#### Font-Color Congruence + +Font personality and color emotion should align. When they conflict, the penalty +is real: 22% credibility loss for incongruent typography (Fox et al. 2007), and +incongruent signals create active confusion, not neutral averaging. + +Congruence examples: + +| Target Profile | Congruent Font | Congruent Color | Why It Works | +|---|---|---|---| +| Calm Trust | Humanist sans, traditional serif | Blue (210–240°), teal | Both signal accessibility + stability | +| Urgent Authority | Slab serif, bold geometric sans | Red, deep orange | Both signal dominance + arousal | +| Friendly Innovation | Humanist sans, rounded geometric | Cyan (185–210°), bright green | Both signal warmth + modernity | +| Luxury Elegance | Didone serif, light weights | Deep purple, gold | Both signal refinement + exclusivity | +| Technical Precision | Monospace, geometric sans | Cool neutrals, cyan | Both signal rationality + honesty | + +Incongruence red flags: +- Script font + saturated red → font says "elegant," color says "urgent" +- Heavy slab serif + pastel pink → font says "industrial," color says "soft" +- Comic Sans/casual script in any professional domain → immediate credibility loss + +#### Font Classification × Domain Associations + +Like color, font classifications carry domain associations. When font-domain and +color-domain signals align, the effect is multiplicative: + +| Domain | Expected Font | Expected Color | Mismatched Font Signal | +|---|---|---|---| +| Law, finance | Serif (transitional) | Navy blue, dark green | Sans-serif = "too casual" | +| Tech, SaaS | Sans (geometric/humanist) | Blue, cyan, purple | Serif = "dated" | +| Developer tools | Monospace + sans body | Cyan, green, neutral | Script = "not serious" | +| Healthcare | Humanist sans | Teal, green, blue | Display = "not trustworthy" | +| Luxury, fashion | Didone serif, thin weights | Gold, deep purple, black | Slab serif = "too industrial" | +| Education | Humanist sans, readable serif | Blue, green, warm accents | Monospace = "too technical" | + +#### Serif vs Sans-Serif: The Readability Non-Issue + +72 studies reviewed (Lund 1999): no meaningful readability difference between serif +and sans-serif on screen. Arditi & Cho (2005): the minor serif advantage sometimes +observed is a spacing artifact. Choose serif vs sans-serif for personality, not +readability. What actually affects legibility: x-height, open counters, character +differentiation, stroke uniformity, spacing (see Phase 3 § X-Height and Font Metrics). + +At sizes below ~10px on screen, serifs become noise and reduce reading speed +(Morris et al. 2002). At 18px+, they are decorative signal contributing to personality. + +#### Monospace: Developer Context + +Monospace carries precision/honesty associations (Shaikh et al. 2006: 40% chose +monospace for programming contexts) but also "dull/plain/conforming" personality. +For developer-facing brands wanting both technical credibility and approachability, +pair monospace (code blocks) with a humanist sans (body text) — the contrast creates +"warm but competent." + +Monospace typography is typically exempt from brand color — syntax highlighting +dominates. The font's personality contribution is structural (uniform advance width, +even typographic color) rather than chromatic. + +#### Cultural Variation + +Monotype/Neurons (2023, N=1,957, 8 countries): +- Humanist sans-serif scored highest for trust in 7 of 8 countries +- Germany uniquely preferred serif (Cotford) for trust +- Romance-language countries preferred classic serif styles +- Japan: low-contrast humanistic typefaces → innovation; brushstroke-feel → trust + +Font personality associations have significant cultural modulation. When targeting +non-English markets, validate font choice with locale-specific research. + +#### Evidence Quality Caveat + +Font classification → personality: strong evidence (multiple replicated studies). +Font-color interaction: weak evidence (inferential only, no controlled experiments +manipulating both variables simultaneously). The congruence framework above is +grounded in theory and converging practitioner evidence, not in direct measurement +of interaction effects. + +### Competitor Landscape Analysis + +The agent should research the current competitor color map before the human selects a hue. Use this template: + +``` +Research query: "[your category] brands and platforms primary colors [current year]" +``` + +**Competitor map template:** + +| Brand | Primary Hue | Hex | Sub-category | +|---|---|---|---| +| [Competitor 1] | ???° | #?????? | [sub-category] | +| [Competitor 2] | ???° | #?????? | [sub-category] | +| ... | | | | + +The agent populates this table via web research. The human reviews for completeness. + +**Key observations to surface:** +- Which hue zones are crowded (Cemetery quadrant — high fame, zero uniqueness)? +- Which category leaders have already differentiated away from the dominant hue? +- Which hue zones are unoccupied in your specific category? +- What is the minimum hue distance achievable from the nearest competitor (target ≥40°)? + +### Audience Credibility & Color-Brand Congruence + +**Sources:** Bottomley & Doyle (2006), *Marketing Theory*; Labrecque & Milne (2012), *J. Academy of Marketing Science*. + +Two findings create productive tension with Sharp's distinctiveness advice: + +1. **Color-product congruence increases processing fluency.** When a color "fits" the category, it increases brand recognition and positive evaluation (Bottomley & Doyle 2006). Consistent color usage can increase brand recognition by up to 80%. + +2. **Atypical colors grab attention but can decrease purchase intent.** Schema incongruity attracts notice but increases skepticism, especially in categories with strong color conventions. + +**Resolution:** Distinctiveness works when the brand has substance to back it up. Audiences in every domain evaluate credibility on content quality, transparency, and peer validation — not on whether your color matches the category leaders. A distinctive color signals confidence; a copied color signals imitation. + +**Brand personality hue mapping** (Labrecque & Milne 2012): + +| Hue | Brand Personality Dimension | +|---|---| +| Red | Excitement | +| Blue | Competence | +| White | Sincerity | +| Black, Purple, Pink | Sophistication | +| Brown | Ruggedness | + +High saturation amplifies the existing hue-personality association (e.g., saturated red = more exciting). It does not create new personality dimensions. + +### Hue Distance Formula + +When evaluating candidate hues against competitors and semantic roles, calculate angular distance on the 360° wheel: + +``` +distance(h1, h2) = min(|h1 − h2|, 360 − |h1 − h2|) +``` + +Minimum recommended distances: +- **≥30°** from any semantic role hue (error, warning, success) +- **≥40°** from the nearest direct competitor +- **≥60°** from the previous brand color (if rebranding) for the shift to be perceptible to casual viewers + +### Decision Framework + +Score each candidate hue across these dimensions: + +| Dimension | Weight | What to Evaluate | +|---|---|---| +| Differentiation | High | Hue distance from competitors; uniqueness in category | +| Semantic Safety | High | Hue distance from error (25°), warning (70°), success (155°) | +| Emotional Profile | Medium | Does the PAD profile at planned peak chroma match brand intent? (See Color Emotion Science) | +| Authority Signal | Medium | Color psychology fit for target audience | +| Dual-Mode WCAG | Medium | Can shade 600 pass AA on white AND shade 400 pass AA on dark? | +| Domain Fit | Medium | Does it signal appropriate values for your category? | +| Typeface Congruence | Medium | Does the chosen font's PAD profile align with the color's PAD profile? (See Typeface Personality Science) | +| Cultural Resonance | Low | Domain-specific subculture associations (varies by audience) | + +The human operator evaluates these qualitatively. The agent can compute hue distances and WCAG numbers to support the decision. Once a hue is chosen, everything below is math. + +--- + +## Phase 2: Palette Engineering (Agent Math) + +From this point forward, the agent generates the palette autonomously. The human provides perceptual feedback during validation. + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherance!** + +## Why OKLCH Over HSL + +HSL is not perceptually uniform. `hsl(60,100%,50%)` (yellow) appears far brighter than `hsl(240,100%,50%)` (blue) at identical lightness values. OKLCH fixes this by design — equal numerical changes produce equal visual changes across all hues. + +Three channels: +- **L** (Lightness): 0.0–1.0 — perceptual brightness +- **C** (Chroma): 0.0–~0.37 — saturation intensity +- **H** (Hue): 0°–360° — position on the color wheel + +All palette math operates in OKLCH. sRGB hex is output-only. + +## Input: Source Hue Table + +The only human judgment calls. Everything downstream is computable math. + +| Role | Hue | Peak Chroma | Rationale | +|------|-----|-------------|-----------| +| Primary | _chosen_ | 0.13–0.15 | Brand identity, CTAs, active states | +| Neutral | _same as primary_ | 0.015 | Brand-tinted gray (same hue, ~10% chroma) | +| Error | 25° | 0.15 | Convention: red-family for danger | +| Warning | 70° | 0.12 | Convention: amber for caution | +| Success | 155° | 0.13 | Teal-green — colorblind-safe vs error red | + +Error is red because users expect it. Success uses teal-green (not pure green) because teal remains distinguishable from error-red under protanopia and deuteranopia. These are convention-fixed, not derived from the brand hue. + +### Primary Hue Selection Constraints + +The primary hue must avoid collision with semantic roles: + +| Zone | Hue Range | Conflict | +|------|-----------|----------| +| Avoid | 0°–50° | Error red (25°) | +| Avoid | 55°–85° | Warning amber (70°) | +| Avoid | 140°–170° | Success teal (155°) | +| Caution | _category-specific_ | Your category's dominant competitor hue zone — low uniqueness | + +Good candidate zones: 90°–135°, 175°–210°, 215°–255°, 285°–350°. + +## Shade Scale Generation + +### Lightness Curve (Non-Linear) + +11 stops mapping to shade names. Not evenly spaced — compressed at extremes for perceptual evenness: + +``` +Shade: 50 100 200 300 400 500 600 700 800 900 950 +Lightness: 0.97 0.93 0.87 0.78 0.69 0.60 0.51 0.43 0.36 0.29 0.25 +``` + +### Parabolic Chroma Curve + +Peak saturation at midtones, taper at extremes. Prevents oversaturated near-white or muddy near-black shades: + +``` +chroma(L) = peakChroma × max(0, 1 − ((L − 0.6) / 0.5)²) +``` + +- Vertex at L=0.6 (shade 500) — maximum chroma +- Denominator 0.5 controls parabola width +- Chroma reaches zero when L deviates ±0.5 from center +- Clamp to `[0, peakChroma]` + +> *Emotional rationale: The parabolic curve peaks at medium chroma (shade 500, L=0.60), which research shows maximizes pleasantness (Valdez & Mehrabian 1994). Extreme shades taper toward zero chroma, reducing arousal — appropriate for backgrounds and subtle UI surfaces. High-chroma midtones are reserved for interactive elements where attention (arousal) is needed.* + +### Dark Mode Inversion + +Same shade names, reversed lightness indices: + +``` +L_dark(i) = LIGHTNESS_STOPS[10 − i] +``` + +Shade 50 in dark mode gets the lightness of shade 950 (0.25), and vice versa. Chroma curve is recomputed from the new lightness — not copied. + +### Bezold-Brücke Hue Compensation + +Colors perceptually drift toward yellow (~65°) or blue (~250°) as lightness changes. For production accuracy, apply per-shade hue correction. The basic implementation uses fixed hue; production systems shift hue ±2–5° per shade to counteract the drift. + +## OKLCH → sRGB Conversion + +Four-step pipeline. All intermediate values are floating-point. + +### Step 1: OKLCH → OKLab + +``` +h_rad = H × π / 180 +a = C × cos(h_rad) +b = C × sin(h_rad) +``` + +### Step 2: OKLab → LMS (cube-root space) + +``` +l' = L + 0.3963377774·a + 0.2158037573·b +m' = L − 0.1055613458·a − 0.0638541728·b +s' = L − 0.0894841775·a − 1.2914855480·b + +l = l'³ +m = m'³ +s = s'³ +``` + +### Step 3: LMS → Linear sRGB + +``` +R_lin = +4.0767416621·l − 3.3077115913·m + 0.2309699292·s +G_lin = −1.2684380046·l + 2.6097574011·m − 0.3413193965·s +B_lin = −0.0041960863·l − 0.7034186147·m + 1.7076147010·s +``` + +### Step 4: Linear → Gamma sRGB + +``` +f(x) = 12.92·x if x ≤ 0.0031308 +f(x) = 1.055·x^(1/2.4) − 0.055 if x > 0.0031308 +``` + +Clamp each channel to `[0, 1]` before gamma. Values outside sRGB gamut are clamped by reducing chroma while preserving hue (gamut mapping). + +### Hex Output + +``` +hex = '#' + toHex(f(clamp(R_lin))) + toHex(f(clamp(G_lin))) + toHex(f(clamp(B_lin))) +toHex(x) = round(x × 255).toString(16).padStart(2, '0') +``` + +## WCAG Contrast Validation + +### Relative Luminance (ITU-R BT.709) + +``` +Y = 0.2126·R_lin + 0.7152·G_lin + 0.0722·B_lin +``` + +Where `R_lin`, `G_lin`, `B_lin` are the linear (pre-gamma) sRGB values, clamped to `[0, 1]`. + +### Contrast Ratio (WCAG 2.1 §1.4.3) + +``` +CR = (Y_lighter + 0.05) / (Y_darker + 0.05) +``` + +### Thresholds + +| Level | Normal text | Large text (≥18pt or ≥14pt bold) | +|-------|-------------|----------------------------------| +| AA | ≥ 4.5:1 | ≥ 3.0:1 | +| AAA | ≥ 7.0:1 | ≥ 4.5:1 | + +### Best Text Color Selection + +``` +white_CR = (1.0 + 0.05) / (Y_bg + 0.05) +black_CR = (Y_bg + 0.05) / (0.0 + 0.05) +text_color = white_CR ≥ black_CR ? white : black +``` + +The crossover occurs around shade 500–600. This is computed, not chosen. + +> **Note:** WCAG 2.x contrast ratios are the legal compliance baseline but have known perceptual inaccuracies, especially for dark color pairs (overestimation up to 250%). Phase 3 introduces APCA (Accessible Perceptual Contrast Algorithm) as a perceptual-truth layer. Run both; flag discrepancies. + +## Semantic Token Mapping + +### Three-Tier Architecture + +| Tier | Name | Example | Purpose | +|------|------|---------|---------| +| Primitive | Raw values | `--color-primary-500: oklch(0.60 0.13 195)` | Color space, no semantics | +| Semantic | Intent | `--color-primary: var(--color-primary-600)` | Meaning, theme-switchable | +| Component | Scoped | `--button-bg: var(--color-primary)` | Component-specific binding | + +Components reference semantic tokens, never primitives. A theme swap changes only the primitive→semantic mapping. + +### Light/Dark Semantic Mapping + +Use shade **600** for light-mode semantic colors (passes AA on white). Use shade **400** for dark-mode semantic colors (passes AA on dark backgrounds). + +| Semantic Token | Light Theme | Dark Theme | +|----------------|-------------|------------| +| `--bg-page` | neutral-50 | neutral-900 | +| `--bg-surface` | white | neutral-800 | +| `--bg-muted` | neutral-100 | neutral-700 | +| `--text-primary` | neutral-900 | neutral-50 | +| `--text-secondary` | neutral-500 | neutral-400 | +| `--border` | neutral-200 | neutral-700 | +| `--primary` | primary-600 | primary-400 | +| `--on-primary` | white | primary-950 | +| `--error` | error-600 | error-400 | +| `--on-error` | white | error-950 | +| `--error-subtle` | error-50 | error-950 | +| `--warning` | warning-600 | warning-400 | +| `--warning-subtle` | warning-50 | warning-950 | +| `--success` | success-600 | success-400 | +| `--success-subtle` | success-50 | success-950 | + +### Paired Foreground Tokens + +Every semantic background needs a paired foreground with guaranteed contrast ≥ 4.5:1. Material Design 3 calls these `on-primary`, `on-error`. Without explicit pairs, agents pick arbitrary text colors and contrast breaks silently. + +## Color Harmony (Decorative Only) + +Harmony rotation produces hue sets for data visualization, illustration, and accents. **Not** for semantic roles — those are convention-fixed. + +| Harmony | Rotation from base | Use case | +|---------|-------------------|----------| +| Monochromatic | 0° (shade scale only) | Single-brand UIs | +| Analogous | ±30° | Harmonious palettes, gradients | +| Complementary | 180° | CTAs, high-contrast accents | +| Split-complementary | 150° + 210° | Balanced accent pairs | +| Triadic | ±120° | Data visualization | +| Tetradic | 90° intervals | Complex UIs needing 4+ hues | + +Apply harmony at shade 500 lightness (L=0.60) using the parabolic chroma at that lightness. + +## Dark Mode Best Practices + +- Never use pure black (`#000000`) — causes halation (white text bleeds). Use dark grays: `#0d1117`, `#1a1a2e`, `#161b22`. +- Never use pure white (`#ffffff`) for body text on dark — use off-white: `#e6edf3`, `#d4d4d4`. +- Pure white is acceptable for headings and high-emphasis text. +- The 60-30-10 rule: 60% dark background, 30% mid-tone surfaces, 10% accent color. +- Neutral scale uses brand hue at 0.015 chroma — produces brand-tinted gray, not pure gray. + +> **Note:** Dark mode introduces additional perceptual effects (astigmatic halation, APCA polarity asymmetry, chroma overstatement) covered in Phase 3 § Accessibility Matrix > Dark Mode Accessibility. + +## Validation Checklist + +For every generated palette, verify: + +- [ ] 5 roles × 11 shades = 55 primitive tokens generated +- [ ] Every shade has both OKLCH and hex values +- [ ] Shade 600 of each role passes WCAG AA (≥4.5:1) on white +- [ ] Shade 400 of each role passes WCAG AA (≥4.5:1) on dark background +- [ ] Shade 700 provides AA fallback if 600 is marginal in light mode +- [ ] Neutral scale chroma capped at 0.015 +- [ ] Every semantic background has a paired `on-*` foreground token +- [ ] No out-of-gamut values (all hex channels in 00–ff) +- [ ] Primary hue ≥30° from any semantic role hue +- [ ] Light and dark theme mappings are complete and cross-validated + +Phase 3 extends this checklist with typography, localization, and accessibility verification items. If the palette will be applied to text content, complete both checklists. + +--- + +## Phase 3: Typography, Localization & Accessibility (Agent Verification) + +Phase 2 produces a validated color palette. Phase 3 verifies that the palette produces legible, accessible results when applied to actual text content. Contrast is not a property of two colors — it is a function of two colors + font size + font weight + x-height ratio + script complexity + rendering context + user capabilities. + +This phase is required when the palette will be used with text. It can be skipped if the palette is decorative-only (e.g., data visualization with labeled values). + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherence!** + +### APCA: Next-Generation Contrast Model + +WCAG 2.x contrast ratios (Phase 2) remain the legal compliance baseline. APCA (Accessible Perceptual Contrast Algorithm) is the perceptual-truth layer used in WCAG 3.0 drafts. Use both; flag discrepancies. + +**Why APCA over WCAG 2.x:** + +WCAG 2.x uses a simple luminance ratio derived from a 1988 CRT standard. It has three documented failure modes: + +1. **False passes:** Dark-on-dark combinations that compute CR ≥ 4.5:1 but are actually unreadable (measure only Lc 33 in APCA). +2. **False fails:** Readable light-gray-on-white combinations that fail CR 3:1 but measure Lc 61 in APCA. +3. **Dark pair overestimation:** WCAG 2.x overestimates contrast for very dark color pairs by 200–250%. + +Additionally, WCAG 2.x is **polarity-blind** — it returns the same ratio for white-on-black and black-on-white. Human perception is asymmetric: light-on-dark produces halation effects that reduce readability compared to dark-on-light at equivalent luminance differences. + +**APCA Lc (Lightness Contrast):** + +APCA produces a signed value: positive for dark text on light background, negative for light text on dark background. Use the absolute value |Lc| for threshold comparison. The Lc scale is perceptually uniform — equal increments represent equal perceived changes across the entire luminance range. + +Reference implementation: `apca-w3` npm package (Myndex). Do not implement the algorithm manually — it involves piecewise power curves with multiple exponents. + +**Lc Thresholds by Use Case:** + +| Use Case | Minimum |Lc| | Preferred |Lc| | Notes | +|---|---|---|---| +| Body text (14–16px, weight 400) | 75 | 90 | Primary reading content | +| Subheadings (18–24px, weight 600–700) | 60 | 75 | Section navigation | +| Large headlines (32px+, weight 700+) | 45 | 60 | Display text | +| Secondary text (captions, bylines) | 45 | 60 | Metadata, timestamps | +| Placeholder text, disabled | 30 | 40 | Non-essential, ghosted | +| Decorative text, watermarks | 15 | 25 | Not intended to be read fluently | +| Non-text UI (icons, borders, focus rings) | 45 | 60 | WCAG 1.4.11 equivalent | + +**Dual-Model Rule:** + +``` +Run both WCAG 2.x CR and APCA Lc for every text-color pair. +Flag if: + - WCAG passes (CR ≥ 4.5) but APCA fails (|Lc| < 75 for body text) + - WCAG fails (CR < 4.5) but APCA passes (|Lc| ≥ 75 for body text) +Report both values. Use WCAG for legal compliance; use APCA for perceptual truth. +``` + +**Sources:** Andrew Somers, *APCA Readability Criterion* (W3C Silver/WCAG 3.0 draft); Somers, "The Realities And Myths Of Contrast And Color" (*Smashing Magazine*, 2022). + +### Font Size × Weight × Contrast Matrix + +Human contrast sensitivity is a function of **spatial frequency**. Thinner strokes at smaller sizes produce higher spatial frequency, which the visual system resolves with significantly lower contrast sensitivity. The same gray text on white appears less readable in weight 300 than weight 600 — identical luminance difference, but the perceived contrast drops because the spatial frequency is higher. + +APCA replaces WCAG's binary "normal/large text" with a continuous lookup table. Weight and size trade off against each other: + +**Minimum |Lc| by Font Size and Weight (APCA Silver Level):** + +| Size (px) | W100 | W200 | W300 | W400 | W500 | W600 | W700 | W800 | W900 | +|---|---|---|---|---|---|---|---|---|---| +| 12 | — | — | — | — | — | — | — | — | — | +| 14 | — | — | — | 100 | 100 | 90 | 75 | — | — | +| 15 | — | — | — | 100 | 90 | 75 | 70 | — | — | +| 16 | — | — | — | 90 | 75 | 70 | 60 | 60 | — | +| 18 | — | — | 100 | 75 | 70 | 60 | 55 | 55 | 55 | +| 21 | — | — | 90 | 70 | 60 | 55 | 50 | 50 | 50 | +| 24 | — | — | 75 | 60 | 55 | 50 | 45 | 45 | 45 | +| 28 | — | 100 | 70 | 55 | 50 | 45 | 43 | 43 | 43 | +| 32 | — | 90 | 65 | 50 | 45 | 43 | 40 | 40 | 40 | +| 36 | — | 75 | 60 | 45 | 43 | 40 | 38 | 38 | 38 | +| 42 | 100 | 70 | 55 | 43 | 40 | 38 | 35 | 35 | 35 | +| 48 | 90 | 60 | 50 | 40 | 38 | 35 | 33 | 33 | 33 | +| 60 | 75 | 55 | 45 | 38 | 35 | 33 | 30 | 30 | 30 | +| 72 | 60 | 50 | 40 | 35 | 33 | 30 | 30 | 30 | 30 | +| 96 | 50 | 45 | 35 | 33 | 30 | 30 | 30 | 30 | 30 | + +`—` = prohibited: insufficient stroke density for legibility at any contrast level. + +**Practical shade selection rule:** + +``` +Given: font_size, font_weight, background_shade +1. Look up required_Lc from the table above +2. Iterate candidate text shades from darkest (or lightest on dark bg): + text_shade = first shade where |Lc(text, bg)| >= required_Lc +3. If no shade passes: increase font_size or font_weight until a shade qualifies +``` + +> *Note: These are 2023 draft values from Myndex APCA Silver. The canonical source is the `apca-w3` package. Values may shift as the WCAG 3.0 specification finalizes.* + +**Source:** Myndex, *APCA Silver Level Font Lookup Tables* (2023 draft). + +### Text Chroma Limits (Helmholtz-Kohlrausch Effect) + +Text readability depends almost entirely on **luminance contrast**. The visual cortex resolves letter shapes through the achromatic luminance channel. Chromatic channels (OKLab a, b axes) process hue and saturation at much lower spatial resolution — they contribute to object categorization but cannot resolve fine text strokes. + +The **Helmholtz-Kohlrausch (H-K) effect** causes highly saturated colors to appear perceptually brighter than their measured luminance. Two color pairs with identical Lc values can produce vastly different reading comfort — the high-chroma pair causes halation and eye strain even though the computed contrast is equivalent. + +**Chroma Budget by Text Role (OKLCH C value):** + +| Text Role | Max Chroma | Rationale | +|---|---|---| +| Body text (paragraphs, lists) | C < 0.04 | Minimize H-K distortion, maximize stroke clarity | +| Labels, captions | C < 0.04 | Same spatial frequency as body text | +| Interactive text (links, buttons) | C < 0.08 | Brand expression allowed; must verify Lc independently of chroma | +| Display headings (28px+) | C < 0.12 | Larger size reduces spatial frequency, tolerates more chroma | +| UI surfaces (button fills, badges) | Full palette | No fine detail to resolve at surface level | +| Backgrounds | Full palette | Large area, low spatial frequency | +| Decorative / illustrative | Full palette | Not read as text | + +**Rule:** + +``` +For any token assigned as a text foreground color: + assert oklch_chroma(token) <= chroma_limit[role] +If brand color must appear as text, reduce chroma to the role limit and re-verify Lc. +``` + +**Sources:** Helmholtz (1867); von Kohlrausch (1935); Fairchild & Pirrotta (1991), "Predicting the brightness of different hues." + +### X-Height and Font Metrics + +The APCA lookup table assumes an **x-height ratio ≥ 0.5** (approximately Inter, Roboto, system sans-serif). CSS `font-size` specifies the em square — not the visible height of lowercase letters. Two fonts at `font-size: 16px` can differ by 38% in actual rendered letter height: + +| Font | x-height Ratio | Effective Size at 16px CSS | Category | +|---|---|---|---| +| Verdana | 0.55 | 17.6px | System sans | +| Inter | 0.54 | 17.3px | Modern sans | +| Roboto | 0.53 | 17.0px | Modern sans | +| Noto Sans | 0.52 | 16.6px | Universal sans | +| Noto Sans CJK | 0.52 | 16.6px (but see Localization) | CJK sans | +| Georgia | 0.48 | 15.4px | System serif | +| Times New Roman | 0.45 | 14.4px | System serif | +| Garamond | 0.41 | 13.1px | Classic serif | + +**Compensation formula:** + +``` +effective_size = css_font_size × (actual_x_height_ratio / 0.5) +``` + +Use `effective_size` when consulting the APCA font-size × weight lookup table. A font with x-height ratio 0.41 (Garamond) at 16px CSS is effectively 13.1px for contrast purposes — requiring significantly higher |Lc| than 16px Inter. + +**Font rendering caveat:** + +`-webkit-font-smoothing: antialiased` forces grayscale antialiasing, which reduces perceived weight and contrast by ~10–15% on standard-DPI displays (~96 DPI). On HiDPI (≥192 DPI), the effect is imperceptible. Gate it behind a resolution media query: + +```css +@media (min-resolution: 192dpi) { + body { -webkit-font-smoothing: antialiased; } +} +/* Standard DPI: leave as system default (subpixel rendering) */ +``` + +Palettes validated on Retina displays must be re-verified on standard-resolution monitors. + +### Localization Constraints + +The APCA lookup table and all preceding contrast calculations assume **Latin script with x-height ratio ~0.52**. Scripts with higher stroke complexity pack more detail into each glyph, creating higher spatial frequency at the same nominal size. This requires larger minimum sizes, heavier weights, and higher contrast targets. + +**Script Complexity and Minimum Thresholds:** + +| Script | Avg Strokes/Glyph | Min Body Size | Min Weight | Min Line-Height | Max Chars/Line | APCA Offset | +|---|---|---|---|---|---|---| +| Latin | 2–3 | 14px | 300 | 1.4× | 75 | 0 (baseline) | +| Cyrillic | 2–3 | 14px | 300 | 1.4× | 75 | 0 | +| Greek | 2–3 | 14px | 300 | 1.4× | 75 | 0 | +| CJK (Han/Kanji) | 8–12 | 16px (18px pref.) | 400 | 1.6× | 40 | −3px | +| Korean (Hangul) | 4–6 | 16px | 400 | 1.5× | 45 | −2px | +| Arabic | 3–5 (connected) | 16px | 400 | 1.7× | 50 | −2px | +| Devanagari | 4–6 | 16px | 400 | 1.8× | 55 | −2px | +| Thai | 3–5 (stacking) | 16px | 400 | 1.8× | 50 | −2px | +| Vietnamese (Latin+) | 2–3 | 14px (16px pref.) | 300 | 1.7× | 70 | 0 | + +**APCA Offset** means: when consulting the font-size × weight lookup table, subtract this value from the CSS font size to get the effective size for that script. CJK at 18px behaves like Latin at 15px for legibility purposes. + +**CJK-specific rules:** + +``` +CJK thin weights (100–200) are prohibited — stroke merging makes text illegible. +CJK body text under 20px: target |Lc| 90 minimum (not the standard 75). +CJK line width: max 40 characters (WCAG SC 1.4.8). +``` + +**Arabic-specific rules:** + +``` +Arabic harakat (vowel marks) at 14px render at 2–3px height — illegible on standard screens. +Minimum 16px for Arabic with diacritics; 18px preferred. +Connected cursive creates variable stroke widths within words — verify Lc at thinnest stroke points. +``` + +**Diacritical mark thresholds:** + +| Script | Feature | Minimum Size | Preferred Line-Height | +|---|---|---|---| +| Vietnamese | Stacked diacritics (e.g., ệ, ở) | 14px (16px pref.) | 1.7× | +| Thai | Up to 4 vertical stacking marks | 16px | 1.8× | +| Arabic | Harakat (shaddah + fathah stacking) | 16px (18px pref.) | 1.7× | +| Hebrew | Nikkud (vowel points) | 14px (16px pref.) | 1.5× | + +Universal safe line-height for any script with diacritical marks: **1.7×**. + +**Text expansion and line length:** + +Translation from English expands text, which pushes line length toward the upper bound where color fatigue increases: + +| Target Language | Expansion (medium strings) | Line Length Adjustment | +|---|---|---| +| German | +20–35% | Reduce max-width to ~58ch | +| Finnish | +30–40% | Reduce max-width to ~55ch | +| French, Spanish | +15–20% | Reduce max-width to ~62ch | +| Russian | +20% | Reduce max-width to ~60ch | +| CJK | −20% char count (full em-width) | Use max-width: 40ch (WCAG) | + +**Font fallback and metric consistency:** + +System CJK fonts across platforms (Hiragino Sans, Yu Gothic, PingFang, Malgun Gothic) have different ascent/descent ratios. A fallback font with a smaller x-height drops the effective visual size by ~13%, potentially failing contrast checks that passed for the primary font. + +Mitigation with CSS `@font-face` descriptors: + +```css +@font-face { + font-family: 'CJK Fallback'; + src: local('Noto Sans CJK SC'), local('Hiragino Sans'); + size-adjust: 113%; /* Compensate for x-height mismatch */ + ascent-override: 88%; /* Normalize vertical metrics */ +} +``` + +**Font loading performance budgets:** + +| Scope | WOFF2 Budget | Strategy | +|---|---|---| +| Latin-only | < 100 KB | Variable font, subset Latin + Latin Extended | +| Latin + 1 CJK region | < 500 KB | unicode-range subsetting via Google Fonts | +| Full multilingual (Noto) | Subset per locale | On-demand loading by detected script | + +> *Note: WCAG and APCA do not define script-specific contrast ratios. The offsets and thresholds above are derived from stroke-density analysis and spatial frequency research. They represent engineering best practice, not specification requirements.* + +### Accessibility Matrix + +#### Color Vision Deficiency (CVD) + +APCA solves **luminance contrast**. It does NOT solve **hue confusion**. Common CVD types (protan, deutan, tritan) have normal luminance perception — their problem is that certain hue pairs collapse to the same perceived color. Hue confusion must be solved through redundant encoding, not contrast algorithms. + +**Redundant encoding rule:** + +``` +Every piece of information conveyed by color MUST also be conveyed by +at least one non-color channel: icon shape, text label, pattern, position, or underline. +``` + +**Specific CVD hazards:** + +| CVD Type | Prevalence | Confused Pairs | Critical Hazard | +|---|---|---|---| +| Protanopia (no L-cone) | ~1% male | Red↔green, red↔black, red↔brown | Red on black: appears near-zero contrast | +| Deuteranopia (no M-cone) | ~1% male | Red↔green, green↔brown | Same red-green confusion as protan | +| Tritanopia (no S-cone) | ~0.01% | Blue↔yellow, blue↔green | Rare; covered by redundant encoding | +| Achromatopsia | ~0.003% | All hue pairs | Relies entirely on luminance contrast | + +**Safe categorical palette (Okabe-Ito):** + +For data visualization or any context requiring ≥6 distinguishable categories under all common CVD types: + +| Name | Hex | Approximate OKLCH | +|---|---|---| +| Black | #000000 | L=0 C=0 H=0 | +| Orange | #E69F00 | L=0.74 C=0.15 H=75 | +| Sky Blue | #56B4E9 | L=0.72 C=0.10 H=230 | +| Bluish Green | #009E73 | L=0.60 C=0.13 H=170 | +| Yellow | #F0E442 | L=0.92 C=0.17 H=100 | +| Blue | #0072B2 | L=0.50 C=0.12 H=245 | +| Vermillion | #D55E00 | L=0.56 C=0.18 H=40 | +| Reddish Purple | #CC79A7 | L=0.62 C=0.10 H=350 | + +**Source:** Okabe & Ito (2002), "Color Universal Design." + +#### Low Vision + +APCA introduces **contrast reserve** — the gap between bare legibility (just-noticeable difference) and fluent reading speed. Body text needs Lc 75 minimum for legibility, Lc 90 for comfortable sustained reading. Low vision users (20/70 to 20/200 acuity) operate with reduced contrast sensitivity and benefit disproportionately from the higher end of this range. + +| Text Role | Min |Lc| | Preferred |Lc| | Min x-height | Min Weight | +|---|---|---|---|---| +| Body text | 75 | 90 | 9px | 400 | +| Captions, secondary | 60 | 75 | 7px | 400 | +| Headlines | 45 | 60 | — | 600+ | +| Interactive (buttons, links) | 60 | 75 | 9px | 500 | + +#### Dyslexia (5–10% of Population) + +OpenDyslexic and similar "dyslexia fonts" have **no robust evidence of benefit** (Rello & Baeza-Yates 2013; Kuster et al. 2018). What does measurably improve dyslexic reading speed: + +``` +letter-spacing: >= 0.12em +word-spacing: >= 0.16em +line-height: >= 1.5× +font-family: sans-serif (no italic for body text) +background: cream/pastel preferred over pure white (reduces glare) +contrast: CR 15–18:1 preferred over maximum 21:1 (avoids "halation" on white) +text-align: left (never justified — uneven word spacing disrupts reading) +``` + +These thresholds align with WCAG SC 1.4.12 (Text Spacing) user override requirements — a palette designed for dyslexic readability automatically passes SC 1.4.12. + +**Source:** Rello & Baeza-Yates (2013), "Good fonts for dyslexia," *ASSETS '13*. + +#### Aging (60+ Population) + +By age 80, less than one-third of blue light (480 nm) passes through the yellowed crystalline lens. Pupil diameter decreases from ~6.6 mm (age 20–30) to ~5.3 mm (age 50+), reducing retinal illumination. Combined with presbyopia pushing screens farther away (reducing effective font size): + +``` +Rules for aging-accessible palettes: + - Body text: >= 18px, weight >= 400 + - Contrast: increase Lc targets by 15 over baseline (body text |Lc| 90+) + - Blue dependence: never use blue as the sole differentiator between states + - Blue text on dark backgrounds: particularly poor for aging eyes; + shift toward cyan (hue ~195°) or green to maintain luminance +``` + +#### Cognitive Load Limits + +Evidence converges on maximum complexity budgets beyond which comprehension degrades: + +``` +Font families: 1–2 maximum (3 with monospace for code) +Emphasis colors: 2–3 maximum (beyond neutral scale) +Categorical colors: 6–8 maximum (charts, tags, status indicators) +Line length: 50–75 characters (66 ideal) +Motion: respect prefers-reduced-motion — no parallax, auto-play, or rapid transitions +``` + +Font pairing: use contrasting strategy (different classifications with complementary +personalities, e.g., serif heading + sans body). Concordant pairing (two similar fonts) +risks monotony. Conflicting pairing (two display fonts) creates visual noise. A typical +three-font system: display/personality heading + neutral body + monospace code. + +~35% of users report motion sensitivity. The `prefers-reduced-motion` media query is essential for any animation or color transition. + +#### User Style Overrides + +Users may activate Windows High Contrast Mode (now "Contrast Themes"), browser zoom, text-only zoom, or custom stylesheets. A well-designed token system must survive these overrides. + +**Detection:** + +```css +@media (forced-colors: active) { + /* System colors replace all custom colors. + Borders, outlines, and text decorations remain visible. + Background-color differentiation is lost. */ +} +``` + +**Design rules for override resilience:** + +- Use `outline: 3px solid transparent` for focus states — invisible normally, becomes visible in forced-colors mode: + +```css +:focus-visible { + outline: 3px solid transparent; + outline-offset: 2px; + box-shadow: 0 0 0 3px var(--primary); +} + +@media (forced-colors: active) { + :focus-visible { + outline-color: Highlight; + box-shadow: none; + } +} +``` + +- WCAG SC 1.4.12 requires that user-overridden text spacing (line-height 1.5×, letter-spacing 0.12em, word-spacing 0.16em, paragraph-spacing 2×) does not break layout. Never use fixed `height` on text containers; use `min-height` or no height constraint. +- Token naming must be purpose-based (`--text-primary`, `--bg-surface`), not appearance-based (`--dark-gray`, `--light-bg`). Purpose-based names remain meaningful after user overrides remap the actual colors. + +#### Color-Only Information (WCAG 1.4.1) + +``` +Triple encoding rule: + Every status indicator = color + icon + text + + Error: red border + ⚠ icon + "Error: ..." text + Success: green border + ✓ icon + "Success: ..." text + Warning: amber border + △ icon + "Warning: ..." text + + Links: must have underline (or equivalent non-color indicator), not just color + Charts: must have shapes/patterns/labels, not just color-coded bars/lines +``` + +#### Photosensitivity + +Saturated red flashes pose the highest seizure risk: + +``` +Red flash threshold: + R / (R + G + B) >= 0.8 AND chromaticity shift > 0.2 → seizure risk + +General flash threshold: + Maximum 3 flashes per second + Flashing area must be < 25% of a 10° visual field (~341 × 256 CSS pixels) + +Mitigation: + prefers-reduced-motion disables all flashing and animation + No large-area saturated red transitions at any speed +``` + +**Source:** WCAG SC 2.3.1; Harding & Harding (2010). + +#### Dark Mode Accessibility + +Astigmatism affects 30–60% of the population. On dark backgrounds, pupil dilation increases optical aberrations, causing bright text to visually "bleed" into surrounding dark pixels (**halation**). This effect compounds with thin font weights and high contrast. + +``` +Dark mode rules: + Background: never pure black (#000). Use #121212 to #1E1E1E (OKLCH L 0.15–0.18). + Body text: never pure white. Cap at neutral-200 range (OKLCH L ~0.87). + Chroma: reduce text and surface chroma by 20–30% (multiply C by 0.7–0.8). + Body text Lc: cap at |Lc| 85–90 (pure white on pure black = Lc ~106, which is excessive). + Weight: use weight >= 400 for body text; increase by one stop vs. light mode. + Size: increase font-size by 1–2px vs. light mode for equivalent readability. + Light toggle: always offer a light mode alternative — dark mode is not universally better. + Polarity: APCA Lc values differ for light-on-dark vs. dark-on-light. + Always compute for actual polarity, never assume symmetry. +``` + +### Variable Fonts as Accessibility Tools + +Three variable font axes directly interact with contrast requirements: + +**Optical size (`opsz`):** Automatically adjusts glyph design for the intended display size. At small sizes (8–14), the font thickens stems, opens counters, and increases x-height — effectively moving leftward (heavier) in the APCA weight table without changing `font-weight`. Enable with: + +```css +body { font-optical-sizing: auto; } +``` + +Fonts with `opsz` axis: Inter, Roboto Flex, Source Sans 3, Segoe UI Variable, SF Pro. + +**Grade (`GRAD`):** Changes perceived stroke density without altering character advance widths (no layout reflow). Use cases: +- Dark mode: increase grade by +50 to counteract halation-induced perceived weight loss +- Hover/active states: change perceived emphasis without layout shift + +```css +[data-theme='dark'] body { + font-variation-settings: 'GRAD' 50; +} +``` + +**Continuous weight (`wght`):** Variable fonts support any weight value (not just 100-step increments). This allows precise targeting of the exact stroke thickness needed to meet a specific Lc threshold at a given size, rather than rounding to 400 or 700. + +### Typography Layout and Color Fatigue + +Typographic layout properties interact with color to affect readability and eye strain during sustained reading: + +**Line length:** + +``` +Optimal: 45–75 characters (66 ideal) +Implementation: max-width: 66ch on text containers +CJK: max-width: 40ch (WCAG SC 1.4.8) +``` + +**Line height by context:** + +| Context | Line Height | Rationale | +|---|---|---| +| Body text (Latin) | 1.4–1.6× | Saccade return accuracy over multiple lines | +| Body text (CJK, Thai, Devanagari) | 1.6–1.8× | Stroke density + diacritical clearance | +| Body text (Arabic with harakat) | 1.7× | Vowel mark clearance | +| Headings | 1.1–1.2× | Tight for visual cohesion at large sizes | +| Code blocks | 1.3–1.5× | Monospace character alignment | + +**Color fatigue interactions:** + +- High contrast + long lines (>75ch) = increased saccade fatigue. If line length exceeds 75ch and cannot be reduced, target Lc 80 rather than 90 for body text. +- Saturated backgrounds + sustained reading = chromatic adaptation stress. Use backgrounds at OKLCH C < 0.02 for content surfaces. +- **Paper reading experience** (Somers): Surround the text block with a neutral surface (`--bg-surface`) at ~85% of page background luminance, creating a comfortable luminance transition. This reduces peak luminance while maintaining high Lc within the reading area. + +### Extended Validation Checklist (Phase 3) + +For every palette applied to text content, verify in addition to the Phase 2 checklist: + +**Contrast (dual-model):** +- [ ] Body text (14–16px, W400): |Lc| ≥ 75 AND CR ≥ 4.5:1 +- [ ] Subheadings: |Lc| ≥ 60 AND CR ≥ 3.0:1 (large text) +- [ ] Headlines (32px+): |Lc| ≥ 45 AND CR ≥ 3.0:1 +- [ ] Non-text UI (icons, borders): |Lc| ≥ 45 AND CR ≥ 3.0:1 +- [ ] All WCAG/APCA discrepancies flagged and documented + +**Typography:** +- [ ] Text chroma verified: body text C < 0.04, interactive C < 0.08 +- [ ] X-height ratio of chosen font ≥ 0.5 (or effective_size compensation applied) +- [ ] Font-smoothing antialiased gated behind ≥192dpi media query +- [ ] Line length: max-width set to 66ch (or script-appropriate value) +- [ ] Line height: ≥ 1.4× for Latin body, ≥ 1.6× for CJK/Arabic/Devanagari + +**Localization (if multilingual):** +- [ ] CJK body text: ≥ 16px, weight ≥ 400, APCA offset applied +- [ ] Arabic body text: ≥ 16px, harakat legible +- [ ] Diacritical scripts: line-height ≥ 1.7× +- [ ] Font fallback metrics adjusted (size-adjust, ascent-override) +- [ ] Text expansion tested for longest target languages + +**Accessibility:** +- [ ] All color-only signals have redundant encoding (icon + text) +- [ ] Error/success/warning states use triple encoding +- [ ] Links have non-color indicators (underline or equivalent) +- [ ] Dark mode: no pure black background, body text |Lc| capped at 85–90 +- [ ] Dark mode: chroma reduced 20–30% vs. light mode +- [ ] `@media (forced-colors: active)` tested — focus states and borders visible +- [ ] Text spacing override (SC 1.4.12) does not break layout +- [ ] No saturated-red flashing > 3 Hz +- [ ] `prefers-reduced-motion` respected + +## References + +### Color Science +- **Björn Ottosson** — OKLCH/OKLab color space specification (2020) +- **W3C** — WCAG 2.1 Success Criterion 1.4.3 (Contrast Minimum), 1.4.11 (Non-text Contrast) +- **ITU-R BT.709** — Relative luminance coefficients (R=0.2126, G=0.7152, B=0.0722) +- **CSS Color Level 4** — `oklch()` function specification +- **Bezold-Brücke effect** — Hue shift under varying luminance (psychophysics) +- **Material Design 3** — Paired foreground token pattern (`on-primary`, `on-error`) + +### Branding & Marketing Science +- **Sharp, B.** — *How Brands Grow* (Oxford University Press, 2010). Empirical evidence that brand growth is driven by penetration and mental availability, not perceived differentiation. Based on Ehrenberg-Bass Institute data across thousands of brands and multiple decades. +- **Sharp, B.** — *How Brands Grow Part 2* (Oxford University Press, 2016). Extended findings on emerging markets and services. Reinforces that distinctiveness drives recall while differentiation claims are poorly perceived by consumers. +- **Romaniuk, J.** — *Building Distinctive Brand Assets* (Oxford University Press, 2018). Operational framework for creating and measuring Distinctive Brand Assets (DBAs). Introduces the Fame × Uniqueness grid and DBA linkage measurement methodology. +- **Ehrenberg-Bass Institute for Marketing Science** — University of South Australia. The empirical research institution behind Sharp and Romaniuk's work. Studies spanning FMCG, technology, services, and B2B markets. + +### Color Emotion & Psychophysiology +- **Wilms, L., & Oberfeld, D.** — "Color and emotion: Effects of hue, saturation, and brightness" (*Psychological Research*, 2018). Most methodologically rigorous factorial study: 62 participants, 27 chromatic colors, CIE LCh colorimetric control, SAM + physiological measures. Saturation is the dominant driver of arousal (η² = .693). Hue did not significantly affect valence (p = .051). +- **Valdez, P., & Mehrabian, A.** — "Effects of color on emotions" (*Journal of Experimental Psychology: General*, 1994). Established that brightness is the strongest predictor of pleasure, saturation the strongest predictor of arousal. Set the methodological standard for independent manipulation of color dimensions. +- **Elliot, A. J., & Maier, M. A.** — "Color-in-context theory" (*Advances in Experimental Social Psychology*, 2012). Color's psychological effect is moderated by context — same color produces opposite responses (approach vs. avoidance) depending on the situation. +- **Jonauskaite, D., et al.** — "Universal patterns in color-emotion associations are further shaped by linguistic and geographic proximity" (*Psychological Science*, 2020). 4,598 participants across 30 nations. Cross-country agreement r = .88. Lightness-valence association confirmed cross-culturally. +- **Palmer, S. E., & Schloss, K. B.** — "An ecological valence theory of human color preferences" (*PNAS*, 2010). 80% of color preference variance explained by the affective valence of associated objects (WAVE model). +- **Mehrabian, A., & Russell, J. A.** — *An Approach to Environmental Psychology* (MIT Press, 1974). The Pleasure-Arousal-Dominance (PAD) framework for emotional response to environments. + +### Color Psychology, Education & Brand Personality +- **SHIFT eLearning** — Research on color's impact on learning outcomes. Findings: intentional color schemes amplify learning by 55–78% and comprehension by up to 73%. Cool colors (blue, green, purple) promote focused learning moods. +- **Labrecque, L. I., & Milne, G. R.** — "Exciting red and competent blue: The importance of color in marketing" (*Journal of the Academy of Marketing Science*, 2012). Mapped hues to Aaker's brand personality dimensions. Saturation amplifies existing hue-personality associations. +- **Bottomley, P. A., & Doyle, J. R.** — "The interactive effects of colors and products on perceptions of brand logo appropriateness" (*Marketing Theory*, 2006). Color-product congruence matters more than color alone. Atypical colors grab attention but decrease purchase intent. +- **Adams, F. M., & Osgood, C. E.** — "A cross-cultural study of the affective meanings of color" (*Journal of Cross-Cultural Psychology*, 1973). Semantic differential scales across 23 cultures. Brightness-valence association stable cross-culturally. + +### Typeface Personality & Classification +- **Brumberger, E.** — "The rhetoric of typography: The awareness and impact of typeface appropriateness" (*Technical Communication*, 2003). Factor analysis of 15 typefaces on 20 adjective pairs. Three personality dimensions: Elegance, Directness, Friendliness. +- **Shaikh, A.D., Chaparro, B.S., & Fox, D.** — "Perception of fonts: Perceived personality traits and uses" (*Usability News*, 2006). 78% consistency in font-domain matching. Monospace associated with programming contexts by 40% of participants. +- **Henderson, P.W., Giese, J.L., & Cote, J.A.** — "Impression management using typeface design" (*Journal of Marketing*, 2004). Typeface design characteristics systematically predict brand personality impressions. +- **Monotype & Neurons** — Cross-cultural typeface perception study (2023). N=1,957, 8 countries. Humanist sans scored highest for trust in 7/8 countries. Significant cultural modulation in serif/sans preference. +- **Arditi, A., & Cho, J.** — "Serifs and font legibility" (*Vision Research*, 2005). Minor serif advantage is a spacing artifact, not a serif benefit. +- **Lund, O.** — *Knowledge construction in typography: The case of legibility research and the legibility of sans serif typefaces* (PhD thesis, University of Reading, 1999). Review of 72 legibility studies: no meaningful serif vs sans-serif difference on screen. +- **Morris, R.A., et al.** — "Serifs slow RSVP reading at very small sizes but don't matter at larger sizes" (2002). Serifs become noise below ~10px; decorative signal at 18px+. +- **Fox, D., Shaikh, A.D., & Chaparro, B.S.** — "Effect of typeface appropriateness on the perception of documents" (2007). 22% credibility loss for incongruent typography. +- **Koch, B.E.** — *Emotional response to typographic design* (Doctoral dissertation, 2011). Emotional responses to type parallel color emotion on PAD-adjacent dimensions. + +### Contrast & Typography Science +- **Somers, A.** — *APCA Readability Criterion* (W3C Silver/WCAG 3.0 draft, 2022–present). Accessible Perceptual Contrast Algorithm. Polarity-sensitive, font-size-aware contrast model for WCAG 3.0. Reference implementation: `apca-w3` npm package (Myndex). +- **Somers, A.** — "The Realities And Myths Of Contrast And Color" (*Smashing Magazine*, 2022). Comprehensive explanation of spatial frequency, APCA vs. WCAG 2.x failures, and font-size × weight interaction with contrast. +- **Myndex** — *APCA Silver Level Font Lookup Tables* (2023 draft). Minimum Lc values by font size and weight. Derived from visual acuity and spatial frequency research. +- **Fairchild, M. D., & Pirrotta, E.** — "Predicting the brightness of different hues" (1991). Quantification of the Helmholtz-Kohlrausch effect: chromatic colors appear brighter than achromatic colors of equal luminance. +- **Whittaker, S. G., & Lovie-Kitchin, J.** — Research on critical size, critical contrast, and contrast reserve for reading. Referenced throughout APCA documentation as the empirical foundation for Lc threshold calibration. + +### Localization & Script Research +- **W3C Internationalization (i18n)** — Best practices for text sizing, line breaking, and vertical text across scripts. Script-specific typography considerations. +- **Noto Fonts** — Google's open-source font family covering 800+ languages. Variable font support for weight 100–900. CJK WOFF2 subset: ~200–500 KB with unicode-range. +- **WCAG SC 1.4.8** — Visual Presentation (AAA): specifies ≤40 characters per line for CJK scripts, ≤80 for Latin. + +### Accessibility Research +- **Rello, L., & Baeza-Yates, R.** — "Good fonts for dyslexia" (*ASSETS '13*, 2013). No evidence OpenDyslexic improves readability; letter spacing and sans-serif fonts show measurable improvement. +- **Kuster, S. M., et al.** — "Dyslexie font does not benefit reading in children with or without dyslexia" (*Annals of Dyslexia*, 2018). Replication confirming no benefit from specialized dyslexia fonts. +- **Okabe, M., & Ito, K.** — "Color Universal Design (CUD): How to make figures and presentations that are friendly to colorblind people" (2002). The Okabe-Ito palette: 8 colors distinguishable under all common CVD types. +- **Birch, J.** — "Worldwide prevalence of red-green color deficiency" (*J. Optical Society of America A*, 2012). ~8% of males, ~0.5% of females. +- **Harding, G., & Harding, P.** — "Photosensitive epilepsy and image safety" (*Applied Ergonomics*, 2010). Flash frequency and saturated-red area thresholds for seizure risk. +- **WCAG SC 1.4.1** — Use of Color: color must not be the only visual means of conveying information. +- **WCAG SC 1.4.11** — Non-text Contrast: UI components and graphical objects require 3:1 minimum contrast ratio. +- **WCAG SC 1.4.12** — Text Spacing: users must be able to override line-height (1.5×), letter-spacing (0.12em), word-spacing (0.16em), paragraph-spacing (2×) without loss of content or functionality. +- **WCAG SC 2.3.1** — Three Flashes or Below Threshold: no content flashes more than 3 times per second. diff --git a/DESIGN_SYSTEM.md b/DESIGN_SYSTEM.md new file mode 100644 index 0000000..1088800 --- /dev/null +++ b/DESIGN_SYSTEM.md @@ -0,0 +1,1028 @@ +# Agentic Coding — Design System + +You are implementing UI for a monochrome-first design system. Color exists only for semantic meaning. All surfaces, borders, and text are achromatic by default. + +## Constraints + +1. **Achromatic base** — Use pure gray for all surfaces, borders, and text. Do NOT use tinted neutrals. Instead, use the neutral palette (C:0.000) for all non-semantic elements. +2. **Color = meaning** — Apply chromatic color only for semantic callouts, diagrams, status indicators, and data viz. Before using any color, answer: "what does this hue mean here?" If there is no semantic answer, use neutral gray instead. +3. **Equal hue standing** — All 9 chromatic hues have equal weight. Do NOT treat any single hue as "the brand color." Instead, select hue based on semantic meaning (see Color Selection Procedure). +4. **Flat construction** — Do NOT use gradients, shadows, or glows. Instead, use solid fills, clean borders (1px solid), and whitespace for visual hierarchy. Exception: All emoji representations (Noto SVGs via `scripts/fetch-emoji.js`, custom inline SVG recreations, or `` fallbacks) are exempt — emoji visuals must never be modified to conform to this system. +5. **Typographic interaction** — Identify interactive elements by typography and shape. Use underlines + font-weight for links. Use dark/light fills for buttons. Do NOT rely on color to signal interactivity. Instead, use shape, weight, and underlines. +6. **Color budget: 60-30-10** — 60% achromatic surfaces, 30% elevated gray, 10% semantic color. Default to 95/5 for content pages. Reserve 60-30-10 for diagram-heavy pages only. +7. **Curved default, angular accent** — Use rounded forms (squircle containers, Bezier connectors) as the default shape vocabulary. Reserve angular forms (diamonds, chevrons, sharp miters) for high-arousal semantic states (error, warning, code). Do NOT mix angular containers with positive-valence content. Instead, match shape curvature to semantic valence (see Illustration System). +8. **Motion is purposive** — Every animated element must answer: "what does this motion orient, teach, or confirm?" If no answer, use no animation. Do NOT animate for decoration. Reserve looping motion (idle states) for semantic signals only: active authoring, AI processing, data flow, system readiness. Max 2 simultaneous idle loop *classes* per figure (staggered DOM instances of the same animation class count as 1). +9. **Static completeness** — Design the final settled state first. Animation reveals content; it does not define it. Every figure must communicate its full concept in the phase=1 state with no motion. + +--- + +## Color Selection Procedure + +### Step 1: Determine semantic category + +| Hue | Semantic Role | Apply To | +|-----|--------------|----------| +| Error (H:25°) | Danger, critical | Error states, breaking changes, destructive actions | +| Warning (H:70°) | Caution, attention | Warnings, deprecation notices, hallucination risk | +| Success (H:155°) | Validated, complete | Completed states, validation passes, active connections | +| Cyan (H:195°) | System, code | System components, code generation, infrastructure | +| Indigo (H:250°) | Knowledge, data | Documentation, context retrieval, data references | +| Violet (H:285°) | AI transformation | AI processing, transformation steps, synthesis operations | +| Magenta (H:320°) | AI creative | LLM agents, creative processes, prompt engineering | +| Neutral | Human actor / base | Human actors, developer intent — achromatic by design | + +**Removed hues:** Lime (H:110°) aliased to Success — semantically overlapping at 45° gap. +Rose (H:355°) aliased to Neutral — human actors represented achromatic per the base principle. +CSS tokens `--visual-lime` and `--visual-rose` remain as aliases for backward compatibility. + +### Step 2: Select shade by context + +| Context | Light Mode Shade | Dark Mode Shade | +|---------|-----------------|-----------------| +| Semantic text and icons | Pareto-optimal¹ (WCAG AA ≥4.5:1 on white) | 400 (WCAG AA on #0d1117) | +| Subtle tinted backgrounds | 50 | 950 | +| Borders, decorative fills | 100–200 | 700–800 | +| Mid-tone accents | 500 | 500 | +| Darkest text on colored bg | 900 | — | + +### Step 3: Use CSS tokens (not raw hex) + +| Token | Light mode | Dark mode | OKLCH | +|-------|------------|-----------|-------| +| `--visual-error` | #ee0028 | #ec7069 | Pareto-optimal, H:25° | +| `--visual-warning` | #a76900 | #cd8c37 | Gamut-clipped C=0.125, H:70° | +| `--visual-success` | #00894d | #48b475 | C=0.137, H:155° | +| `--visual-cyan` | #008485 | #00b2b2 | Gamut-clipped C=0.095, H:195° | +| `--visual-indigo` | #307ac0 | #53a0ec | C=0.13, H:250° | +| `--visual-violet` | #736cc3 | #938eeb | C=0.13, H:285° | +| `--visual-magenta` | #9d5fab | #c07ecf | C=0.13, H:320° | +| `--visual-neutral` | #666666 | #9b9b9b | Achromatic | +| `--visual-lime` | → `--visual-success` | → `--visual-success` | Alias — removed from spectrum | +| `--visual-rose` | → `--visual-neutral` | → `--visual-neutral` | Alias — removed from spectrum | + +**Chroma normalization:** Categorical hues (indigo, violet, magenta) are capped at C=0.13 to +enforce visual equal-standing. The previous Pareto-optimal approach produced violet/magenta at +C≈0.29 — three times louder than cyan (C=0.095). The 1.37× residual variation is physical gamut +limits at constrained hue angles (cyan, warning). Error retains Pareto-optimal for its semantic +role as a high-arousal danger signal. + +### Step 4: For diagram region fills, use background tokens + +Transparent tints at 10% light / 15% dark: + +```css +--visual-bg-{hue}: color-mix(in srgb, var(--visual-{hue}) 10%, transparent); +/* Dark mode: 15% instead of 10% */ +``` + +All 10 hues (error, warning, lime, success, cyan, indigo, violet, magenta, rose, neutral) have `--visual-bg-*` tokens. + +--- + +## Typography + +### Font Assignment + +| Role | Font | CSS Variable | Weights | +|------|------|-------------|---------| +| Display / headings | Space Grotesk | `--font-display` | 600, 700 | +| Body text | Inter | `--font-body` | 400, 500, 600, 700, 800 | +| Code — default | Monaspace Neon | `--font-mono` | 400, 500, 600, 700 | +| Code — AI voice | Monaspace Argon | `--font-mono-ai` | 400, 500 | +| Code — spec/schema | Monaspace Xenon | `--font-mono-spec` | 400, 500, 600 | +| Code — human note | Monaspace Radon | `--font-mono-human` | 400 | +| Code — keyword/op | Monaspace Krypton | `--font-mono-keyword` | 400, 500 | + +```css +--font-display: 'Space Grotesk', system-ui, sans-serif; +--font-body: 'Inter', system-ui, sans-serif; +--font-mono: 'Monaspace Neon', monospace; +--font-mono-ai: 'Monaspace Argon', monospace; +--font-mono-spec: 'Monaspace Xenon', monospace; +--font-mono-human: 'Monaspace Radon', monospace; +--font-mono-keyword: 'Monaspace Krypton', monospace; +--font-mono-features: 'calt' 1, 'liga' 0; +``` + +Apply `--font-display` to `h1`, `h2`. Apply `--font-body` to body. Monaspace faces share identical metrics — mix freely. + +### OpenType Features + +Apply to all Monaspace containers: + +```css +font-feature-settings: var(--font-mono-features); +``` + +- `calt` ON — Texture healing for even visual density across the monospace grid. +- `liga` OFF — Prevents ambiguous ligatures. Opt in per-context via stylistic sets (`ss01`–`ss10`), never globally. + +### Typographic Voice Selection + +Color encodes *category*. Typeface encodes *speaker*. These axes are orthogonal — a keyword can be Krypton *and* cyan; a comment can be Radon *and* muted gray. + +| Face | Voice | Apply To | +|------|-------|----------| +| **Neon** | System / neutral | Source code, terminal output, config files. Default for all `` and prompt block base text. | +| **Argon** | AI agent | LLM responses, agent reasoning traces, AI-generated explanations, ghost text. | +| **Xenon** | Authoritative / structural | Spec IDs, schema keys, API contracts, system boundary labels, constraint rules. | +| **Radon** | Human / informal | Code comments, developer notes, prompt drafts, TODO markers. | +| **Krypton** | Technical / mechanical | Keywords, operators, CLI flags, file paths in callouts, taxonomy labels. | + +### Voice Application by Content Type + +**Code blocks:** Neon base. Comments in Radon. Keywords in Krypton. AI output in Argon. + +**Prompt blocks:** Five classes, each assigned two voices (max-2 constraint applies per block): + +| Class | Base | Highlight | Highlight tokens | +|-------|------|-----------|-----------------| +| Example | Neon | Krypton | Tool names, file paths, HTTP codes/headers, library names, protocol refs, search queries | +| Template | Neon | Xenon | Placeholders (`$VAR`, `{VAR}`, `[name]`), deliverable filenames, section headers | +| Command | Krypton | Xenon | URLs, file paths, `@element-refs` | +| Response | Argon | Krypton | Code tokens within AI-generated text | +| Constraint | Neon | Xenon | `DO NOT`, `ALWAYS`, `Instead` — rule-enforcement keywords | +| Human | Radon | — | Human-voice text: persona attribution ("You are a [role]"), conversational/polite phrasing, natural-language requests | + +Radon marks any text written in a human voice — persona strings, polite conversational prompts, natural-language requests. It is never used for technical content. Apply as `` within a Neon block. Radon is scarce: one span per block maximum. + +**Spec tables:** Spec IDs in Xenon. Verification methods in Neon. Rationale in Argon. + +**Diagram labels:** System/boundary names in Xenon. Agent labels in Argon. Human actors in Radon. Data flows in Krypton. + +**Inline code:** Default Neon. Override via utility class: ``, ``, etc. Inside `.prompt-example`, bare `` defaults to Krypton — no class needed. Override to Xenon with `` for Template/Constraint highlights. + +### Voice Constraints + +Voice faces: monospace only. Max 2 per block. Radon is scarce — human voice only, never for emphasis or technical content. Fallback: `var(--font-mono)` → `monospace`. + +### Prompt Block Component + +Use `` for all inline prompt illustrations in MDX docs. Do NOT use fenced code blocks for prompt examples — they lose the typographic voice layer. + +```tsx +import PromptExample from '@site/src/components/PromptExample'; +``` + +The component renders a `
` and **automatically colorizes markdown structural punctuation** (`#`, `##`, `-`, `1.`) in cyan (`--visual-cyan`) by wrapping them in ``. No manual `` needed — just use the component. + +**CSS class: `.prompt-example`** (defined in `website/src/css/custom.css`) +- Base font: `--font-mono` (Neon) +- `` children default to `--font-mono-keyword` (Krypton) — correct for Example and Command classes +- Override for Template/Constraint class: add `className="mono-spec"` to placeholder or constraint-keyword `` elements (Xenon) +- Override for Response class: add `className="mono-ai"` to the `
` itself (Argon base) + +```jsx +{/* Example class — Krypton highlights */} + + Use ChunkHound to search for "error patterns" in our codebase. + + +{/* Template class — Xenon placeholders */} + + Analyze $FAILURE_DESCRIPTION and propose a fix. + + +{/* Constraint class — Xenon enforcement keywords */} + + Do NOT store passwords in plain text. + Instead, use bcrypt with 10 salt rounds. + + +{/* Human class — Radon for human-voice text */} + + Could you help me write a function to validate email addresses?
+ Thanks in advance!
+
+ +{/* Response class — Argon base (pass className via wrapper if needed) */} +
+ I found 3 locations where raw errors leak: auth.ts:47, users.ts:112… +
+``` + +**`.md-punct`** — Applied automatically by `` to leading markdown structural characters (`#`, `##`, `-`, `1.`). Color: `var(--visual-cyan)`. Do not apply manually. + +For line breaks within a prompt block: use `
` only for **intentional structural breaks** (one requirement per line in Exact/Command prompts). Prose-style prompts (Exploration) wrap freely — no `
`. + +--- + +## Design Tokens + +### Token Architecture + +| Tier | Purpose | Naming | Example | +|------|---------|--------|---------| +| Primitive | Raw palette values | `{hue}-{shade}` | `cyan-600`, `neutral-200` | +| Semantic | UI surfaces, text, borders, illustration | `--{category}-{role}` | `--surface-page`, `--visual-cyan` | +| Component | Per-component overrides | (not in this document) | — | + +### Surface, Text & Border Tokens + +| Token | Light Mode | Dark Mode | +|-------|-----------|-----------| +| `--surface-page` | #ffffff | #0d1117 | +| `--surface-raised` | #f5f5f5 (neutral-50) | #161b22 | +| `--surface-muted` | #e8e8e8 (neutral-100) | #3d3d3d (neutral-800) | +| `--text-heading` | #2b2b2b (neutral-900) | #e8e8e8 (neutral-100) | +| `--text-body` | #505050 (neutral-700) | #d4d4d4 (neutral-200) | +| `--text-muted` | #808080 (neutral-500) | #9b9b9b (neutral-400) | +| `--border-subtle` | #d4d4d4 (neutral-200) | #3d3d3d (neutral-800) | +| `--border-default` | #b7b7b7 (neutral-300) | #505050 (neutral-700) | + +--- + +## Spatial System + +Base unit: 8px. All spacing and line-heights snap to 8px grid multiples. + +### Spacing Scale + +| Token | Value | +|-------|-------| +| `--space-0` | 0px | +| `--space-px` | 1px | +| `--space-0h` | 4px | +| `--space-1` | 8px | +| `--space-2` | 16px | +| `--space-3` | 24px | +| `--space-4` | 32px | +| `--space-5` | 48px | +| `--space-6` | 64px | +| `--space-7` | 80px | +| `--space-8` | 96px | +| `--space-9` | 128px | +| `--space-10` | 160px | + +| Purpose | Steps | Values | +|---------|-------|--------| +| Component padding | step 2–3 | 16–24px | +| Section gap | step 5–6 | 48–64px | +| Page margin | step 7–8 | 80–96px | + +Do NOT use arbitrary pixel values for spacing. Instead, use `--space-*` tokens. + +### Type Scale + +Minor Third (1.200), base 16px. Sizes integer-rounded. Line-heights 8px-snapped. + +| Token | Size | Line-Height | Role | +|-------|------|-------------|------| +| `--text-xs` | 11px | 24px (`--lh-sm`) | Fine print, captions | +| `--text-sm` | 13px | 24px (`--lh-sm`) | Secondary, metadata | +| `--text-base` | 16px | 24px (`--lh-sm`) | Body text | +| `--text-lg` | 19px | 32px (`--lh-lg`) | Lead paragraphs | +| `--text-xl` | 23px | 32px (`--lh-lg`) | h4 subheadings | +| `--text-2xl` | 28px | 40px (`--lh-2xl`) | h3 section headings | +| `--text-3xl` | 33px | 40px (`--lh-2xl`) | h2 major headings | +| `--text-4xl` | 40px | 48px (`--lh-3xl`) | h1 page titles | + +Do NOT use computed `line-height: 1.5`. Instead, assign `--lh-*` tokens. + +Apply `max-width: 66ch` to text containers. + +### Border Radius + +| Token | Value | +|-------|-------| +| `--radius-none` | 0px | +| `--radius-sm` | 4px | +| `--radius-md` | 8px | +| `--radius-lg` | 12px | +| `--radius-xl` | 16px | +| `--radius-2xl` | 24px | +| `--radius-full` | 9999px | + +| Context | Adjustment | Radius | +|---------|-----------|--------| +| Error/danger | -50% | 2px | +| Warning | -25% | 2px | +| Success | +25% | 4px | +| Neutral/info | default | 3px | +| Avatar | circle | 50% | +| Input fields | -25% | 2px | + +Do NOT apply high curvature (`--radius-full`) to error or warning elements. Instead, use `--radius-sm` or lower. + +Use `--radius-md` (8px) for cards and containers. Use `--radius-sm` (4px) for inputs and badges. + +Do NOT apply border-radius to accent-bordered elements (3–4px `border-left` or `border-top` callouts, blockquotes, step indicators). Radius rounds the accent stroke's endpoints into decoration that communicates nothing. Instead, use `border-radius: 0` and let the accent border terminate with sharp edges. Exception: cards with a full surrounding `border` (all four sides) may use `--radius-md` even when one side carries a thicker accent override. + +### Line Weight + +| Token | Thickness | Light Color | Dark Color | +|-------|-----------|-------------|------------| +| `--border-subtle` | 1px | #d4d4d4 (neutral-200) | #3d3d3d (neutral-800) | +| `--border-default` | 1px | #b7b7b7 (neutral-300) | #505050 (neutral-700) | +| `--border-emphasis` | 1px | #808080 (neutral-500) | #808080 (neutral-500) | +| `--border-strong` | 2px | #505050 (neutral-700) | #b7b7b7 (neutral-300) | +| `--border-accent` | 3px | semantic color | semantic color | + +Do NOT exceed 4 visible structural borders per viewport section. Instead, use spacing or surface tone for grouping. + +Reserve `--border-accent` (3px) for semantic callouts only. + +### Target Sizes + +| Token | Height | H-Padding | Use | +|-------|--------|-----------|-----| +| `--target-sm` | 32px | 16px | Tertiary actions, inline buttons, tags | +| `--target-md` | 40px | 24px | Secondary actions, form inputs | +| `--target-lg` | 48px | 32px | Primary actions, main CTAs | +| `--target-xl` | 56px | 48px | Hero CTAs, prominent actions | + +All interactive elements must be at least 24×24 CSS px (WCAG 2.2 AA). Primary actions use `--target-lg` (48px) to meet WCAG AAA. + +Do NOT place adjacent interactive elements closer than 8px apart. + +### Proximity Grouping + +| Relationship | Spacing | +|-------------|---------| +| Tightly related | 8px (`--space-1`) | +| Within-group | 16px (`--space-2`) | +| Between-group | 48px (`--space-5`) | +| Between-section | 64–96px (`--space-6` – `--space-8`) | + +Within-group spacing must be less than half the between-group spacing. + +Do NOT use equal spacing within and between groups. Instead, ensure at least a 2× step difference. + +--- + +## Content Composition + +### Page Reading Flow + +Content follows a single-column vertical flow. Block elements (figures, code blocks, admonitions) interrupt the prose column and span its full width. + +| Transition | Spacing | Token | +|-----------|---------|-------| +| Paragraph → paragraph | 16px | `--space-2` | +| Paragraph → block element | 32px | `--space-4` | +| Block element → paragraph | 32px | `--space-4` | +| Block element → block element | 24px | `--space-3` | +| Section heading → first element | 16px | `--space-2` | +| Last element → section heading | 64px | `--space-6` | + +Apply `max-width: 66ch` to prose containers. Block elements (figures, code blocks) may extend to the content column's full width but do NOT exceed it. + +Do NOT place a figure before its first textual reference. Instead, the figure appears immediately after the paragraph that introduces it. + +### Figure Integration + +Use semantic `
` and `
` for all visual block elements — diagrams, screenshots, illustrations, and annotated code. + +```html +
+ +
Figure 4.3 — Context window token allocation across three agent turns.
+
+``` + +| Property | Value | +|----------|-------| +| Caption font | `--text-sm` (13px) | +| Caption color | `--text-muted` | +| Caption spacing | `--space-1` above caption | +| Figure margin | `--space-4` top and bottom | +| Numbering | Optional, section-based: `Figure {section}.{n} —` | + +| Figure Type | Width | Alignment | +|------------|-------|-----------| +| Diagram (SVG) | 100% of content column | Centered via `margin-inline: auto` | +| Screenshot | Intrinsic, max 100% | Centered | +| Inline icon pair | Intrinsic | Inline with text | + +Do NOT use figures without captions. Every `
` must contain a `
` that describes the content. + +Do NOT use `` directly for diagrams or illustrations. Instead, wrap in `
` with a descriptive caption. + +### Progressive Disclosure + +| Pattern | Primitive | Use When | +|---------|-----------|----------| +| Collapsible depth | `
` / `` | Optional deep-dive, implementation detail, proof, or derivation | +| Parallel alternatives | `` | Multiple equivalent approaches (languages, frameworks, OS) | +| Semantic alert | Admonition (`:::type`) | Contextual warnings, tips, or prerequisites that interrupt flow | + +| Question | If Yes → | +|----------|----------| +| Is this content required to understand the main argument? | Keep inline — do NOT hide it | +| Does the reader choose one of N equivalent paths? | `` | +| Is this a tangent that only some readers need? | `
` | +| Does this interrupt flow with a warning, tip, or prerequisite? | Admonition | + +Do NOT hide critical content behind `
`. Instead, keep essential information in the primary prose flow. + +Do NOT nest disclosure patterns. A `
` inside a `` panel (or vice versa) adds cognitive overhead. Instead, flatten the structure. + +### Content Block Hierarchy + +Content occupies three tiers; tiers 1–2 must be self-sufficient. + +| Tier | Elements | Role | +|------|----------|------| +| 1 — Primary | Prose paragraphs, headings, inline code | Core argument and explanation | +| 2 — Secondary | Figures, code blocks, tables | Evidence, demonstration, specification | +| 3 — Tertiary | Admonitions, `
`, footnotes | Supplementary context, caveats, deep-dives | + +Do NOT place essential information exclusively in tier 3. Instead, state the key point in tier 1 prose, then elaborate in tier 3 if needed. + +Do NOT exceed 3 consecutive block elements (tier 2 or 3) without intervening prose. Instead, add a bridging sentence that connects the blocks to the argument. + +--- + +## UI Patterns + +### Buttons + +| Variant | Background | Border | Text | Use | +|---------|-----------|--------|------|-----| +| Primary CTA | cyan-600 (light) / cyan-400 (dark) | none | white | Main conversion action (1 per page max) | +| Primary | #2b2b2b (light) / #e8e8e8 (dark) | none | white (light) / dark (dark) | Standard primary actions | +| Outline | transparent | dark | dark | Secondary actions | +| Ghost | transparent | none | underlined dark | Tertiary / inline actions | + +Chromatic color is permitted on **Primary CTA only** — one per page, using `var(--visual-cyan)` as background. This is the sole exception to achromatic buttons. All other button variants remain neutral. Do NOT introduce additional hue variants. Hover: darken fill (shift to cyan-700/cyan-300). Do NOT change hue on hover. + +White text on cyan-600 (#007576) achieves 5.38:1 contrast ratio (WCAG AA). White text on cyan-400 (#00b2b2) in dark mode achieves sufficient contrast on the lighter teal fill. + +### Badges + +| Mode | Background | Text | Border | +|------|-----------|------|--------| +| Light | shade-50 | shade-600 | 1px solid shade-600 | +| Dark | shade-600 | white | none | + +Example (cyan light): `background: #d4fffe; color: #007576; border: 1px solid #007576;` +Example (cyan dark): `background: #007576; color: #fff;` + +### Cards + +- Border: `1px solid var(--border-default)`, `border-radius: var(--radius-md)` +- Background: `var(--surface-raised)` or `var(--surface-page)` +- Hover: shift border to neutral-400. Do NOT add color on hover. Instead, increase border contrast only. + +### Callout Borders + +- 3px left border in semantic color + colored label text. Body text stays neutral. +- Example: `border-left: 3px solid #1369b0;` with `TIP` +- Do NOT apply border-radius to callout containers. Instead, use `border-radius: 0`. The accent border's sharp endpoints reinforce its directional intent. + +### Interactive States + +| State | Treatment | +|-------|-----------| +| Rest | neutral border | +| Hover | border lightens (neutral-700 → neutral-400) | +| Focus | darker border (contrast shift) | +| Active | darker fill or inverted contrast | + +All state changes use contrast/weight shifts. Do NOT introduce hue changes on interaction states. Instead, shift lightness within the neutral palette. + +### Inputs + +- Border: neutral. Focus: darker border. +- Do NOT use colored focus rings. Instead, increase border contrast on focus. + +### Admonitions + +Docusaurus admonition types map to the semantic hue palette and use the Callout Borders pattern (3px left border + colored label). + +| Admonition | Hue | Token | Label Color | +|-----------|-----|-------|-------------| +| `:::tip` | Cyan (H:195°) | `--visual-cyan` | `var(--visual-cyan)` | +| `:::info` | Indigo (H:250°) | `--visual-indigo` | `var(--visual-indigo)` | +| `:::note` | Neutral | `--visual-neutral` | `var(--visual-neutral)` | +| `:::caution` | Warning (H:70°) | `--visual-warning` | `var(--visual-warning)` | +| `:::danger` | Error (H:25°) | `--visual-error` | `var(--visual-error)` | + +Body text inside admonitions stays `--text-body`. Only the label and left border carry the semantic color. + +Do NOT apply background tints to admonitions. Instead, use `--surface-raised` for the container background, matching the flat construction constraint. + +--- + +## Illustration System + +Curved forms (Smooth Circuit) are default. Angular forms (Terminal Geometry) reserved for high-arousal states. See `ILLUSTRATION_GUIDE.md`. + +### Actor Primitives + +All actor primitives share a bounding-box grid for visual equal-standing. + +#### Bounding Box Grid + +| Primitive | Emoji Ref | Bounding Box | Semantic Color | +|-----------|-----------|--------------|----------------| +| `OperatorNode` | 🧑‍💻 | 40×40 (primary) / 32×32 (worker) | Neutral (`--visual-neutral`) | +| `AgentNode` | 🤖 | 40×40 (primary) / 32×32 (worker) | Original emoji palette (hardcoded) | + +All coordinates in `ActorNodes.tsx` are annotated: `// Computed via scripts/compute-actor-coords.js`. + +#### Size Encoding + +Hierarchy is expressed through **size only**, never through color. + +| Role | BB Size | Use | +|------|---------|-----| +| Primary / orchestrator | 40×40 | Top of hierarchy, one per level | +| Worker / delegate | 32×32 | Bottom of hierarchy, multiple parallel | + +Do NOT distinguish orchestrator from worker agents by color. Use 40→32 size differential only. +Do NOT place primary and worker actors in the same horizontal row without applying the size step. + +#### Construction + +**OperatorNode** — Smooth Circuit head, Terminal Geometry legs: +- Head circle: `r = BB × 0.15`. Fill `--visual-bg-neutral`, stroke `--visual-neutral` 2px. +- Shoulder U-path: Smooth Circuit (`stroke-linecap="round"` `stroke-linejoin="round"`). Span = BB × 0.90, corner radius = BB × 0.10. +- Legs: two lines (Terminal Geometry, `stroke-linecap="square"`) spreading to BB base. + +**AgentNode** — Original Google Noto 🤖 emoji (`/img/emoji/u1f916.svg`): +- Delegates to `NotoEmoji` component with `codepoint="1f916"`. +- Gradient ID isolation is automatic; SVG `` renders as a separate document. + +**NotoEmoji** — Generic wrapper for any Noto emoji: +- `` renders `/img/emoji/u1f4a1.svg`. +- Add new emojis via `node scripts/fetch-emoji.js ` — caches raw in `scripts/.noto-cache/`, writes cleaned SVG to `website/static/img/emoji/u{cp}.svg`. + +#### Animated Emoji: `NotoEmoji` vs Hand-Coded Components + +Use **`NotoEmoji`** (via ``) for static emoji nodes. The SVG renders as an opaque sub-document — individual paths inside it are not accessible to the parent SVG's CSS or SMIL, so per-path animation is impossible. + +Use a **hand-coded component** (e.g. `AuthorWaveNode`) when the emoji needs animation. Inline the paths directly into the parent SVG so CSS transforms and `className` can target individual elements. Obtain source paths from the Noto SVG (via `scripts/fetch-emoji.js`); simplify fills to flat colors (no gradients) and omit any paths not visible at the target size. + +#### Emoji Node Rendering + +ALL `NotoEmoji` instances render **bare** — no background ``, no filled container, no exceptions. + +- The emoji IS the node. Its bounding box IS the hit area. +- Do NOT wrap `NotoEmoji` in a colored ``, ``, or any other filled shape. +- Do NOT add squircle or pill containers behind emoji nodes — those are structural region shapes (see Shape Selection Procedure). + +#### Ghost Placeholders + +Ghost placeholders provide spatial mass before a node enters, preserving layout balance (per the "every act-state must be visually complete" rule). + +- **Construction:** dashed-stroke rect (`strokeDasharray="3 4"`, `strokeWidth={1}`), semantic background fill (`--visual-bg-{hue}`), semantic stroke (`--visual-{hue}`). +- **Size:** matches the emoji's bounding box (or the head portion for actor primitives with body geometry). +- **Lifecycle:** `ghostShown` → visible while node is pending; `ghostHidden` → `opacity: 0` when node enters. +- The ghost is the ONLY rect. The settled node is a bare emoji with no background shape. + +#### Communication Medium + +> **Note:** `PromptCard` was renamed to `PromptIcon`. `TravelingPromptCard` is its animated variant — used for delegation-flow motion along an edge path; not a separate primitive. + +Every edge connecting actor nodes carries either: +- A `PromptIcon` artifact (36×20 centered prompt card, used in delegation flows), or +- A plain Bezier arc with arrowhead (for return flow / result propagation). + +Do NOT use other metaphors (looms, punch cards, pipes) for prompt artifacts. + +--- + +### Shape Vocabulary + +| Family | Forms | Superellipse n | Default For | +|--------|-------|----------------|-------------| +| Smooth Circuit | Squircle containers, Bezier connectors, circular endpoints, round caps | n = 3–4 (squircle), n = 2 (circle) | Positive-valence: success, AI, system, knowledge, progress | +| Terminal Geometry | Diamond accents, chevron arrows, angular miters, bracket syntax | n = 1–1.5 (diamond), 45° angles | High-arousal: error, warning, code structure, human action | + +Do NOT use strict-rectangle-only construction (90° routing, sharp corners on all containers). Instead, use squircle containers (n=3–4) even for box-like elements. + +Do NOT apply angular containers (diamonds, sharp-cornered shapes) to success, AI, or system content. Instead, use squircle or circular containers for positive-valence elements. + +### Shape Selection Procedure + +Smooth Circuit for all hues except Error and Warning (Terminal Geometry). + +#### Step 1: Select container shape + +| Content Type | Container | SVG Implementation | +|-------------|-----------|-------------------| +| System node / module | Squircle | `` (40px box) or superellipse `` | +| Agent / AI process region | Circle or pill | `` or `` | +| Data / knowledge | Rounded rect | `` | +| Human actor region | Circle or squircle | `` or `` | +| Generic container | Squircle | `` to `` | +| Code / terminal | Diamond or sharp rect | `` (4-point) or `` | +| Error state | Sharp rect or diamond | `` or `` | +| Warning state | Triangle or diamond | `` (3-point up) | + +> **Note:** These containers describe **structural regions and grouping shapes** — not per-node backgrounds. Emoji icon nodes always render bare (see Actor Primitives → Emoji Node Rendering). `AgentNode` is a bare 🤖 emoji, not a circle container; "Agent / AI process region" refers to a panel or zone grouping multiple agent nodes. + +#### Step 3: Choose connector style + +| Connection Type | Connector | SVG Implementation | +|----------------|-----------|-------------------| +| Data flow (happy path) | Bezier curve | `` with `stroke-linecap="round"` | +| Error / rejection path | Angular polyline | `` with `stroke-linecap="square"` | +| Bidirectional | Bezier with markers at both ends | `marker-start` + `marker-end` | +| Optional / dashed | Bezier with dash array | `stroke-dasharray="6 4"` | + +### Stroke Weight Scale + +| Token | Value | Purpose | +|-------|-------|---------| +| `--stroke-fine` | 1px | Grid lines, hairlines, decorative rules | +| `--stroke-light` | 1.5px | Secondary connectors, annotations | +| `--stroke-default` | 2px | Standard connectors, internal details | +| `--stroke-medium` | 2.5px | Container outlines, primary shapes | +| `--stroke-heavy` | 3px | Emphasized elements, primary flow arrows | +| `--stroke-accent` | 4px | Semantic accent strokes (1 per diagram max) | + +Do NOT exceed 4px stroke width. Instead, use fill-weight or size increase for emphasis. + +Do NOT use more than 3 distinct stroke weights in a single diagram. + +### Arrow Markers + +| Type | Marker Size | refX | Polygon Points | +|------|------------|------|---------------| +| Standard | 6×6 | 5 | `0 0, 6 3, 0 6` | +| Large | 8×8 | 6 | `0 0, 8 4, 0 8` | + +Use Standard (6px) by default. Use Large (8px) only in narrow viewBox diagrams (< 400px wide). Arrow fill inherits from stroke color. + +### Construction Rules + +1. **Grid snap** — All anchor points snap to the 8px spatial grid (`--space-*` tokens). Minimum shape dimension = 8px. +2. **Preferred angles** — 15°, 30°, 45°, 60°, 75°, 90°. Other angles require justification. +3. **Proportional corner radius** — Scale `rx` proportionally: `rx = height × 0.25` (range 8–16px). Do NOT use a fixed `rx` regardless of box size. +4. **Minimum gap** — 8px (`--space-1`) between shapes. 16px (`--space-2`) between shape and label. +5. **Label placement** — Labels go directly adjacent to elements (spatial contiguity). Do NOT use a separate legend when inline labels fit. +6. **Coherence** — Every shape serves a communicative purpose. Do NOT add decorative shapes. + +### Flat Construction Enforcement + +Do NOT use SVG filters (``, `feDropShadow`, `feGaussianBlur`). Instead, use stroke-weight hierarchy and surface tone for emphasis. + +Do NOT use SVG `` or `` for fills. Instead, use solid `--visual-bg-*` tint tokens. + +Do NOT use `box-shadow` on diagram containers. Instead, use `border` with `--border-default` or semantic color. + +--- + +## Icon & Diagram Geometry + +### Icon Tiers + +| Tier | Canvas / viewBox | Style | Use | +|------|-----------------|-------|-----| +| UI icon | `0 0 24 24` | Outline stroke (`currentColor`) | Inline text, buttons, nav, phase indicators | +| Illustration icon | `0 0 128 128` | Flat filled (Noto emoji style) | Bullet icons, feature cards, hero callouts | + +Both tiers share `--icon-*` size tokens for rendered width/height. The viewBox is fixed per tier; scaling is via `width`/`height` attributes. + +### Icon Sizes + +| Token | Value | Purpose | +|-------|-------|---------| +| `--icon-sm` | 16px | Inline text icons | +| `--icon-md` | 24px | Default UI icons | +| `--icon-lg` | 32px | Card/callout icons | +| `--icon-xl` | 48px | Diagram node icons | +| `--icon-2xl` | 64px | Hero/feature icons | + +Do NOT create icons at arbitrary sizes. Instead, use `--icon-*` tokens. + +### UI Icon Construction + +| Property | Value | +|----------|-------| +| `fill` | `none` (outline style) | +| `stroke` | `currentColor` | +| `stroke-width` | `2` (at 24px canvas) | +| `stroke-linecap` | `round` (Smooth Circuit) or `square` (Terminal Geometry) | +| `stroke-linejoin` | `round` (Smooth Circuit) or `miter` (Terminal Geometry) | + +### Illustration Icon Construction + +Illustration icons follow a Noto Color Emoji–inspired flat construction standard. All 128×128 viewBox icons must comply. This section applies to *custom* illustration icons — emoji representations (fetched, inlined, or recreated from Noto) are exempt per constraint #4. + +#### Core Principles + +1. **Flat fills only** — solid color shapes. No tonal gradation within the same structural part. +2. **Layered silhouette** — 3–5 overlapping filled shapes that read as a bold silhouette at 32px. +3. **Geometric simplicity** — minimize path points; prefer arcs and clean curves over organic detail. +4. **Achromatic structure + chromatic accent** — neutral fills for structure, `currentColor` for the one semantic layer. + +#### Fill Budget + +Maximum 4 distinct fill values per icon: + +| Slot | Fill | Neutral shade | Role | +|------|------|---------------|------| +| Body | hardcoded hex | neutral-600 (`#666666`) | Primary structural mass | +| Detail | hardcoded hex | neutral-900 (`#2b2b2b`) | Dark accents, internal features | +| Secondary | hardcoded hex | neutral-300 (`#b7b7b7`) | Secondary body, bezels, minor marks | +| Accent | `currentColor` | inherited from parent | Semantic tint (solid or `opacity="0.15"` wash) | + +Slot usage: Body + Accent are required. Detail and Secondary are optional. Total ≤ 4. + +#### Accent Layer + +- Every illustration icon MUST have ≥1 element with `fill="currentColor"`. +- Two permitted opacity values: `1` (solid accent) and `0.15` (tinted wash). No other opacities. +- Do NOT hardcode semantic hex colors (e.g., `#ad3735`). Use `currentColor` so the parent assigns meaning via `--visual-*`. +- Structural fills must be opaque. A path that serves as backdrop for other visible elements (e.g., a clock face behind tick marks) must use a neutral hex fill, not a `currentColor` wash. Reserve `opacity="0.15"` washes for overlay accents that layer on top of opaque structure (e.g., a tinted face layered over a `#666666` body). + +#### Banned Techniques + +- Highlight fills (lighter neutral on darker to simulate reflection) +- Shadow ridge fills (darker neutral alongside structural neutral to simulate depth) +- Multiple tonal layers on the same part (e.g., neutral-100 + -300 + -500 + -600 on one object) +- SVG filters, gradients, or opacity shading on neutral fills +- neutral-50 (`#f5f5f5`), neutral-100 (`#e8e8e8`), neutral-400 (`#9b9b9b`), neutral-500 (`#808080`) as fill colors — these exist only for 3D simulation + +### Diagram Sizing + +| Diagram Type | ViewBox Width | Typical Height | +|-------------|--------------|---------------| +| Inline icon pair | 100–200 | 48–64px | +| Flow diagram | 400–600 | 160–240px | +| System diagram | 500–1000 | 300–500px | +| Comparison (side-by-side) | 600–1000 | 200–400px | +| Figure caption | Same as parent figure | `--text-sm` line-height (24px) | + +All diagram containers use `width: 100%` with SVG `viewBox` controlling aspect ratio. Do NOT set fixed pixel widths on diagram containers. + +### Diagram Accessibility + +Every SVG diagram requires: + +1. `role="img"` on the `` element +2. `aria-label` describing the diagram's content and relationships +3. Semantic color paired with a non-color indicator (shape difference, label text, or pattern) + +Do NOT rely on color alone to distinguish diagram elements. Instead, combine color + shape + label. + +--- + +## Motion System + +Generated from `ANIMATION_GUIDE.md` Phase 2 (agent-executed math). Brand profile: productive style, medium arousal, medium pleasure. Do NOT override computed values. Re-run `ANIMATION_GUIDE.md` scripts if brand PAD profile changes. + +### Brand Motion Profile + +| PAD Axis | Level | Motion consequence | +|----------|-------|--------------------| +| Pleasure | Medium | Productive curves; slight warmth on diagram reveals | +| Arousal | Low–Medium | Moderate duration (150–240ms); 80ms stagger | +| Dominance | Medium | Ease-out settle; no spring bounce on standard UI | + +**Motion style:** Productive. Expressive curves reserved for actor diagram reveals only — never for UI chrome, buttons, or tables. + +### Motion Tokens + +```css +/* Duration */ +--duration-instant: 70ms; /* toggle, checkbox — at perception floor */ +--duration-fast: 110ms; /* opacity/color, badge update */ +--duration-subtle: 150ms; /* node entrance, icon swap */ +--duration-moderate: 240ms; /* connector draw, widget reveal */ +--duration-deliberate: 400ms; /* large panel, full-section reveal */ +--duration-ambient: 700ms; /* background overlay — not user-triggered */ + +/* Exit variants (×0.75 per NNGroup asymmetry rule) */ +--duration-fast-exit: 80ms; +--duration-subtle-exit: 110ms; +--duration-moderate-exit: 180ms; +--duration-deliberate-exit: 300ms; + +/* Easing — productive style */ +--ease-enter: cubic-bezier(0.00, 0.00, 0.38, 0.9); /* entrance: decelerate to rest */ +--ease-exit: cubic-bezier(0.20, 0.00, 1.00, 0.9); /* exit: accelerate away */ +--ease-standard: cubic-bezier(0.20, 0.00, 0.38, 0.9); /* reposition within viewport */ +--ease-linear: linear; /* spinners, progress bars only */ + +/* Stagger */ +--motion-stagger-sm: 60ms; /* 5–8 items */ +--motion-stagger-md: 80ms; /* 3–4 items */ +--motion-stagger-lg: 100ms; /* 2 items */ + +/* Reveal offsets */ +--motion-reveal-y-scroll: 12px; /* scroll-reveal: elements rise into viewport */ +--motion-reveal-y-load: -8px; /* page-load: elements settle downward */ +--motion-reveal-scale: 0.96; /* modal/dialog only — never for lists */ +``` + +Do NOT use the CSS `ease` keyword. Use `--ease-*` tokens exclusively. +Do NOT animate `width`, `height`, `left`, `top`, `margin`, or `padding`. Use `opacity` and `transform` only. + +### Figure Animation Grammar + +Five archetypes. Each has a fixed animation grammar. Do NOT mix grammars. + +| Archetype | Trigger | Acts | Entrance | Idle | Budget | +|-----------|---------|------|----------|------|--------| +| **Actor diagram** | scroll phase 0→1 | 4–6 phase-gated | `fadeIn + translateY(12px)` per node at `--duration-subtle`; connectors draw via `stroke-dashoffset` at `--duration-moderate` | cursor-blink / status-pulse / ready-breathe per act | ≤1000ms story | +| **Data viz** | scroll phase 0→1 | 2–3 | Containers simultaneous; connectors draw last | flow-drift (optional) | ≤600ms | +| **Interactive widget** | scroll reveal (IO) | 1 | Unit `fadeIn + translateY(12px)` at `--duration-moderate` | none — user-driven | ≤300ms | +| **Data table** | scroll reveal (IO) | 1 | Row stagger at `--motion-stagger-sm` (60ms/row) | `ping-once` on badges at row entry | ≤500ms | +| **Sticky narrative** | chapter ID | N | Chapter transition at `--duration-subtle`; exit at `--duration-subtle-exit` | per-chapter idle | chapter-driven | + +**Entrance keyframe (shared by all archetypes):** + +```css +@keyframes actEnter { + from { opacity: 0; transform: translateY(var(--motion-reveal-y-scroll)); } + to { opacity: 1; transform: translateY(0); } +} +/* apply: animation: actEnter --duration-subtle --ease-enter both; */ +``` + +**Connector draw-on:** + +```css +/* set stroke-dasharray = stroke-dashoffset = el.getTotalLength() via JS on mount */ +@keyframes drawPath { to { stroke-dashoffset: 0; } } +/* apply: animation: drawPath --duration-moderate --ease-enter both; */ +``` + +### Act System + +An **act** is a discrete visual state of a figure. Acts advance monotonically as scroll phase increases — never reverse. + +``` +phase 0→1 (from ScrollDrivenFigure context, or chapter ID from NarrativeFigure) + │ + ▼ +useActs(actDefs, phase) → { wasReached(id), isCurrentAct(id) } + │ + ├── wasReached(id) → element visible, settled appearance + ├── isCurrentAct(id) → apply idle class + └── !wasReached(id) → opacity: 0; pointer-events: none +``` + +Each `ActDef`: `{ id: string, threshold: number }` where threshold is 0–1 (phase-driven) or a chapter ID string (narrative-driven). + +**Do NOT** put act logic in CSS `animation-delay` chains. Use `useActs` + conditional class names. CSS modules define entrance keyframes; the hook controls when they fire. + +**Every act-state must be a visually complete, balanced composition.** Elements that arrive in later acts must have placeholder mass (ghost geometry, neutral fill) in earlier acts to preserve spatial balance. A diagram that looks broken at any act boundary violates this rule. + +### Idle Micro-animation Vocabulary + +All idle classes defined once in `custom.css`. Do NOT define idle keyframes per-component. + +| Class | Motion | Loop | Semantic use | +|-------|--------|------|-------------| +| `.idle-cursor-blink` | `opacity 1→0→1` step-end | 1000ms | Active authoring (prompt artifact during composing act) | +| `.idle-status-pulse` | `opacity 1→0.5→1` ease-in-out | 2000ms | AI processing; connector during transit | +| `.idle-flow-drift` | `translateX 0→2px→0` ease-in-out | 3000ms | Data moving through a channel | +| `.idle-ready-breathe` | `scale 1→1.02→1` ease-in-out | 4000ms | Agent idle; system ready | +| `.ping-once` | `scale 1→1.4; opacity 1→0` ease-out | 400ms, 1× | One-shot attention on act entry | +| `.idle-arm-rock` | `rotate 0→-4°→0` ease-in-out | 5000ms | Human authoring; developer working | +| `.idle-palm-wave` | `rotate 0→±18°→0` ease-in-out | 5000ms | Greeting; interaction acknowledgement | +| `.idle-gear-spin` | `rotate 0→360°` linear | 2500ms | Mechanical processing; LLM inference engine | + +Rules: +- Max **2 idle loop classes** simultaneously per figure. Staggered instances of the same class on multiple DOM elements count as 1. `cursor-blink` and `status-pulse` never together. +- `ping-once` is exempt (transient; `animation-iteration-count: 1`). +- Remove idle classes via `useActs` when the act advances — do NOT let them run in settled state. + +### Stagger Order Rules + +| Content | Order | +|---------|-------| +| Actor diagram nodes | Data-flow order (source → sink) | +| Connector drawing | After both connected nodes are settled | +| Labels / legends | Always last, after the elements they label | +| Data table rows | Top-to-bottom (reading order) | +| Parallel equal-weight grid | Simultaneous — stagger implies false hierarchy | +| Form fields | Top-to-bottom (completion order) | + +### Reduced-Motion Contract + +`ScrollDrivenFigure` enforces the entire contract at the wrapper level. Diagram components do NOT check `prefers-reduced-motion` directly. + +| Condition | Behavior | +|-----------|----------| +| `prefers-reduced-motion: reduce` | `ScrollDrivenFigure` sets `phase=1` on mount; all acts fire instantly; diagrams render in final settled state; idle loop classes removed | +| No JavaScript | Diagrams render in final settled state (SSR default) | +| No CSS scroll-timeline | `IntersectionObserver` fires `phase=1` on first intersection; entrance animations play time-based (correct degradation) | + +```css +@media (prefers-reduced-motion: reduce) { + .idle-cursor-blink, .idle-status-pulse, + .idle-flow-drift, .idle-ready-breathe, .ping-once, + .idle-arm-rock, .idle-palm-wave, .idle-gear-spin { + animation: none !important; + } +} +``` + +Do NOT use `animation-duration: 0` — use `0.01ms`. Below 40ms, browsers may not fire `animationend` reliably. + +--- + +## Dark Mode + +| Property | Value | +|----------|-------| +| Page background | #0d1117 | +| Surface/cards | #161b22 | +| Muted surface | #3d3d3d (neutral-800) | +| Body text | #cdd6d6 (neutral-200) | +| Heading text | #e2eae9 (neutral-100) | +| Semantic colors | shade-400 (not shade-600) | +| Bg tint opacity | 15% (not 10%) | + +Do NOT use pure #000000 for backgrounds. Instead, use #0d1117. +Do NOT use pure #ffffff for text. Instead, use neutral-100 (#e2eae9) or neutral-200 (#cdd6d6). + +All `--visual-*` tokens shift from shade-600 to shade-400 in dark mode. + +--- + +## Accessibility + +- **WCAG AA contrast:** shade-600 on white ≥ 4.5:1. shade-400 on #0d1117 ≥ 4.5:1. +- **Redundant encoding:** Every color signal must pair with a non-color indicator (icon, text label, pattern, or underline). Do NOT convey information through color alone. Instead, always add icon + text label alongside color. +- **Error example:** red border + icon + "Error:" text label. Success: green border + icon + "Success:" text. +- **Links:** Must have underline or equivalent non-color indicator. +- **Colorblind-safe:** Success uses teal-green (H:155°), distinguishable from error red under protanopia/deuteranopia. + +--- + +## Brand Identity Assets + +### Logo Mark + +- `` monochrome glyph. Dark on light backgrounds, light on dark backgrounds. +- Do NOT create colored logo variants. The mark is always achromatic. + +### Favicon + +- SVG with `prefers-color-scheme` media query for light/dark switching +- `.ico` fallback at 32x32, Apple touch icon at 180x180 + +### Social Card + +- Dark background (#111111), white title text, optional 3px semantic accent line + +--- + +## Color Palettes (Reference) + +### Neutral — Achromatic (C:0.000) + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #f5f5f5 | #e8e8e8 | #d4d4d4 | #b7b7b7 | #9b9b9b | #808080 | #666666 | #505050 | #3d3d3d | #2b2b2b | #222222 | + +### Error — H:25° C:0.16 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #fff2f0 | #ffdfdc | #ffc3bd | #ff958d | #ec7069 | #ce514d | #ad3735 | #8d2324 | #701719 | #520e10 | #410b0c | + +### Warning — H:70° C:0.13 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #fff3e6 | #ffe3c3 | #fcca91 | #e6aa63 | #cd8c37 | #b17000 | #8e5900 | #704500 | #573400 | #402400 | #331b00 | + +### Lime — H:110° C:0.14 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #f7fac9 | #eaedb0 | #d8da8d | #bcbe5c | #a1a22b | #868600 | #6b6b00 | #535400 | #404000 | #2e2e00 | #242400 | + +### Success — H:155° C:0.14 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #ddffe8 | #bef8d1 | #9fe8b8 | #72ce95 | #48b475 | #1c985a | #007a44 | #006034 | #004a27 | #00361a | #002a13 | + +### Cyan — H:195° C:0.145 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #d4fffe | #a5faf9 | #7aeae9 | #2ad0d0 | #00b2b2 | #009393 | #007576 | #005c5c | #004747 | #003333 | #002828 | + +### Indigo — H:250° C:0.14 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #eef6ff | #d7eaff | #b4d8ff | #7cbdff | #53a0ec | #3284d0 | #1369b0 | #005190 | #003e71 | #002c54 | #002242 | + +### Violet — H:285° C:0.14 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #f4f4ff | #e5e5ff | #cfcfff | #b0adff | #938eeb | #7971d0 | #6057af | #4b4290 | #393172 | #282254 | #1f1b42 | + +### Magenta — H:320° C:0.14 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #fcf0ff | #f9ddff | #f2bffd | #da9de8 | #c07ecf | #a462b4 | #874895 | #6d3579 | #55265f | #3d1a45 | #301436 | + +### Rose — H:355° C:0.13 + +| 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 | +|---|---|---|---|---|---|---|---|---|---|---| +| #fff1f6 | #ffdde9 | #ffbfd7 | #f198bb | #d7799f | #bb5c84 | #9b436a | #7e3053 | #632240 | #48172d | #391223 | diff --git a/ILLUSTRATION_GUIDE.md b/ILLUSTRATION_GUIDE.md new file mode 100644 index 0000000..50b8a38 --- /dev/null +++ b/ILLUSTRATION_GUIDE.md @@ -0,0 +1,985 @@ +# Illustration & Diagram Generation Guide for AI Agents + +Production illustration generation using shape grammar formalism, parametric curve mathematics, cross-modal psychophysics, Gestalt composition constraints, and computational aesthetic validation. Designed as agent-executable specification — every formula is code-ready. Domain-agnostic: works for any brand category. + +This guide is the illustration counterpart to `COLOR_GUIDE.md` (color science, palette engineering, typography accessibility) and `SPATIAL_GUIDE.md` (spacing, curvature, proportions, target sizes). It takes three inputs — a validated color palette, a spatial token system, and a brand emotional profile (target PAD vector) — and produces a complete, validated illustration system: shape vocabulary, construction rules, composition algorithms, and aesthetic quality metrics. + +Four phases: **Illustration Strategy** (human-driven shape semantics and conceptual metaphor decisions grounded in perception science), **Shape Engineering** (agent-executable parametric math), **Composition & Layout** (agent-executable arrangement algorithms), and **Aesthetic Validation** (agent verification of complexity, balance, and congruence). The first phase grounds the human operator in shape psychophysics and visual communication research. The remaining phases are fully computable. + +**Prerequisite:** Complete `COLOR_GUIDE.md` Phases 1–3 and `SPATIAL_GUIDE.md` Phases 1–2 first. This guide references the PAD emotional model, color token architecture, font-color congruence framework, border radius system, spacing scale, and Gestalt proximity threshold defined there. + +--- + +## Phase 1: Illustration Strategy (Human Judgment) + +Before generating shapes or composing diagrams, ground the decisions in shape perception science, conceptual metaphor theory, and multimedia learning research. The agent assists with shape-emotion mapping and constraint validation; the human makes the judgment call on visual vocabulary and metaphor selection. + +### Shape Semantics Framework + +Geometric shapes carry measurable semantic associations. These associations are cross-culturally robust for basic forms but context-dependent for complex compositions — parallel to color-in-context theory (Elliot & Maier 2012, see `COLOR_GUIDE.md` Phase 1). + +**Sources:** Lundholm (1921); Aronoff et al. (1992); Larson et al. (2007); Dehaene et al. (2025), *eLife*. + +| Shape | Primary Semantic | Secondary Associations | Cultural Variations | +|---|---|---|---| +| Circle | Unity, wholeness, cycles, protection | Infinity, perfection, continuity | Zen: enlightenment; Chinese: heavens; Universal: sun/moon | +| Triangle (point-up) | Hierarchy, stability, ascent | Direction, dynamism, trinity | Religious trinity; navigation; fire element | +| Triangle (point-down) | Descent, instability, feminine energy | Funnel, concentration | Water element; inverted hierarchy | +| Square | Solidity, order, material world | Dependability, permanence | Chinese: earth; Native American: permanence | +| Hexagon | Balance, equilibrium, structure | Efficiency, tessellation | Beehive/nature; Star of David | +| Star (5-pointed) | Hope, protection, wholeness | Achievement, five elements | Islam: hope; Wicca: protection | +| Arrow | Direction, movement, causality | Progress, force, flow | Universal: path/trajectory; maps to PATH image schema | +| Rounded rectangle | Containment with approachability | Screens, cards, containers | Modern UI convention | + +**Neural basis:** Geometric shapes activate intraparietal and inferior temporal regions also involved in mathematical processing, indicating a strong link between geometric intuition and mathematical cognition (Dehaene et al. 2025). Humans encode shapes symbolically via discrete regularities (symmetries, parallelism), distinct from continuous visual processing. + +### Conceptual Metaphor Theory + +**Source:** Lakoff, G. & Johnson, M. (1980). *Metaphors We Live By*. University of Chicago Press. + +Humans understand abstract concepts by mapping them onto concrete spatial experiences. This operates through **image schemas** — recurring pre-conceptual patterns arising from embodied experience. Every diagram implicitly uses these schemas; making them explicit improves communication effectiveness. + +| Image Schema | Structure | Visual Representation | Abstract Mapping | +|---|---|---|---| +| CONTAINER | Inside, outside, boundary | Enclosed shapes, bordered regions | Inclusion/exclusion, scope, categories | +| PATH | Source, trajectory, goal | Arrows, lines, flow diagrams | Progress, process, narrative sequence | +| FORCE | Push, pull, resistance | Thickness, weight, directional emphasis | Causality, influence, agency | +| UP-DOWN | Vertical axis | Vertical positioning | GOOD IS UP, MORE IS UP, POWER IS UP | +| LINK | Connection between entities | Lines, connectors | Relationship, dependency | +| BALANCE | Equilibrium point | Symmetry, centering | Fairness, stability | + +**Spatial metaphors in design** (Casasanto & Bottini 2022, *Frontiers in Psychology*): +- **GOOD IS UP**: Positive valence associated with upward spatial position — reflected in UI patterns where success states appear above failure states. +- **GOOD IS RIGHT**: Positive emotions linked to right-side space. Products placed on the right are preferred. +- **Mental number line**: Smaller numbers instinctively mapped left, larger numbers right (reversed in right-to-left reading cultures). + +**Design implication:** Visual metaphors are not universal — the direction of mental timelines varies across cultures (left-to-right vs. right-to-left). Ignoring cultural variation in spatial metaphors degrades user experience. + +### Bouba/Kiki Cross-Modal Correspondences + +**Sources:** Kohler (1929), *Gestalt Psychology*; Ramachandran & Hubbard (2001), *J. Consciousness Studies*; Fort et al. (2022), *Scientific Reports*. + +Shape properties map systematically to abstract concepts through cross-modal correspondences. The bouba/kiki effect — first observed as "maluma/takete" by Kohler — demonstrates that rounded shapes associate with softness, warmth, and low pitch, while angular shapes associate with sharpness, coldness, and high pitch. Agreement rate: **95–98%** across cultures, replicated in preverbal infants as young as 4 months. + +| Shape Property | Abstract Mapping | Direction | Mechanism | +|---|---|---|---| +| Rounded/curvy | Lower pitch, larger size, heavier weight, slower speed | Round = low-frequency, large, heavy | Physical sound-shape correspondence | +| Angular/spiky | Higher pitch, smaller size, lighter weight, faster speed | Sharp = high-frequency, small, light | Abrupt spectral changes map to sharp contours | +| Symmetrical | Stability, order, competence | Regularity = trustworthiness | Processing fluency | +| Asymmetrical | Dynamism, creativity, novelty | Irregularity = energy | Effortful processing → arousal | + +**Quantitative findings:** +- Curvature preference meta-analysis: **Hedges' g = 0.39** (medium effect size) across tasks and contexts (Chuquichambi et al., CBS working paper). +- "Balance x Continuity" model predicts bouba/kiki with **mean R² = 0.60** (range 0.26–0.94) from existing datasets (Fort et al. 2022). + +**Implication for illustration:** Shape property choices are not arbitrary stylistic decisions — they carry measurable semantic weight. An angular illustration vocabulary signals precision/urgency; a rounded vocabulary signals warmth/approachability. These map directly onto the PAD model (see `SPATIAL_GUIDE.md` Phase 1 § Geometric Personality Framework). + +### Shape-Emotion PAD Mapping + +Extending `SPATIAL_GUIDE.md` Phase 1 with quantitative data from shape psychophysics: + +| Shape Property | Primary PAD Axis | Direction | Effect Size | Key Source | +|---|---|---|---|---| +| Curvature | Pleasure | Curved ↑ | g = 0.39 | Chuquichambi et al. (meta-analysis) | +| Angularity | Arousal + Dominance | Angular ↑ | Amygdala bilateral activation | Bar & Neta 2007 | +| Symmetry | Pleasure | Symmetric ↑ | Longer fixation, higher ratings | Frontiers in Psychology 2016 | +| Complexity (D) | Arousal | Mid-range ↑ | D = 1.3–1.5 sweet spot | Taylor et al. 2011 | +| Size | Dominance | Larger ↑ | Fitts' law, cross-domain | Fitts 1954 | +| Regularity | Pleasure + Dominance | Regular ↑ | Shorter description length | Feldman 2003 | +| Organic noise | Arousal (low) + Pleasure | Organic ↑ | 60% stress reduction at D ~1.4 | Taylor (Oregon lab) | + +**Critical moderator** (Leder et al. 2011, *Perception*): Curvature preference disappears when the object carries negative semantic valence. Applying high curvature to error states, destructive actions, or danger signals is incongruent and may reduce perceived severity. See `SPATIAL_GUIDE.md` Phase 2 § Curvature by Semantic Role. + +### Diagram Effectiveness Principles + +**Source:** Mayer, R. E. (2009). *Multimedia Learning* (2nd ed.). Cambridge University Press; Mayer & Fiorella (2014), *Cambridge Handbook of Multimedia Learning*. + +Mayer's Cognitive Theory of Multimedia Learning rests on three assumptions: dual-channel processing (visual + auditory), limited capacity per channel, and active processing (select, organize, integrate). These produce measurable design principles with replicated effect sizes: + +| Principle | Description | Studies Supporting | Median Effect Size (d) | +|---|---|---|---| +| Spatial Contiguity | Place words and pictures near each other | 22/22 | **1.10** | +| Temporal Contiguity | Present corresponding words and pictures simultaneously | 9/9 | **1.22** | +| Coherence | Exclude extraneous material | 23/23 | **0.86** | +| Redundancy | Graphics + narration > graphics + narration + text | 16/16 | **0.86** | +| Signaling | Add cues to highlight organization | 24/28 | **0.41** | +| Segmenting | Break complex information into user-paced segments | — | Moderate | +| Pre-training | Teach component names before processes | — | Moderate | + +**Application to illustration:** +- **Spatial contiguity (d = 1.10)** is the strongest effect. Labels must be placed directly adjacent to the element they describe — never in a separate legend when space permits. +- **Coherence (d = 0.86)** means decorative elements that do not support the message actively harm learning. Every shape must serve a communicative purpose. +- **Signaling (d = 0.41)** supports the use of visual hierarchy (weight, size, color) to guide attention through the diagram. + +### Icon and Pictogram Design Constraints + +**Source:** FHWA-RD-03-065 (2003), *Symbol Signs: Icon Design Guidelines*. Federal Highway Administration. + +The FHWA research defines mathematical constraints for recognizable symbols, grounded in Gestalt psychology: + +| Constraint | Specification | +|---|---| +| Design grid | 20 x 20 units | +| Minimum feature size | No significant detail smaller than 1 grid unit (5% of symbol area) | +| Minimum visual angle (detail) | 3 degrees for significant details | +| Minimum visual angle (stroke) | 2 degrees for line thickness | +| Detail budget rule | Only details whose removal would reduce recognition should be included | + +**Gestalt-based symbol principles (FHWA):** +1. **Figure/ground**: Clear, stable, solid relationship between symbol elements and background. +2. **Figure edges**: Solid shapes preferred over thin/dotted lines (except for depicting motion). +3. **Closure**: Closed figures without discontinuous lines or disjointed elements. +4. **Simplicity**: Include only necessary detail. Removing any essential detail should significantly reduce recognition. +5. **Unity**: All parts enclosed within a single boundary. + +### Decision Framework + +Score each candidate shape vocabulary against the brand's target PAD vector and communication goals: + +| Dimension | Weight | What to Evaluate | +|---|---|---| +| Semantic Clarity | High | Does each shape carry a clear, unambiguous meaning for the target audience? | +| PAD Congruence | High | Does the shape vocabulary's PAD profile match the brand's target? (See Shape-Emotion PAD Mapping) | +| Cross-Modal Fit | Medium | Do shapes align with bouba/kiki correspondences for intended concepts? | +| Diagram Effectiveness | Medium | Does the vocabulary support spatial contiguity (d = 1.10) and coherence (d = 0.86)? | +| Construction Feasibility | Medium | Can shapes be generated from parametric primitives? (See Phase 2) | +| Color System Fit | Medium | Does the shape vocabulary compose well with semantic color tokens? (See `COLOR_GUIDE.md`) | +| Cultural Safety | Low | Are shapes free of unintended cultural associations for the target audience? | +| Complexity Budget | Low | Is the total number of distinct shape types within cognitive load limits (6–8 categories max)? | + +The human operator evaluates these qualitatively against brand intent. The agent can compute PAD congruence checks and construction feasibility. Once the shape vocabulary is chosen, everything below is math. + +--- + +## Phase 2: Shape Engineering (Agent Math) + +From this point forward, the agent generates shape primitives autonomously. The human provides perceptual feedback during validation. + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherence!** + +## Shape Primitive Vocabulary + +All illustration primitives are parametric functions that emit SVG path data. An agent specifies shapes by parameter vectors — not by drawing pixels. + +### Superellipse (Lame Curve) + +The superellipse generalizes the ellipse into a family of curves controlled by an exponent parameter. It is the mathematical basis for "squircle" corners used in Apple's design language and the emerging CSS `corner-shape: superellipse()` specification. + +**Source:** Lame, G. (1818). *Examen des differentes methodes employees pour resoudre les problemes de geometrie*. Popularized by Piet Hein (1965) for urban design. + +**Implicit equation:** + +``` +|x/a|^n + |y/b|^n = 1 +``` + +**Parametric equations** (for SVG path generation, `0 <= t < 2*pi`): + +``` +x(t) = a * |cos(t)|^(2/n) * sgn(cos(t)) +y(t) = b * |sin(t)|^(2/n) * sgn(sin(t)) +``` + +where `a`, `b` are semi-axes and `n` controls curvature. + +**Shape behavior by exponent:** + +| n | Shape | Emotional Signal | +|---|---|---| +| 0 < n < 1 | Four-armed star (concave sides) | Sharp, aggressive, high arousal | +| n = 1 | Rhombus (diamond) | Angular, directional | +| 1 < n < 2 | Rounded rhombus (convex sides) | Transitional | +| n = 2 | Ellipse (circle when a = b) | Neutral, balanced | +| n > 2 | Rounded rectangle (squircle territory) | Soft, approachable | +| n = 4 | Squircle (a = b) | Maximum approachability | + +**CSS superellipse mapping** (emerging specification): + +``` +CSS corner-shape: superellipse(k) --> Lame exponent n = 2^k + +k = 0 --> n = 1 (bevel) +k = 1 --> n = 2 (circular arc, standard border-radius) +k = 2 --> n = 4 (squircle) +``` + +**SVG implementation:** Sample `t` at 64–128 points, compute `(x, y)` pairs, emit as SVG `` using `M` and `L` commands, or fit cubic Bezier curves for smoother output. + +### Gielis Superformula + +A polar-coordinate generalization of the superellipse that can describe an enormous variety of natural and abstract forms with a single equation. + +**Source:** Gielis, J. (2003). "A generic geometric transformation that unifies a wide range of natural and abstract shapes." *American Journal of Botany*, 90(3), 333–338. + +**Polar equation:** + +``` +r(phi) = ( |cos(m * phi / 4) / a|^n2 + |sin(m * phi / 4) / b|^n3 )^(-1/n1) +``` + +**Cartesian conversion:** + +``` +x = r(phi) * cos(phi) +y = r(phi) * sin(phi) +``` + +**Parameters:** + +| Parameter | Role | Typical Range | +|---|---|---| +| `a`, `b` | Scaling factors | Often 1.0 | +| `m` | Rotational symmetry (integer m gives m-fold) | 1–12 | +| `n1` | Overall roundness | 0.1–10.0 | +| `n2`, `n3` | Shape exponents (pinching, sharpness) | 0.1–10.0 | + +**Special cases:** + +``` +m=4, n1=n2=n3=2, a=b=1 --> circle +m=4, n1=2, n2=n3=2, a!=b --> ellipse +m=0 --> circle (no angular modulation) +Varying n1, n2, n3: starfish, flowers, leaves, diatoms, seed shapes +``` + +### Cubic Bezier Curves (SVG Standard) + +The workhorse of SVG path data, font outlines, and UI animation easing functions. + +**Source:** Bezier, P. (1968). French Patent 1,475,841. + +**General Bezier curve of degree n** with control points `P_0, ..., P_n`: + +``` +B(t) = SUM_{i=0}^{n} C(n,i) * (1-t)^(n-i) * t^i * P_i, 0 <= t <= 1 +``` + +where `C(n,i)` is the binomial coefficient. + +**Cubic Bezier** (4 control points, the SVG `C` command): + +``` +B(t) = (1-t)^3 * P0 + 3*(1-t)^2 * t * P1 + 3*(1-t) * t^2 * P2 + t^3 * P3 +``` + +- `P0`, `P3` are endpoints (on-curve). +- `P1`, `P2` are control handles (off-curve), defining tangent direction and magnitude. + +### Decorative Curves + +Parametric curves for borders, backgrounds, and ornamental elements. All emit `(x, y)` pairs for SVG path generation. + +| Curve | Parametric Equations | Key Parameters | Visual Character | +|---|---|---|---| +| Rose (Rhodonea) | `x = a * cos(k*t) * cos(t)`, `y = a * cos(k*t) * sin(t)` | `a` (amplitude), `k` (petals: odd k → k petals, even k → 2k petals) | Floral, radial symmetry | +| Lissajous | `x = A * sin(a*t + delta)`, `y = B * sin(b*t)` | `a/b` ratio (lobe count), `delta` (rotation) | Oscilloscope, harmonic | +| Hypotrochoid (Spirograph) | `x = (R-r)*cos(t) + d*cos((R-r)/r * t)`, `y = (R-r)*sin(t) - d*sin((R-r)/r * t)` | `R` (fixed circle), `r` (rolling), `d` (tracing distance) | Geometric, mandala-like | +| Epitrochoid | `x = (R+r)*cos(t) - d*cos((R+r)/r * t)`, `y = (R+r)*sin(t) - d*sin((R+r)/r * t)` | Same as hypotrochoid | Outer-rolling variant | + +**SVG implementation:** Iterate `t` from `0` to `2*pi * lcm(R, r) / R` (for spirograph) or `0` to `2*pi` (for rose/Lissajous with integer ratios). Emit polyline or fit Bezier curves. + +## Shape Grammar Formalism + +Shape grammars provide the formal framework for constructing complex illustrations from primitive shapes through rule-based transformations. + +**Sources:** Stiny, G. & Gips, J. (1972). "Shape Grammars and the Generative Specification of Painting and Sculpture." *Information Processing 71*; Knight, T. (2003). "Computing with ambiguity." *Environment and Planning B*. + +### Formal Definition + +A shape grammar is a 4-tuple: + +``` +SG = (S, L, R, I) +``` + +where: +- `S` — a finite set of shapes drawn from a shape algebra +- `L` — a finite set of labels (markers/tags) controlling rule application +- `R` — a finite set of production rules of the form `a -> b` (LHS -> RHS) +- `I` — an initial shape (axiom/start symbol) + +### Rule Application + +Given a current working shape `s` and a rule `a -> b`, the transformation produces a new shape `t`: + +``` +t = (s - a) + b +``` + +where `s - a` removes the matched subshape and `+ b` adds the replacement. + +### Embedding Relation + +A rule `a -> b` can only apply if `a` is a **part** (subshape) of `s`: + +``` +s . a = a (the product of s and a equals a) +``` + +This **embedding relation** is the most powerful aspect of shape grammars — it allows rules to match against any recognizable subshape, not just shapes explicitly placed by prior rules. This enables **emergence**: recognition of shapes that arise from spatial relationships of existing elements. + +### Weighted (Augmented) Shapes + +Shapes carry non-geometric attributes via tuple notation: + +``` +w_s = (s, a_s^1, a_s^2, a_s^3, ...) +``` + +where `s` is the geometric shape and `a_s^i` are attributes (color token, semantic role, weight, etc.). Operations (union, product, difference) apply separately across each component. + +### Agent-Executable Encoding + +A shape grammar encodes as a rule engine: +1. **Shape representation**: shapes as sets of line segments or parametric primitives, each with carrier and boundary +2. **Pattern matching**: detect subshapes via the embedding relation +3. **Rule selection**: when multiple rules match, choose stochastically, by priority, or exhaustively +4. **Transformation**: apply Euclidean transformations (translation, rotation, reflection, scaling) to embed the RHS + +## Construction Constraints + +Inspired by the IBM Design Language illustration system, which has one of the most rigorously formalized corporate illustration grammars. + +**Source:** IBM Design Language (ibm.com/design/language/illustration). + +### Grid Alignment + +``` +All anchor points snap to the spatial grid defined in SPATIAL_GUIDE.md. +Minimum shape dimension = base_unit (8px default). +Minimum negative space between shapes = base_unit. +Layered shapes require base_unit safe area. +``` + +### Shape Primitives + +Illustrations are constructed from: +- Squares, circles, rectangles, triangles (geometric foundation) +- Superellipses (controlled curvature) +- Cubic Bezier curves (organic forms built on geometric scaffolding) + +### Angle Constraints + +``` +Preferred angles: 15, 30, 45, 60, 75, 90 degrees +Other angles: permitted but require justification +``` + +### Radii Rules + +``` +Nested curved lines must maintain equal spacing with increasing radius. +No mixed corner radii within a nested design. +Border radius tokens from SPATIAL_GUIDE.md apply to all illustration containers. +``` + +## Noise and Organic Deformation + +For illustrations requiring natural, organic character (biology diagrams, nature scenes, terrain), apply noise-based deformation to geometric primitives. + +### Perlin Noise + +**Sources:** Perlin, K. (1985). "An Image Synthesizer." *ACM SIGGRAPH*, 19(3), 287–296; Perlin, K. (2002). "Improving Noise." *ACM Trans. Graphics*, 21(3), 681–682. + +**Improved fade function** (5th-degree smoothstep with zero first and second derivatives at endpoints): + +``` +fade(t) = t^3 * (t * (t * 6 - 15) + 10) +``` + +**2D Perlin noise algorithm:** +1. Divide space into unit cells. Assign pseudorandom unit gradient vector `g` at each grid vertex. +2. For point `(x, y)`: identify four surrounding grid corners. Compute offset vectors from each corner. +3. Dot products: for each corner `i`, compute `dot(g_i, offset_i)`. +4. Interpolation: bilinear interpolation using faded fractional coordinates: + +``` +u = fade(x - floor(x)) +v = fade(y - floor(y)) +result = lerp(v, + lerp(u, dot00, dot10), + lerp(u, dot01, dot11) +) +``` + +Output range: approximately `[-1, 1]`. + +### Fractal Brownian Motion (fBm) + +Layering multiple octaves of noise for richer, more natural-looking results: + +``` +fBm(x, y) = SUM_{i=0}^{octaves-1} amplitude_i * noise(frequency_i * x, frequency_i * y) + +where: + frequency_i = lacunarity^i (commonly lacunarity = 2.0) + amplitude_i = persistence^i (commonly persistence = 0.5) +``` + +**Normalization:** + +``` +fBm_normalized = fBm / SUM_{i=0}^{octaves-1} persistence^i +``` + +**Typical parameters:** `octaves` = 4–8, `lacunarity` = 2.0, `persistence` = 0.5. + +### SVG Path Deformation + +Apply noise-based displacement to any SVG path: + +``` +x_deformed = x_original + amplitude * noise(x_original * freq, y_original * freq) +y_deformed = y_original + amplitude * noise(x_original * freq + 1000, y_original * freq + 1000) +``` + +The offset (`+1000`) ensures x and y displacements use different noise values. `amplitude` controls distortion magnitude; `freq` controls granularity. + +## Recursive Systems (Reference) + +For fractal and plant-like illustration elements. These are reference formulas — the agent selects from them based on the illustration concept. + +### L-Systems (Lindenmayer Systems) + +**Source:** Prusinkiewicz, P. & Lindenmayer, A. (1990). *The Algorithmic Beauty of Plants*. Springer-Verlag. + +String-rewriting system with turtle graphics interpretation: +- `F` → move forward and draw +- `+` → turn left by angle +- `-` → turn right by angle +- `[` → push state (branch start) +- `]` → pop state (branch end) + +| Fractal | Axiom | Rules | Angle | +|---|---|---|---| +| Koch curve | `F` | `F -> F+F-F-F+F` | 90° | +| Sierpinski triangle | `A` | `A -> B-A-B`, `B -> A+B+A` | 60° | +| Dragon curve | `F` | `F -> F+G`, `G -> F-G` | 90° | +| Fractal plant | `X` | `X -> F+[[X]-X]-F[-FX]+X`, `F -> FF` | 25° | + +### Iterated Function Systems (IFS) + +**Source:** Barnsley, M. F. (1988). *Fractals Everywhere*. Academic Press. + +Contractive affine transformations applied iteratively: + +``` +f_i(x, y) = (a_i*x + b_i*y + e_i, c_i*x + d_i*y + f_i) +``` + +**Barnsley Fern coefficients:** + +| i | a | b | c | d | e | f | p | +|---|---|---|---|---|---|---|---| +| 1 | 0.00 | 0.00 | 0.00 | 0.16 | 0.00 | 0.00 | 0.01 | +| 2 | 0.85 | 0.04 | -0.04 | 0.85 | 0.00 | 1.60 | 0.85 | +| 3 | 0.20 | -0.26 | 0.23 | 0.22 | 0.00 | 1.60 | 0.07 | +| 4 | -0.15 | 0.28 | 0.26 | 0.24 | 0.00 | 0.44 | 0.07 | + +**Chaos Game algorithm:** Pick random point; iteratively select transformation `f_i` with probability `p_i`; apply to current point; plot. Repeat for 10,000–1,000,000+ iterations. + +--- + +## Phase 3: Composition & Layout (Agent Math) + +From this point forward, the agent composes shapes into diagrams and illustrations autonomously. The human provides perceptual feedback during validation. + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherence!** + +## Visual Balance Computation + +### Deviation of Center of Mass (DCM) + +The most rigorously validated measure of visual balance. Treats pixel luminance (or saliency) as physical mass and computes how far the composition's center of gravity deviates from its geometric center. + +**Source:** Hubner, R. & Fillinger, M. G. (2016). "Comparison of Objective Measures for Predicting Perceptual Balance and Visual Aesthetic Preference." *Frontiers in Psychology*, 7, 335. + +**Formula:** + +For an image where each pixel `(i, j)` has mass `m(i, j)` (luminance, saliency, or binary occupancy): + +``` +b_x = SUM(i * m(i,j)) / N (mass-weighted horizontal centroid) +b_y = SUM(j * m(i,j)) / N (mass-weighted vertical centroid) + +where N = SUM(m(i,j)) (total mass) +``` + +Normalized location: `b'_x = b_x / width` (ranges 0 to 1; geometric center = 0.5) + +Deviation: `d_x = 0.5 - b'_x`, `d_y = 0.5 - b'_y` + +Final DCM (as percentage): + +``` +DCM = 100 * sqrt(d_x^2 + d_y^2) +``` + +Lower DCM = higher balance. + +**Empirical validation:** N = 16 participants, 130 stimuli. DCM correlated with subjective balance ratings: r = -0.822, p < 0.001, **R² = 0.675**. DCM outperformed all other objective measures tested. + +### APB Symmetry Decomposition + +**Source:** Wilson, A. & Chatterjee, A. (2005). "The assessment of preference for balance." *Empirical Studies of the Arts*, 23(2), 165–180. + +Decomposes an image into symmetry measures across four axes (horizontal, vertical, main diagonal, anti-diagonal), each computed in two ways (halves and inner-outer): + +``` +Divide image into four vertical strips A1-A4. +h = (|[f(A1) + f(A2)] - [f(A3) + f(A4)]| / N) * 100 +h_io = (|[f(A1) + f(A4)] - [f(A2) + f(A3)]| / N) * 100 +``` + +Analogous measures for vertical (`v`, `v_io`), main diagonal (`md`, `md_io`), anti-diagonal (`ad`, `ad_io`). The APB score is the mean of all eight partial measures. Lower = more balanced. + +**Empirical validation:** Multiple regression R² = 0.751, F(8,121) = 45.5, p < 0.001. Horizontal component had the largest standardized beta. + +### Arnheim's Visual Weight Factors + +**Source:** Arnheim, R. (1974). *Art and Visual Perception*. University of California Press. + +Qualitative factors affecting visual weight, applicable to computing element-level mass for DCM: + +``` +visual_weight(element) = f(area, luminance_inverse, saturation, warmth, regularity, distance_from_center) +``` + +- Larger area = heavier +- Darker value = heavier +- Higher saturation = heavier +- Warm colors (red, orange) = heavier than cool (blue, green) +- Regular shapes = heavier than irregular +- Distance from center acts as lever arm (moment = weight × distance) + +## Gestalt Proximity Formalization + +**Sources:** Kubovy, M. & Wagemans, J. (1995), *Psychological Science*; Kubovy, M., Holcombe, A. O., & Wagemans, J. (1998), *Cognitive Psychology*; Kubovy, M. & van den Berg, M. (2008), *Psychological Review*. + +### The Pure Distance Law + +The probability of perceiving grouping along a given orientation is a distance-decay function: + +``` +log(p_k / p_base) = -alpha * (d_k / d_base - 1) +``` + +where `d_k` is the center-to-center distance between elements along orientation `k`, `d_base` is the shortest inter-element distance, and `alpha` is the empirically fitted attraction constant. + +**Key finding:** Grouping strength depends only on the **ratio** of local distances, not absolute distances. + +**Extension with similarity** (Kubovy & van den Berg, 2008): + +``` +log(p_k / p_base) = -alpha * (d_k / d_base - 1) + beta * delta_L +``` + +where `delta_L` is the luminance difference supporting grouping and `beta` is the similarity weight. + +**Application:** This formalizes the Gestalt proximity threshold from `SPATIAL_GUIDE.md` Phase 2 § Gestalt Proximity Threshold (`within_group_spacing < between_group_spacing * 0.5`) as a special case of the pure distance law. + +### Continuity as Curvature Minimization + +The visual system prefers contours that minimize total curvature: + +``` +minimize: INTEGRAL(kappa(s)^2 ds) +``` + +where `kappa(s)` is the curvature at arc-length parameter `s`. This is equivalent to the Euler elastica problem — connecting elements with smooth, low-curvature paths produces stronger perceived continuity. + +**Source:** Ullman, S. (1976). "Filling-in the gaps." *Biological Cybernetics*. + +## Constraint-Based Layout + +Inspired by the Penrose system (CMU), which provides a fully declarative, constraint-based diagram generation approach. + +**Source:** Penrose System (penrose.cs.cmu.edu); Ye et al. (2020), *SIGGRAPH 2020*. + +### Constraint Catalog + +Spatial relationship constraints an agent can declare and solve: + +| Category | Constraints | Purpose | +|---|---|---| +| Containment | `contains(outer, inner)`, `containsWithPadding(outer, inner, pad)` | Nesting, grouping | +| Overlap | `overlapping(a, b)`, `disjoint(a, b)` | Venn diagrams, exclusion | +| Proximity | `near(a, b, dist)`, `notTooClose(a, b, min)`, `touching(a, b)` | Gestalt grouping | +| Direction | `above(a, b)`, `below(a, b)`, `leftOf(a, b)`, `rightOf(a, b)` | Spatial metaphors | +| Distribution | `distributeHorizontally(elements, gap)`, `distributeVertically(elements, gap)` | Even spacing | +| Geometry | `perpendicular(l1, l2)`, `collinear(a, b, c)`, `isRegular(polygon)` | Structural alignment | +| Comparison | `equal(a.width, b.width)`, `lessThan(a.height, maxH)` | Size coordination | + +### Solving Approach + +Constraints are satisfied via numerical optimization (gradient descent on a differentiable energy function) or constraint propagation (Cassowary linear solver for layout constraints). + +**Cassowary constraint format** (for linear constraints): + +``` +a_1*x_1 + a_2*x_2 + ... + a_n*x_n {=, <=, >=} c [strength: required|strong|medium|weak] +``` + +**Source:** Badros, G. J., Borning, A. & Stuckey, P. J. (2001). "The Cassowary Linear Arithmetic Constraint Solving Algorithm." *ACM TOPLAS*, 23(4), 462–513. + +## Proportion Systems + +### Golden Ratio + +``` +phi = (1 + sqrt(5)) / 2 ~= 1.6180339887 +``` + +**Golden section division:** Place the primary focal element at approximately 61.8% / 38.2% of the composition frame, rather than at the midpoint. + +**Empirical caveat:** The claim that phi is universally preferred is contested. Fechner (1876) found slight preference for golden rectangles, but effect sizes were small and subsequent replications mixed. Modern meta-analyses suggest the preference is real but modest and culturally modulated. Treat as a default to deviate from intentionally, not a law. + +**Source:** Livio, M. (2002). *The Golden Ratio*. Broadway Books. + +### Rule of Thirds + +Divide the composition into a 3x3 grid. Place key elements at the four intersection points. + +**Formalization** (Bhattacharya et al. 2010): + +``` +F = (1 / (H * W)) * [||x_0 - s_1||, ||x_0 - s_2||, ||x_0 - s_3||, ||x_0 - s_4||] +``` + +where `x_0` is the saliency center of mass and `s_i` are the four rule-of-thirds points at `(W/3, H/3)`, `(2W/3, H/3)`, `(W/3, 2H/3)`, `(2W/3, 2H/3)`. + +**Relationship to golden ratio:** Golden ratio divides at ~38.2%/61.8%; rule of thirds at 33.3%/66.7%. Close but not identical. They emerged independently. + +## Layout Algorithms + +The agent selects algorithm based on data structure: + +| Algorithm | Input Structure | Output | Best For | +|---|---|---|---| +| Force-directed | Graph (nodes + edges) | Node positions | Relationship diagrams, networks | +| Treemap | Hierarchical (tree + values) | Nested rectangles | Space-filling, proportion visualization | +| Circle packing | Hierarchical (tree + values) | Nested circles | Organic hierarchy, set containment | +| Voronoi tessellation | Seed points | Cell partitions | Territory, proximity-based regions | +| Constraint solver | Constraints | Element positions | Structured diagrams, labeled layouts | +| Narrative diagram | Ordered act sequence | N static compositions | Scroll-driven reveals, causal sequence diagrams | + +### Force-Directed Layout + +**Sources:** Fruchterman, T. M. J. & Reingold, E. M. (1991), *Software: Practice and Experience*; Kamada, T. & Kawai, S. (1989), *Information Processing Letters*. + +**Fruchterman-Reingold model:** + +``` +f_rep(d) = -k^2 / d (repulsive: between all node pairs) +f_att(d) = d^2 / k (attractive: between connected nodes) + +where k = C * sqrt(area / |V|) (ideal edge length) +``` + +**Kamada-Kawai stress minimization:** + +``` +E = SUM_{i0) [log(N(epsilon)) / log(1/epsilon)] +``` + +Overlay the image with boxes of side length `epsilon`. Count boxes `N(epsilon)` containing part of the pattern. Plot `log(N)` vs. `log(1/epsilon)`; the slope is D. + +**Preferred fractal dimension:** + +| Pattern Type | Preferred D Range | Notes | +|---|---|---| +| Statistical fractals (nature) | **1.3–1.5** | Most prevalent in nature and species-rich habitats | +| Exact fractals (geometric) | Higher D preferred | Precise repetition shifts optimum upward | +| Human eye search pattern | D = 1.5 | Intrinsic eye movement traces fractal at D ~1.5 | + +**Stress reduction:** Viewing natural fractal patterns (D = 1.3–1.5) reduces physiological stress by up to 60% (Taylor, University of Oregon lab). + +**"Fractal fluency" hypothesis:** The visual system is tuned to efficiently process fractal complexities found in nature, producing both aesthetic pleasure and restorative outcomes when D matches the eye's intrinsic search pattern of approximately 1.5. + +**Validation threshold:** + +``` +For illustrations targeting aesthetic pleasure: + assert 1.2 <= D <= 1.6 +For geometric/technical diagrams: + D constraint relaxed (structure clarity takes priority over fractal fluency) +``` + +### Machado-Cardoso Aesthetic Measure + +**Source:** Machado, P. & Cardoso, A. (1998). "Computing aesthetics." *Proc. 14th Brazilian Symposium on AI*; Machado et al. (2015), *Acta Psychologica*. + +``` +AM = IC / PC +``` + +where: +- **IC** (Image Complexity) — estimated via JPEG compression ratio: `IC = compressed_size / raw_size`. Higher ratio = lower complexity. +- **PC** (Processing Complexity) — estimated via compression ratio of the edge-detected image. Edge detection → compression approximates structural processing complexity. + +Images with high IC but low PC (rich visual content that is easy to parse structurally) score highest — matching the Berlyne model: complex but comprehensible. + +### Shannon Entropy + +**Source:** Shannon, C. E. (1948). *Bell System Technical Journal*. + +``` +H = -SUM(p_i * log2(p_i)) +``` + +where `p_i` is the probability of intensity level `i` (for tonal complexity) or orientation bin `i` (for edge-orientation entropy). + +**Application:** Edge-orientation entropy — measuring how evenly luminance edges distribute across orientations — predicts aesthetic ratings for man-made images (Redies et al. 2018, *Frontiers in Neuroscience*). + +### Berlyne's Inverted-U Curve + +**Source:** Berlyne, D. E. (1971). *Aesthetics and Psychobiology*. Appleton-Century-Crofts. + +Aesthetic appreciation follows an inverted-U relationship with complexity: + +``` +Too simple (low D, low H) --> boring, trivial +Optimal middle range --> maximum aesthetic pleasure +Too complex (high D, high H) --> overwhelming, incomprehensible +``` + +This applies across visual art, music, and cinema. The optimal range corresponds to fractal dimension D = 1.3–1.5 and moderate entropy. + +## Balance Validation + +``` +For every composed illustration: + compute DCM + assert DCM < 10.0 (highly balanced) + warn if DCM > 15.0 (noticeable imbalance) + + For intentionally asymmetric compositions: + DCM threshold relaxed + but document the asymmetry rationale +``` + +## Diagram Effectiveness Checklist + +For every instructional diagram, verify these principles are satisfied: + +``` +Spatial Contiguity (d = 1.10): + Labels placed directly adjacent to elements they describe. + No separate legend when labels can be integrated. + +Coherence (d = 0.86): + Every visual element serves a communicative purpose. + No decorative elements that do not support the message. + +Signaling (d = 0.41): + Visual hierarchy guides attention through the diagram. + Key elements distinguished by size, weight, or semantic color. + +Redundant Encoding (WCAG 1.4.1): + Every color signal paired with a non-color indicator (shape, label, pattern). + See COLOR_GUIDE.md Phase 3 § Color-Only Information. +``` + +## Congruence Check + +Verify alignment with the color and spatial token systems: + +``` +For each illustration element: + color_PAD = PAD vector from color properties (from COLOR_GUIDE.md) + spatial_PAD = PAD vector from spatial properties (from SPATIAL_GUIDE.md) + shape_PAD = PAD vector from shape properties (this guide) + + congruence_check: + |color_PAD.pleasure - shape_PAD.pleasure| <= 1 tier + |spatial_PAD.dominance - shape_PAD.dominance| <= 1 tier + |color_PAD.arousal - shape_PAD.arousal| <= 1 tier + + Incongruence red flags: + Angular shapes + high-curvature spatial tokens → shape says "threat," space says "warm" + Organic noise + geometric spatial grid → shape says "natural," layout says "rigid" + High-complexity illustration + minimal color palette → visual density mismatch + Rounded shapes + error semantic color → shape says "safe," color says "danger" +``` + +## Extended Validation Checklist + +For every generated illustration, verify: + +**Shape Primitives:** +- [ ] All shapes generated from parametric formulas (superellipse, Bezier, superformula) +- [ ] Shape exponents/parameters documented for reproducibility +- [ ] Shapes snap to spatial grid (`--space-*` tokens from `SPATIAL_GUIDE.md`) +- [ ] Preferred angles used (15°, 30°, 45°, 60°, 75°, 90°) or deviations justified +- [ ] Minimum feature size ≥ 1 grid unit (FHWA symbol constraint) + +**Composition:** +- [ ] DCM < 10.0 (or intentional asymmetry documented) +- [ ] Gestalt proximity threshold met: within-group spacing < between-group spacing × 0.5 +- [ ] Layout algorithm appropriate for data structure (force-directed for graphs, treemap for hierarchies) +- [ ] Spatial contiguity: labels adjacent to elements (Mayer, d = 1.10) +- [ ] Coherence: no decorative elements without communicative purpose (Mayer, d = 0.86) + +**Aesthetic Quality:** +- [ ] Fractal dimension D in range 1.2–1.6 (for organic illustrations) +- [ ] Berlyne complexity in optimal range (not too simple, not too complex) +- [ ] Shannon entropy computed and within expected range for illustration type +- [ ] Machado-Cardoso AM > threshold (high image complexity, low processing complexity) + +**Color and Spatial Congruence:** +- [ ] All colors from semantic token system (`--visual-*` tokens from `DESIGN_SYSTEM.md`) +- [ ] Shape PAD vector within ±1 tier of color PAD vector on all dimensions +- [ ] Shape PAD vector within ±1 tier of spatial PAD vector on all dimensions +- [ ] No incongruence red flags (see Congruence Check above) + +**Accessibility:** +- [ ] Every color signal has redundant encoding (WCAG 1.4.1) +- [ ] Minimum contrast for text within illustrations meets APCA thresholds (see `COLOR_GUIDE.md` Phase 3) +- [ ] Illustrations function in both light and dark mode +- [ ] No seizure-risk content (no large-area saturated red transitions, no flashing > 3 Hz) +- [ ] `prefers-reduced-motion` respected for animated illustrations + +**Technical Output:** +- [ ] SVG output is valid and well-formed +- [ ] All path data uses standard commands (M, L, C, A, Z) +- [ ] No hardcoded color values — all colors reference CSS custom properties +- [ ] File size within budget (< 50 KB for icons, < 200 KB for full illustrations) + +--- + +## References + +### Shape Perception & Psychophysics +- **Bar, M. & Neta, M.** — "Humans prefer curved visual objects" (*Psychological Science*, 2006, 17(8), 645–648). Curvature preference at 84ms subliminal exposure. +- **Bar, M. & Neta, M.** — "Visual elements of subjective preference modulate amygdala activation" (*Neuropsychologia*, 2007, 45(10), 2191–2200). fMRI: sharp contours activate amygdala bilaterally. +- **Leder, H., Tinio, P. P. & Bar, M.** — "Emotional valence modulates the preference for curved objects" (*Perception*, 2011, 40(6), 649–655). Curvature preference only significant for positive/neutral valence. +- **Ramachandran, V. S. & Hubbard, E. M.** — "Synaesthesia — A window into perception, thought and language" (*J. Consciousness Studies*, 2001, 8(12), 3–34). Bouba/kiki effect formalization. +- **Kohler, W.** — *Gestalt Psychology* (Liveright, 1929). Original maluma/takete observation. +- **Fort, M., Schwartz, J.-L. & Boulle, G.** — "Resolving the bouba-kiki effect enigma by rooting iconic sound symbolism in physical properties of round and spiky objects" (*Scientific Reports*, 2022, 12, 19172). Balance x Continuity model, R² = 0.60. +- **Chuquichambi, E. G., Palumbo, L., Rey, G. D. & Munar, E.** — "How universal is preference for visual curvature?" (CBS working paper). Meta-analysis: Hedges' g = 0.39. +- **Dehaene, S. et al.** — "Neural mechanisms of geometric shape perception" (*eLife*, 2025, reviewed preprint 106464). Geometric intuition linked to mathematical cognition. +- **Lundholm, H.** — "The affective tone of lines" (*Psychological Review*, 1921). Curved = gentle/quiet; angular = agitating/hard/furious. +- **Aronoff, J., Barclay, A. M. & Stevenson, L. A.** — "The recognition of threatening facial stimuli" (*J. Personality and Social Psychology*, 1992). V-shapes → anger association. +- **Larson, C. L., Aronoff, J. & Stearns, J. J.** — "The shape of threat" (*Emotion*, 2007). Angular → anger; rounded → happiness. + +### Conceptual Metaphor & Spatial Cognition +- **Lakoff, G. & Johnson, M.** — *Metaphors We Live By* (University of Chicago Press, 1980). Conceptual Metaphor Theory and image schemas. +- **Casasanto, D. & Bottini, R.** — "Spatial metaphors in design and everyday objects" (*Frontiers in Psychology*, 2022). GOOD IS UP, GOOD IS RIGHT, mental number line. + +### Multimedia Learning +- **Mayer, R. E.** — *Multimedia Learning* (2nd ed., Cambridge University Press, 2009). Dual-channel, limited-capacity, active-processing model. +- **Mayer, R. E. & Fiorella, L.** — "Principles for reducing extraneous processing in multimedia learning" (*Cambridge Handbook of Multimedia Learning*, 2nd ed., 2014). Effect sizes: spatial contiguity d = 1.10, coherence d = 0.86. + +### Iconography & Symbol Design +- **FHWA-RD-03-065** — Chapter 4: Icon design guidelines (Federal Highway Administration, 2003). 20x20 grid, minimum feature size, Gestalt-based symbol principles. + +### Shape Grammars & Formal Systems +- **Stiny, G. & Gips, J.** — "Shape Grammars and the Generative Specification of Painting and Sculpture" (*Information Processing 71*, 1972, pp. 1460–1465). Foundational shape grammar paper. +- **Stiny, G.** — "Introduction to Shape and Shape Grammars" (*Environment and Planning B*, 1980, 7(3), 343–351). Shape algebra formalization. +- **Knight, T.** — "Computing with ambiguity" (*Environment and Planning B*, 2003, 30(2), 165–180). Emergence in shape computation. +- **Krishnamurti, R.** — "The construction of shapes" (*Environment and Planning B*, 1981, 8(1), 5–40). First interpreter with full embedding relation. + +### Parametric Curves & Procedural Generation +- **Lame, G.** — *Examen des differentes methodes employees pour resoudre les problemes de geometrie* (1818). Superellipse formulation. +- **Gielis, J.** — "A generic geometric transformation that unifies a wide range of natural and abstract shapes" (*American Journal of Botany*, 2003, 90(3), 333–338). Superformula. +- **Bezier, P.** — "Procede de definition numerique des courbes et surfaces non mathematiques" (French Patent 1,475,841, 1968). Bezier curves. +- **Perlin, K.** — "An Image Synthesizer" (*ACM SIGGRAPH*, 1985, 19(3), 287–296). Perlin noise. +- **Perlin, K.** — "Improving Noise" (*ACM Trans. Graphics*, 2002, 21(3), 681–682). Improved fade function. +- **Prusinkiewicz, P. & Lindenmayer, A.** — *The Algorithmic Beauty of Plants* (Springer-Verlag, 1990). L-systems. +- **Barnsley, M. F.** — *Fractals Everywhere* (Academic Press, 1988). IFS and Barnsley Fern. + +### Visual Balance & Composition +- **Hubner, R. & Fillinger, M. G.** — "Comparison of Objective Measures for Predicting Perceptual Balance and Visual Aesthetic Preference" (*Frontiers in Psychology*, 2016, 7, 335). DCM: r = -0.822, R² = 0.675. +- **Wilson, A. & Chatterjee, A.** — "The assessment of preference for balance" (*Empirical Studies of the Arts*, 2005, 23(2), 165–180). APB score. +- **Arnheim, R.** — *Art and Visual Perception: A Psychology of the Creative Eye* (revised ed., University of California Press, 1974). Structural skeleton, visual weight factors. +- **Bhattacharya, S., Sukthankar, R. & Shah, M.** — "A framework for photo-quality assessment and enhancement based on visual aesthetics" (*Proc. ACM Multimedia*, 2010, 271–280). Rule of thirds formalization. +- **Livio, M.** — *The Golden Ratio: The Story of Phi* (Broadway Books, 2002). + +### Gestalt Formalization +- **Kubovy, M. & Wagemans, J.** — "Grouping by proximity and multistability in dot lattices" (*Psychological Science*, 1995, 6(4), 225–234). Pure distance law. +- **Kubovy, M., Holcombe, A. O. & Wagemans, J.** — "On the lawfulness of grouping by proximity" (*Cognitive Psychology*, 1998, 35(1), 71–98). +- **Kubovy, M. & van den Berg, M.** — "The whole is equal to the sum of its parts" (*Psychological Review*, 2008, 115(1), 131–154). Proximity + similarity additivity. +- **Ullman, S.** — "Filling-in the gaps" (*Biological Cybernetics*, 1976). Continuity as curvature minimization. +- **Feldman, J.** — "Perceptual grouping by selection of a logically minimal model" (*Int. J. Computer Vision*, 2003, 55(1), 5–25). Closure as MDL. + +### Computational Aesthetics +- **Taylor, R. P., Micolich, A. P. & Jonas, D.** — "Fractal analysis of Pollock's drip paintings" (*Nature*, 1999, 399, 422). +- **Spehar, B., Clifford, C. W. G., Newell, B. R. & Taylor, R. P.** — "Universal aesthetic of fractals" (*Computers & Graphics*, 2003, 27(5), 813–820). +- **Taylor, R. P., Spehar, B., Van Donkelaar, P. & Hagerhall, C. M.** — "Perceptual and physiological responses to Jackson Pollock's fractals" (*Frontiers in Human Neuroscience*, 2011, 5, 60). D = 1.3–1.5 sweet spot. +- **Berlyne, D. E.** — *Aesthetics and Psychobiology* (Appleton-Century-Crofts, 1971). Inverted-U curve. +- **Machado, P. & Cardoso, A.** — "Computing aesthetics" (*Proc. 14th Brazilian Symposium on AI*, 1998, 219–228). AM = IC/PC. +- **Machado, P. et al.** — "Computerized measures of visual complexity" (*Acta Psychologica*, 2015, 160, 43–57). +- **Birkhoff, G. D.** — *Aesthetic Measure* (Harvard University Press, 1933). M = O/C. +- **Shannon, C. E.** — "A mathematical theory of communication" (*Bell System Technical Journal*, 1948, 27(3), 379–423). +- **Redies, C., Brachmann, A. & Hayn-Leichsenring, G. U.** — "Edge-orientation entropy predicts preference for diverse types of man-made images" (*Frontiers in Neuroscience*, 2018, 12, 678). +- **Hasler, D. & Suesstrunk, S. E.** — "Measuring colorfulness in natural images" (*Proc. SPIE*, 2003, 5007, 87–95). Colorfulness formula. + +### Layout Algorithms +- **Fruchterman, T. M. J. & Reingold, E. M.** — "Graph drawing by force-directed placement" (*Software: Practice and Experience*, 1991, 21(11), 1129–1164). +- **Kamada, T. & Kawai, S.** — "An algorithm for drawing general undirected graphs" (*Information Processing Letters*, 1989, 31(1), 7–15). +- **Badros, G. J., Borning, A. & Stuckey, P. J.** — "The Cassowary Linear Arithmetic Constraint Solving Algorithm" (*ACM TOPLAS*, 2001, 23(4), 462–513). +- **Bruls, M., Huizing, K. & van Wijk, J. J.** — "Squarified Treemaps" (*Joint Eurographics / IEEE TCVG Symposium on Visualization*, 2000, 33–42). +- **Wang, W. et al.** — "Visualization of large hierarchical data by circle packing" (*CHI 2006*, 517–520). + +### Industry Systems +- **IBM Design Language** — Illustration system (ibm.com/design/language/illustration). 8px grid, shape primitives, angle constraints, radii rules. +- **Penrose System** — Constraint-based mathematical diagram generation (penrose.cs.cmu.edu). Domain/Substance/Style DSL. Ye et al. (2020), *SIGGRAPH 2020*. +- **Bertin, J.** — *Semiologie Graphique* (1967). Visual encoding channels. +- **Munzner, T.** — *Visualization Analysis and Design* (CRC Press, 2014). Channel effectiveness ranking. + +### Data Visualization +- **Wilkinson, L.** — *The Grammar of Graphics* (Springer, 2005). Formal framework for data-to-visual mapping. +- **Okabe, M. & Ito, K.** — "Color Universal Design" (2002). CVD-safe categorical palette. (See `COLOR_GUIDE.md` Phase 3.) diff --git a/SPATIAL_GUIDE.md b/SPATIAL_GUIDE.md new file mode 100644 index 0000000..72a7940 --- /dev/null +++ b/SPATIAL_GUIDE.md @@ -0,0 +1,743 @@ +# Spatial Design System Generation Guide for AI Agents + +Production spatial system generation using psychophysical shape perception, Gestalt grouping thresholds, Fitts' law motor modeling, modular proportion scales, and PAD-mapped geometric emotion. Designed as agent-executable specification — every formula is code-ready. Domain-agnostic: works for any brand category. + +This guide is the spatial counterpart to `COLOR_GUIDE.md`. It takes three inputs — a validated color palette, a chosen typography stack, and a brand emotional profile (target PAD vector) — and produces a complete, validated spatial token system: spacing scale, border radii, line weights, surface hierarchy, proportion ratios, and target sizes. + +Two phases: **Spatial Strategy** (human-driven brand geometry decisions grounded in perception science) and **Spatial Engineering** (agent-executable math producing token values). The first phase establishes the geometric personality of the brand. The second phase computes every token from that personality. + +**Prerequisite:** Complete COLOR_GUIDE.md Phases 1–3 first. This guide references the PAD emotional model, font-color congruence framework, and three-tier token architecture defined there. + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherence!** + +--- + +## Phase 1: Spatial Strategy (Human Judgment) + +Before computing spatial tokens, ground the geometric decisions in perception science. The agent assists with research retrieval and PAD alignment checks; the human makes the judgment call on brand geometry. + +### Geometric Personality Framework + +Color triggers emotional response along Pleasure-Arousal-Dominance. So do spatial properties — but on partially different axes. The table below maps each spatial dimension to its primary PAD axis, based on converging evidence from psychophysics, neuroaesthetics, and HCI research. + +| Spatial Property | Primary PAD Axis | Direction | Evidence Strength | Key Source | +|---|---|---|---|---| +| Curvature (border-radius) | Pleasure | Curved ↑, Angular ↓ | Strong (fMRI, cross-cultural, cross-species) | Bar & Neta 2007; Gómez-Puerto et al. 2015 | +| Whitespace amount | Pleasure | More space ↑ | Moderate (+20% comprehension) | Mapletree Studio 2024; Fogg et al. 2003 | +| Border weight | Dominance | Thicker ↑ | Moderate (accessibility audits) | WordPress 5.3 audit 2019 | +| Element size | Dominance | Larger ↑ | Strong (Fitts' law, cross-domain) | Fitts 1954; ISO 9241-411 | +| Spacing density | Arousal | Tighter ↑ | Moderate (cognitive load studies) | NNG 2024 | +| Symmetry | Pleasure | Symmetric ↑ | Strong (EEG, eye-tracking) | Frontiers in Psychology 2016 | +| Asymmetry | Arousal | Asymmetric ↑ | Moderate | Frontiers in Human Neuroscience 2015 | +| Proportion (golden ratio) | Pleasure | Closer to φ ↑ | Moderate (aesthetic-usability effect) | Tractinsky et al. 2000 | +| Surface luminance stepping | Dominance | Greater step ↑ | Moderate | Material Design elevation studies | + +**Critical insight:** Color saturation is the dominant arousal lever (η² = .693, Wilms & Oberfeld 2018). Font weight is the dominant dominance lever (see COLOR_GUIDE.md). Curvature is the dominant *spatial* pleasure lever. These three axes are partially orthogonal — they combine multiplicatively, not additively. Bold text + high chroma + angular geometry = maximum arousal AND dominance AND low pleasure (urgent, commanding, aggressive). Light text + medium chroma + curved geometry = moderate arousal, low dominance, high pleasure (friendly, approachable, warm). + +### Curvature Preference: The Evidence Base + +The human preference for curved over angular contours is one of the most replicated findings in visual perception. It is not a design opinion — it is a measured neural response. + +**Source:** Gómez-Puerto, G., Munar, E., & Nadal, M. (2015). "Preference for curvature: A historical and conceptual framework." *Frontiers in Human Neuroscience*, 9:712. Comprehensive review spanning decades of curvature research. + +Key experimental findings, chronologically: + +| Study | Sample | Finding | +|---|---|---| +| Fantz & Miranda 1975 | 1-week-old neonates | Longer fixation on curved vs. sharp contour forms | +| Lundholm 1921 | Adults | Curved lines perceived as "gentle, quiet"; sharp lines as "agitating, hard, furious" | +| Poffenberger & Barrows 1924 | Adults | Confirmed Lundholm's affective associations | +| Bar & Neta 2006, *Psychological Science* | Adults | Curved stimuli liked more than angular even at 84ms exposure (subliminal threshold) | +| Bar & Neta 2007, *Neuropsychologia* | Adults (fMRI) | Sharp contours activate amygdala bilaterally; curved do not. Sharp = neural threat signal | +| Quinn et al. 1997 | 3–4 month infants | Curvature preference facilitates Gestalt organization | +| Leder et al. 2011, *Perception* | Adults | Curvature preference only significant when object has positive/neutral valence | +| Silvia & Barona 2009 | Adults | Non-experts prefer circles to hexagons; expertise moderates effect | +| Gómez-Puerto et al. 2013 | Rural Ghana | Curvature preference confirmed cross-culturally in non-Western population | +| Munar et al. 2015 | Non-human primates | Curvature preference observed across species | +| Vartanian et al. 2013 | Adults | Curved interior architectural spaces subjectively preferred | +| UXPA Journal 2024 | N=187 (between-subjects) | Rounded app corners → higher aesthetics (M=4.63/7, α=.89), warmth (M=5.25/7, α=.83), ease of use, satisfaction, and prosocial behavior | + +**Critical moderator (Leder et al. 2011):** Curvature preference disappears when the object carries negative semantic valence. This means curvature is not a universal "make it better" lever — it works when the content supports a positive or neutral emotional frame. Error states, destructive actions, and danger signals are semantically negative. Applying high curvature to negative-valence UI elements is incongruent and may reduce perceived severity. + +### Shape × Emotion Associations + +**Sources:** Lundholm (1921); Aronoff et al. (1992); Larson et al. (2007); Uher (1991). + +| Contour Type | Emotional Association | Neural Basis | +|---|---|---| +| Smooth curves | Gentle, quiet, safe, affiliative, happy | No amygdala activation; fluent processing | +| Wavy lines | Affiliative adjectives (Uher 1991) | Low spatial frequency, easy to parse | +| Sharp angles | Agitating, hard, furious, threatening | Bilateral amygdala activation (Bar & Neta 2007) | +| Zigzag lines | Antagonistic adjectives (Uher 1991) | High spatial frequency, effortful processing | +| V-shapes | Anger (Aronoff et al. 1992; Larson et al. 2007) | Threat geometry (eyebrow configuration) | +| Rounded shapes | Happiness (Larson et al. 2007) | Approach motivation | + +**Processing fluency explanation:** Curves are preferred partly because they are computationally cheaper for the visual system. Smooth contours have lower spatial frequency — the visual cortex encodes them more economically, leading to faster mental rotation and improved visual search performance (Frontiers in Computer Science, 2024). Fluent processing → positive affect (the "beauty-in-averageness" / processing fluency pathway). + +### Context-Dependent Shape Meaning + +Like color emotion (Elliot & Maier 2012, Color-in-Context Theory), shape emotion is context-dependent: + +| Context | Angular = | Curved = | Source | +|---|---|---|---| +| Service environment (crowded) | Competence → higher satisfaction | — | Ohio State, news.osu.edu | +| Service environment (uncrowded) | — | Friendliness → higher satisfaction | Ohio State, news.osu.edu | +| Consumer power state: high | Preferred (competence signal) | — | Frontiers in Psychology 2021 | +| Consumer power state: low | — | Preferred (warmth signal) | Frontiers in Psychology 2021 | +| Professional/institutional setting | Structure, efficiency | Too casual | Architizer 2024 | +| Consumer/personal setting | Cold, impersonal | Inviting, warm | Architizer 2024 | + +**Implication for agents:** The optimal curvature is not fixed — it depends on the brand's target PAD profile, the audience power state, and the interaction context. The agent should compute curvature values that match the brand's target emotional profile, not default to maximum curvature. + +### Flat vs. Elevated Surfaces + +**Sources:** Interaction Design Foundation, Material Design documentation; Smashing Magazine (2017), "Using shadows and blur effects in UI design"; Kota.co.uk (2024), "The texture of trust." + +Flat design (no shadows, no gradients) and material design (elevation via shadows) represent two strategies for the same goal: communicating interactive hierarchy. + +| Strategy | Depth Mechanism | Trust Signal | Risk | +|---|---|---|---| +| Flat | Spacing + border contrast + surface tone | Modernity, premium, clarity | Loss of affordance cues (NNG: unlabeled elements, hidden actions) | +| Flat 2.0 | Subtle tone stepping + border emphasis | Clean but navigable | Requires disciplined surface hierarchy | +| Material | Drop shadows at computed elevation | Physical metaphor → intuitive | Visual noise if overused; complexity budget | + +**For flat design systems:** Without shadows, perceived depth is created entirely by **luminance stepping** between surface tiers. This is computable: + +``` +perceived_elevation = |L_surface − L_page_background| +``` + +The agent must ensure each surface tier has sufficient luminance difference to be perceptible. The Weber fraction for luminance discrimination is approximately 1–3% for the adapted observer. Surface steps should exceed this threshold. + +### Decision Framework + +Score each spatial decision against the brand's target PAD vector: + +| Dimension | What to Evaluate | Alignment Check | +|---|---|---| +| Curvature profile | Border-radius range across component types | Does curvature match target Pleasure level? | +| Spacing density | Base unit × scale factor | Does density match target Arousal level? | +| Border weight range | Min/max stroke widths | Does weight match target Dominance level? | +| Proportion system | Type scale ratio, layout ratios | Does scale contrast match hierarchy needs? | +| Surface hierarchy | Number of tiers, luminance stepping | Does depth match information architecture? | +| Symmetry strategy | Default symmetric vs. intentional asymmetry | Does layout energy match target Arousal? | +| Target sizing | Touch targets, interactive areas | Do sizes meet accessibility thresholds? | + +The human operator evaluates these qualitatively against brand intent. The agent can compute PAD-congruent token values. Once the geometric personality is chosen, everything below is math. + +--- + +## Phase 2: Spatial Engineering (Agent Math) + +From this point forward, the agent generates the spatial token system autonomously. The human provides perceptual feedback during validation. + +**IMPORTANT: The agent MUST write code to run the math then execute it, NEVER attempt to compute values directly. Strict mathematical adherence!** + +## Input: Brand Geometry Table + +The only human judgment calls. Everything downstream is computable math. + +| Parameter | Value | Rationale | +|---|---|---| +| Target Pleasure | Low / Medium / High | From brand PAD profile | +| Target Arousal | Low / Medium / High | From brand PAD profile | +| Target Dominance | Low / Medium / High | From brand PAD profile | +| Base spacing unit | 4px or 8px | 8px default; 4px for dense data UIs | +| Type scale ratio | 1.125–1.618 | See Proportion System section | +| Base font size | 14–18px | From typography decisions | +| Primary surface strategy | Flat / Flat 2.0 / Elevated | From design philosophy | + +## Spacing Scale Generation + +### Base Unit Selection + +The 8px base unit is the industry-converged standard (Google Material, Apple HIG, IBM Carbon, Shopify Polaris). It works because: + +1. **Divisibility:** 8 divides cleanly into common viewport widths (320, 360, 375, 390, 414, 768, 1024, 1440) +2. **Sub-pixel avoidance:** Halving yields 4px → 2px → 1px — all integers on 1×, 2×, and 3× displays +3. **Perceptual stepping:** 8px increments produce visible but not jarring differences at UI scale + +Use 4px base for dense UIs (data tables, dashboards, IDE-like interfaces) where finer control is needed. The 4px unit is a half-step within the 8px grid, not a separate system. + +### Scale Formula + +A modified geometric progression — not pure arithmetic (too uniform at large values) or pure geometric (non-integer values, gaps too large at small end): + +``` +spacing(step) = base × multiplier[step] + +Step: 0 1 2 3 4 5 6 7 8 9 10 +Multiplier: 0 1 2 3 4 6 8 10 12 16 20 +Value (8px): 0 8 16 24 32 48 64 80 96 128 160 +Value (4px): 0 4 8 12 16 24 32 40 48 64 80 +``` + +The multiplier progression: ×1, ×2, ×1.5, ×1.33, ×1.5, ×1.33, ×1.25, ×1.2, ×1.33, ×1.25. This compresses at small values (where absolute pixel differences are perceptible) and expands at large values (where proportional differences matter more). + +**Why not pure geometric (e.g., ×1.5 throughout)?** A strict 1.5× scale from 8px yields: 8, 12, 18, 27, 40.5... — non-integer values that cause sub-pixel rendering artifacts. The hybrid approach preserves integer multiples of the base unit while maintaining roughly proportional steps. + +### Spacing Density by Brand Arousal + +The spacing scale is the same for all brands. What changes is which steps are used for which purposes: + +| Target Arousal | Component padding | Section gap | Page margin | Emotional Effect | +|---|---|---|---|---| +| Low (calm) | step 3–4 (24–32px) | step 6–7 (64–80px) | step 8–9 (96–128px) | Generous space → low arousal, high pleasure | +| Medium (balanced) | step 2–3 (16–24px) | step 5–6 (48–64px) | step 7–8 (80–96px) | Moderate density → neutral arousal | +| High (energetic) | step 1–2 (8–16px) | step 4–5 (32–48px) | step 5–6 (48–64px) | Tight density → high arousal, high dominance | + +**Quantitative basis:** Generous whitespace around paragraphs increases reading comprehension by up to 20% (cognitive load research synthesized by Mapletree Studio 2024). Grid-based alignment improves usability ratings (Parallel HQ 2026). Tight spacing increases cognitive load but also increases perceived information density and system capability — valued in data-heavy professional contexts (NNG; MASTERCAWEB UX density research). + +### Gestalt Proximity Threshold + +**Source:** Kubovy, M., & van den Berg, M. (2008). "The whole is equal to the sum of its parts: A probabilistic model of grouping by proximity and similarity in regular patterns." *Psychological Review*. Springer (2017) — Tilt Aftereffect measurement of grouping strength: proximity produces larger perceptual grouping effects than color similarity. + +The visual system groups elements by proximity. The critical threshold is a **ratio**, not an absolute value: + +``` +within_group_spacing < between_group_spacing × 0.5 +``` + +If spacing within a group is less than half the spacing between groups, the visual system reliably perceives grouping. When the ratio approaches 1.0, grouping dissolves; elements are perceived as equidistant and ungrouped. + +**Token mapping for proximity-based grouping:** + +| Relationship | Spacing Step | Ratio to Within-Group | Example | +|---|---|---|---| +| Tightly related (within component) | step 1 (8px) | 1.0× (baseline) | Label-to-input, icon-to-text | +| Related (within group) | step 2 (16px) | 2.0× | Items in a list, form fields | +| Loosely related (between groups) | step 4–5 (32–48px) | 4–6× | Section to section, card groups | +| Unrelated (between sections) | step 6–8 (64–96px) | 8–12× | Page sections, major content areas | + +The ratio between adjacent tiers should be ≥ 2× for the grouping boundary to be perceptible. Ratios below 1.5× create ambiguous grouping. + +### Vertical Rhythm + +**Source:** Robert Bringhurst, *The Elements of Typographic Style* (2004); Gamma UX (2023), "Types of grids: The evolution toward the 4-point grid system." + +All vertical spacing should snap to multiples of the base unit to create a consistent visual rhythm. This is the typographic equivalent of rhythmic regularity in music — predictable patterns reduce cognitive effort. + +``` +line_height = ceil(font_size × line_height_ratio / base_unit) × base_unit + +Example: font_size = 16px, line_height_ratio = 1.5, base_unit = 8px + raw_value = 16 × 1.5 = 24px + snapped = ceil(24 / 8) × 8 = 24px ✓ + +Example: font_size = 14px, line_height_ratio = 1.5, base_unit = 8px + raw_value = 14 × 1.5 = 21px + snapped = ceil(21 / 8) × 8 = 24px (rounds up to grid) +``` + +Snapping ensures that every text block, every component, and every section boundary aligns to the grid. When elements sit on the grid, vertical scanning is effortless. When they drift off-grid, the eye perceives "something is wrong" even without conscious awareness. + +**Line-height ratios by context** (these feed into the snapping formula): + +| Context | Ratio | Rationale | Source | +|---|---|---|---| +| Body text (Latin) | 1.4–1.6× | Saccade return accuracy | Bringhurst 2004 | +| Body text (CJK, Thai, Devanagari) | 1.6–1.8× | Stroke density + diacritical clearance | COLOR_GUIDE.md Phase 3 | +| Headings (display) | 1.1–1.2× | Tight for visual cohesion at large sizes | Convention | +| Code blocks | 1.3–1.5× | Monospace alignment | Convention | +| Captions, metadata | 1.3–1.4× | Compact but readable | Convention | + +## Border Radius System + +### Curvature as Computed Emotional Lever + +Border radius modulates the curved–angular continuum from Phase 1. The agent computes radius values from the brand's target Pleasure level, the element's semantic role, and the element's size. + +**Normalized curvature metric:** + +``` +curvature_ratio(r, width, height) = r / (min(width, height) / 2) + + 0.0 = sharp rectangle (maximum angularity) + 0.0–0.15 = subtle rounding + 0.15–0.35 = moderate rounding + 0.35–0.50 = strong rounding + 0.50 = pill/stadium shape (maximum curvature for non-square rectangle) + 1.0 = circle (square element with r = 50%) +``` + +### Curvature by Brand Pleasure Target + +| Target Pleasure | Default curvature_ratio | Pixel Range (for 40px-tall element) | Emotional Signal | +|---|---|---|---| +| Low (austere) | 0.0–0.05 | 0–1px | Sharp, institutional, authoritative | +| Medium (balanced) | 0.10–0.20 | 2–4px | Professional, approachable, structured | +| High (warm) | 0.25–0.40 | 5–8px | Friendly, inviting, consumer-facing | +| Maximum (playful) | 0.50 | pill | Casual, youthful, high warmth | + +### Curvature by Semantic Role + +Semantic role overrides brand default when congruence demands it. Error states should be sharper (threat congruence); success states can be rounder (positive valence congruence). + +**Source for congruence penalty:** Fox, D., Shaikh, A. D., & Chaparro, B. S. (2007). "Effect of typeface appropriateness on the perception of documents." Measured 22% credibility loss for incongruent typography. The same principle applies to shape-emotion congruence. + +| Element Role | Curvature Adjustment | Rationale | +|---|---|---| +| Error/danger states | −50% from default (sharper) | Angular = threat congruent (Bar & Neta 2007) | +| Warning states | −25% from default | Mild threat signal | +| Success/positive states | +25% from default (rounder) | Curved = positive valence congruent (Leder et al. 2011) | +| Neutral/informational | Brand default | No semantic override needed | +| Avatars, user photos | 50% (circle) | Maximum warmth for human representation | +| Input fields | −25% from default | Structure signals "fill this in" | + +**Formula:** + +``` +element_radius(element_role, brand_default_ratio, element_height) = + let adjustment = semantic_adjustment[element_role] // -0.5, -0.25, 0, +0.25 + let adjusted_ratio = clamp(brand_default_ratio × (1 + adjustment), 0, 0.5) + return round(adjusted_ratio × (element_height / 2)) +``` + +### Radius Scale (Token Output) + +Rather than computing per-element, generate a radius token scale and assign tokens semantically: + +``` +radius_scale(brand_pleasure, base_unit) = + let base_r = base_unit × pleasure_multiplier[brand_pleasure] + return [0, base_r × 0.5, base_r, base_r × 1.5, base_r × 2, base_r × 3, 9999] + +pleasure_multiplier: + low = 0.5 → base_r = 4px (with 8px base unit) + medium = 1.0 → base_r = 8px + high = 1.5 → base_r = 12px +``` + +**Token naming:** + +| Token | Formula | Low Pleasure | Medium Pleasure | High Pleasure | +|---|---|---|---|---| +| `--radius-none` | 0 | 0px | 0px | 0px | +| `--radius-sm` | base_r × 0.5 | 2px | 4px | 6px | +| `--radius-md` | base_r | 4px | 8px | 12px | +| `--radius-lg` | base_r × 1.5 | 6px | 12px | 18px | +| `--radius-xl` | base_r × 2 | 8px | 16px | 24px | +| `--radius-2xl` | base_r × 3 | 12px | 24px | 36px | +| `--radius-full` | 9999px | pill | pill | pill | + +## Line Weight System + +### Border Weight as Dominance Lever + +Line weight operates on the **Dominance** axis — analogous to font weight (see COLOR_GUIDE.md Phase 1 § Font Weight as Independent Emotional Lever). Thicker borders command attention and assert structural authority. Thinner borders recede and defer. + +**Source:** WordPress 5.3 accessibility audit (make.wordpress.org, 2019). Documented measurable improvements in element discoverability when borders shifted from `1px solid #ddd` (low contrast, thin) to `1px solid #7e8993` (adequate contrast). The variable is contrast × thickness jointly — a high-contrast 1px border can outweigh a low-contrast 2px border. + +| Border Weight | Dominance Level | Perceptual Role | Use | +|---|---|---|---| +| 0px | Lowest | Invisible boundary — relies entirely on spacing/color | Open cards, flush layouts, borderless surfaces | +| 1px | Low | Structural definition without commanding | Default borders, input fields, separators, table rules | +| 2px | Medium | Emphasis — draws attention to boundary | Active/selected states, section breaks, focused inputs | +| 3px | Medium-High | Accent — semantic signal carrier | Callout left-borders, active tab indicators, progress bars | +| 4px+ | High | Maximum structural dominance | Section dividers, hero element boundaries, decorative rules | + +### Border Weight × Font Weight Congruence + +Line weight and font weight should be congruent on the Dominance axis. Mismatched weight levels create the same perceptual confusion documented for font-color incongruence (Fox et al. 2007, ~22% credibility loss): + +| Target Dominance | Font Weight | Border Weight | Curvature | Combined Signal | +|---|---|---|---|---| +| Low (elegant, delicate) | 300–400 | 0–1px | High radius | Approachable, refined, airy | +| Medium (professional) | 400–500 | 1–2px | Medium radius | Balanced, structured, readable | +| High (authoritative) | 600–700 | 2–3px | Low radius | Commanding, institutional, assertive | +| Maximum (impact) | 800–900 | 3–4px | Minimal radius | Bold, urgent, declarative | + +### Border Density Budget + +**Observation from user research:** Excessive borders ("lines lines lines everywhere") increase cognitive load and decrease pleasure. This parallels the chroma budget concept in COLOR_GUIDE.md Phase 3 (body text C < 0.04) and the cognitive load limits (max 2–3 emphasis colors, max 6–8 categorical colors). + +``` +borders_per_viewport_section ≤ 4 visible structural lines +``` + +Beyond approximately 4 visible borders in a single viewport section, perceived density spikes and pleasure drops. Mitigation strategies: +- Use **spacing** (Gestalt proximity) instead of borders when possible +- Use **surface tone changes** (luminance stepping) instead of borders for container definition +- Reserve borders for elements that need explicit boundary definition (inputs, tables, separators) + +### Border Contrast Rule + +A border's visual weight is contrast × thickness. A 1px border at high contrast (neutral-200 on white = ΔL ~0.04) is less prominent than a 1px border at maximum contrast (neutral-900 on white = ΔL ~0.72) but both are "1px." + +``` +perceived_border_weight = thickness_px × |L_border − L_background| +``` + +The agent should compute perceived weight, not just pixel thickness. Token assignment should specify both thickness and color: + +| Token | Thickness | Color (Light) | Color (Dark) | Perceived Weight | +|---|---|---|---|---| +| `--border-subtle` | 1px | neutral-200 | neutral-800 | ~0.04 | +| `--border-default` | 1px | neutral-300 | neutral-700 | ~0.10 | +| `--border-emphasis` | 1px | neutral-500 | neutral-500 | ~0.20 | +| `--border-strong` | 2px | neutral-700 | neutral-300 | ~0.72 | +| `--border-accent` | 3px | semantic color | semantic color | Variable (semantic) | + +## Surface Hierarchy + +### Luminance-Stepped Elevation (Flat Systems) + +Without shadows, perceived depth is created exclusively by luminance differences between surface tiers. The agent computes surface luminance values that exceed the perceptual discrimination threshold. + +**Perceptual threshold:** The Weber fraction for luminance discrimination is approximately 1–3% for the adapted observer at typical screen luminance (100–300 cd/m²). Surface steps must exceed this threshold to be perceivable. + +``` +ΔL_minimum = 0.03 (3% Weber fraction — just noticeable) +ΔL_comfortable = 0.04–0.06 (clearly distinct without being jarring) +``` + +### Surface Tier Formula + +Three tiers minimum (page, raised, muted). Each tier's lightness is offset from the page background: + +``` +Light mode (page L = 1.0): + surface_raised = L_page − ΔL_step_1 // e.g., 1.0 − 0.04 = 0.96 + surface_muted = L_page − ΔL_step_1 − ΔL_step_2 // e.g., 0.96 − 0.05 = 0.91 + +Dark mode (page L = 0.11): + surface_raised = L_page + ΔL_step_1 // e.g., 0.11 + 0.02 = 0.13 + surface_muted = L_page + ΔL_step_1 + ΔL_step_2 // e.g., 0.13 + 0.11 = 0.24 +``` + +Dark mode uses smaller initial steps because the Weber fraction increases at low luminance (the visual system is less sensitive to absolute differences in dark ranges). Subsequent steps can be larger. + +### Surface Tiers by Brand Dominance + +Higher dominance brands use **more contrast between tiers** (bolder surface differentiation). Lower dominance brands use **subtler stepping** (less visual assertiveness): + +| Target Dominance | ΔL between tiers (light) | ΔL between tiers (dark) | Emotional Effect | +|---|---|---|---| +| Low | 0.02–0.03 | 0.01–0.02 | Subtle, minimal, premium | +| Medium | 0.04–0.05 | 0.02–0.04 | Clear but not commanding | +| High | 0.06–0.08 | 0.04–0.06 | Bold surface distinction | + +### The 60-30-10 Surface Distribution + +**Source:** Johannes Itten, *The Art of Color* (1961); applied to UI by Material Design. + +The proportion of each surface tier controls visual rhythm and accent impact: + +| Proportion | Surface Role | Emotional Effect | +|---|---|---| +| 60% | Page background (dominant surface) | Sets base arousal — low chroma, extreme L = calm | +| 30% | Raised/muted surfaces (secondary) | Creates structure, rhythm, moderate contrast | +| 10% | Accent (semantic color, borders, interactive) | Draws attention — high arousal and dominance | + +For content-heavy pages (documentation, articles), use 95/5 — nearly all page background with minimal accent. For diagram-heavy or interactive pages, use 60-30-10. The agent should compute based on content type. + +## Proportion System + +### Modular Type Scale + +**Sources:** Robert Bringhurst, *The Elements of Typographic Style* (2004); Spencer Mortensen's modular scale theory. + +A modular scale generates harmonious font sizes from a base size and a ratio. The ratio determines hierarchical contrast — how dramatically headings differ from body text. + +``` +font_size(step) = base_size × ratio^step +``` + +**Ratios and their emotional profiles:** + +| Ratio | Musical Name | Hierarchy Contrast | Emotional Profile | Best For | +|---|---|---|---|---| +| 1.067 | Minor Second | Minimal | Subtle, dense, bureaucratic | Dense data UIs | +| 1.125 | Major Second | Low | Reserved, professional, understated | Text-heavy apps, documentation | +| 1.200 | Minor Third | Moderate | Balanced, readable, workmanlike | Technical content, body-heavy sites | +| 1.250 | Major Third | Moderate+ | Confident, clear, structured | Docs + marketing hybrid | +| 1.333 | Perfect Fourth | Strong | Authoritative, editorial, dramatic | Magazine layouts, editorial sites | +| 1.414 | Augmented Fourth | Strong+ | Assertive, dynamic, high-contrast | Presentation slides, hero sections | +| 1.500 | Perfect Fifth | High | Bold, declarative, commanding | Marketing landing pages | +| 1.618 | Golden Ratio | Maximum | Monumental, dramatic, display | Display-only typography | + +### Scale Ratio × Brand Congruence + +The type scale ratio should align with the brand's target PAD profile: + +| Target Profile | Congruent Scale | Rationale | +|---|---|---| +| Calm, trust (low A, low D) | 1.125–1.200 | Minimal contrast = low arousal, low dominance | +| Balanced, professional (mid all) | 1.200–1.250 | Moderate contrast = balanced PAD | +| Bold, authoritative (high D) | 1.333–1.500 | Strong contrast = high dominance | +| Playful, energetic (high A, high P) | 1.250–1.333 | Moderate-strong contrast with curvature | + +### Font Size Output + +Given base_size and ratio, generate the scale: + +``` +for step in [-2, -1, 0, 1, 2, 3, 4, 5]: + raw = base_size × ratio^step + snapped = round(raw) // integer pixels for sub-pixel avoidance + line_height = ceil(snapped × lh_ratio / base_unit) × base_unit // grid snap +``` + +**Token naming convention:** + +| Step | Token | Role | +|---|---|---| +| -2 | `--text-xs` | Fine print, legal, captions | +| -1 | `--text-sm` | Secondary text, metadata | +| 0 | `--text-base` | Body text (base_size) | +| 1 | `--text-lg` | Lead paragraphs, emphasized body | +| 2 | `--text-xl` | h4, subheadings | +| 3 | `--text-2xl` | h3, section headings | +| 4 | `--text-3xl` | h2, major headings | +| 5 | `--text-4xl` | h1, page titles | + +### Layout Proportions + +**Source:** Interaction Design Foundation, Golden Ratio in design; Kurosu & Kashimura (1995), "Apparent usability vs. inherent usability." *CHI '95*. + +The golden ratio (φ = 1.618) and related proportions guide layout division: + +``` +content_width / sidebar_width ≈ φ (1.618:1 or ~62%:38%) +``` + +**The aesthetic-usability effect:** Tractinsky, Katz, & Ikar (2000) replicated Kurosu & Kashimura's finding across cultures — proportionally harmonious layouts are rated as more usable even when functionality is identical. Φ-proportioned layouts benefit from this effect. + +**Layout proportion tokens:** + +| Ratio | Division | Use | +|---|---|---| +| 1:1 | 50%:50% | Equal-weight comparison layouts | +| φ:1 (1.618:1) | ~62%:38% | Content + sidebar (golden section) | +| 2:1 | ~67%:33% | Content + narrow sidebar | +| 3:1 | 75%:25% | Content-dominant with metadata column | + +### Symmetry Strategy + +**Source:** Frontiers in Psychology (2016). Comparative eye-tracking study on symmetric pattern perception. Symmetric designs increase fixation duration (longer engagement) and score higher on pleasantness. Asymmetric layouts increase arousal (dynamic tension). + +| Target Arousal | Layout Strategy | Effect | +|---|---|---| +| Low (calm, orderly) | Symmetric (centered, equal columns) | High pleasure, low arousal | +| Medium (balanced) | Mostly symmetric with focal asymmetry | Moderate arousal, focused attention | +| High (dynamic, energetic) | Intentional asymmetry (off-center, unequal columns) | High arousal, tension | + +**Default:** Symmetric grid. Use asymmetry only for intentional emphasis — e.g., off-center hero text, asymmetric content:sidebar ratios, staggered card layouts. + +## Target Size & Interactive Geometry + +### Fitts' Law + +**Source:** Fitts, P. M. (1954). "The information capacity of the human motor system in controlling the amplitude of movement." *Journal of Experimental Psychology*, 47(6), 381–391. + +``` +MT = a + b × log₂(2D/W + 1) + +MT = movement time (ms) +D = distance from current pointer position to target center +W = target width along movement axis +a = intercept constant (device-dependent, ~50ms for mouse) +b = slope constant (device-dependent, ~150ms for mouse) +``` + +The logarithmic relationship means: doubling target size yields diminishing returns. The largest gains come from making small targets bigger. Once targets exceed ~48px, further size increases provide minimal speed improvement. + +### Minimum Target Sizes (Converged Standards) + +| Standard | Minimum Size | Physical Size | Context | +|---|---|---|---| +| WCAG 2.2 SC 2.5.8 (AA) | 24 × 24 CSS px | ~6.4mm | Absolute minimum for compliance | +| WCAG 2.1 SC 2.5.5 (AAA) | 44 × 44 CSS px | ~11.7mm | Recommended for accessible interfaces | +| Apple iOS HIG | 44 × 44 pt (~59px) | ~11.7mm | Apple ecosystem | +| Android Material | 48 × 48 dp | ~9mm (finger pad) | Android ecosystem | +| NNG recommendation | — | 10 × 10mm | Universal physical size | +| Apple Vision Pro | 60 × 60 pt (~80px) | — | Spatial computing (eye-tracking) | + +**Minimum spacing between adjacent targets:** 8px (Android guideline). This prevents adjacent-tap errors and provides visual separation per Gestalt proximity. + +### Size Hierarchy Rule + +Interactive elements should have a size hierarchy that matches their importance: + +``` +primary_action_height ≥ secondary_action_height × 1.25 +secondary_action_height ≥ tertiary_action_height × 1.15 +``` + +This creates a natural visual weight hierarchy driven by Fitts' law (larger = faster to acquire = more important) without relying on color differentiation. + +**Token output:** + +| Token | Height | Padding (horizontal) | Use | +|---|---|---|---| +| `--target-sm` | 32px | step 2 (16px) | Tertiary actions, inline buttons, tags | +| `--target-md` | 40px | step 3 (24px) | Secondary actions, form inputs | +| `--target-lg` | 48px | step 4 (32px) | Primary actions, main CTAs | +| `--target-xl` | 56px | step 5 (48px) | Hero CTAs, prominent actions | + +### Content Width & Line Length + +**Source:** Bringhurst (2004); WCAG SC 1.4.8. See also COLOR_GUIDE.md Phase 3 § Typography Layout and Color Fatigue. + +``` +Optimal line length: 45–75 characters (66 ideal for Latin script) +Implementation: max-width: 66ch on text containers +CJK: max-width: 40ch (WCAG SC 1.4.8) +``` + +The `ch` unit is relative to the width of the "0" glyph in the current font. This automatically adapts line length to font choice. + +**Color fatigue interaction (from COLOR_GUIDE.md):** High contrast + long lines (>75ch) increases saccade fatigue. If line length exceeds 75ch and cannot be reduced, reduce contrast target from |Lc| 90 to |Lc| 80 for body text. + +## Congruence Validation + +### Three-Axis Alignment Check + +Every design decision should be congruent across all three perceptual axes: color, typography, and spatial geometry. The agent should validate alignment after generating all tokens: + +``` +For each UI component: + color_PAD = PAD vector from color properties (hue, chroma, lightness) + type_PAD = PAD vector from typography properties (weight, classification, size) + spatial_PAD = PAD vector from spatial properties (radius, spacing, border weight, size) + + congruence_check: + |color_PAD.pleasure − spatial_PAD.pleasure| ≤ 1 tier // e.g., both "medium" or adjacent + |type_PAD.dominance − spatial_PAD.dominance| ≤ 1 tier + |color_PAD.arousal − spatial_PAD.arousal| ≤ 1 tier +``` + +### Congruence Examples + +| Target Emotion | Color | Typography | Spatial | Congruent? | +|---|---|---|---|---| +| Calm Trust | Low chroma, cool hue | 400 weight, humanist sans | 8–12px radius, generous spacing, 1px borders | Yes — all low A, low D, high P | +| Calm Trust | Low chroma, cool hue | 400 weight, humanist sans | 0px radius, tight spacing, 3px borders | **No** — spatial signals high D, high A | +| Urgent Authority | High chroma, warm hue | 700 weight, geometric sans | 0–4px radius, tight spacing, 2–3px borders | Yes — all high A, high D | +| Urgent Authority | High chroma, warm hue | 700 weight, geometric sans | 16px radius, generous spacing, 0px borders | **No** — spatial signals high P, low D | +| Friendly Innovation | Med chroma, cyan/green | 400–500, humanist sans | 12–16px radius, moderate spacing, 1px | Yes — balanced A, high P | +| Technical Precision | Low chroma, neutral | 400, monospace + geometric | 4–6px radius, grid-even spacing, 1px | Yes — low A, med D, med P | +| Premium Elegance | Low chroma, deep hue | 300 weight, didone serif | 0–2px radius, very generous spacing, 0–1px | Yes — low A, low D, high P | + +### Incongruence Red Flags + +Flag and report these mismatches to the human operator: + +- High curvature (pill buttons) + heavy font weight (800+) → spatial says "warm," type says "commanding" +- Generous whitespace + high chroma accent overuse → spatial says "calm," color says "urgent" +- Angular geometry (0px radius) + light font weight (300) → spatial says "institutional," type says "delicate" +- Tight spacing + low information density → arousal signal without content to justify it (wasted tension) +- Multiple surface tiers + minimal content per tier → dominance signal without hierarchy to communicate + +## Validation Checklist + +For every generated spatial system, verify: + +**Spacing:** +- [ ] Spacing scale generated with all steps as integer multiples of base unit +- [ ] All spacing values are whole pixels (no sub-pixel rendering) +- [ ] Within-group spacing < between-group spacing × 0.5 (Gestalt proximity threshold) +- [ ] Component padding and section gaps assigned from appropriate scale steps +- [ ] Vertical rhythm: all line-heights snap to base unit multiples + +**Border radius:** +- [ ] Radius scale generated from brand pleasure target +- [ ] Semantic role adjustments applied (error sharper, success rounder) +- [ ] No radius exceeds min(width, height) / 2 for any element +- [ ] Radius tokens named and mapped to component roles + +**Line weight:** +- [ ] Border weight range matches brand dominance target +- [ ] Border weight congruent with font weight selection (±1 dominance tier) +- [ ] Border density ≤ 4 structural lines per viewport section +- [ ] All borders have sufficient contrast: perceived_weight > 0.03 +- [ ] Border color tokens paired with thickness tokens + +**Surfaces:** +- [ ] Luminance step between adjacent tiers ≥ Weber fraction minimum (ΔL ≥ 0.03 light, ≥ 0.01 dark) +- [ ] Surface distribution follows 60-30-10 (or 95-5 for content pages) +- [ ] Dark mode surface values computed (not copied from light mode) +- [ ] No pure black (#000000) backgrounds; no pure white (#ffffff) body text on dark + +**Proportions:** +- [ ] Type scale ratio selected and congruent with brand PAD profile +- [ ] Font sizes generated, rounded to integers, with grid-snapped line-heights +- [ ] Layout proportions defined (content:sidebar ratios) +- [ ] Content containers have max-width set (66ch Latin, 40ch CJK) + +**Target sizes:** +- [ ] All interactive elements ≥ 24×24 CSS px (WCAG 2.2 AA minimum) +- [ ] Primary actions ≥ 44×44 CSS px (WCAG AAA target) +- [ ] Adjacent interactive elements have ≥ 8px gap +- [ ] Size hierarchy: primary > secondary × 1.25 + +**Congruence:** +- [ ] Spatial PAD vector within ±1 tier of color PAD vector on all dimensions +- [ ] Spatial PAD vector within ±1 tier of typography PAD vector on all dimensions +- [ ] No red flags from incongruence check +- [ ] Semantic role overrides documented and justified + +--- + +## References + +### Shape & Curvature Perception +- **Bar, M., & Neta, M.** — "Humans prefer curved visual objects" (*Psychological Science*, 2006, 17(8), 645–648). Curved stimuli preferred even at 84ms subliminal exposure. +- **Bar, M., & Neta, M.** — "Visual elements of subjective preference modulate amygdala activation" (*Neuropsychologia*, 2007, 45(10), 2191–2200). fMRI: sharp contours activate amygdala bilaterally; curved do not. +- **Gómez-Puerto, G., Munar, E., & Nadal, M.** — "Preference for curvature: A historical and conceptual framework" (*Frontiers in Human Neuroscience*, 2015, 9:712). Comprehensive review of curvature preference across ages, cultures, species. +- **Leder, H., Tinio, P. P., & Bar, M.** — "Emotional valence modulates the preference for curved objects" (*Perception*, 2011, 40(6), 649–655). Curvature preference only significant for positive/neutral valence objects. +- **Silvia, P. J., & Barona, C. M.** — "Do people prefer curved objects? Angularity, expertise, and aesthetic preference" (*Empirical Studies of the Arts*, 2009, 27(1), 25–42). Expertise moderates curvature preference. +- **Bertamini, M., Palumbo, L., Gheorghes, T. N., & Galatsidas, M.** — "Do observers like curvature or do they dislike angularity?" (*British Journal of Psychology*, 2015). Approach/avoidance behavioral measurement of curvature preference. +- **Fantz, R. L., & Miranda, S. B.** — "Newborn infant attention to form of contour" (*Child Development*, 1975). Neonates fixate longer on curved contours. +- **Lundholm, H.** — "The affective tone of lines" (*Psychological Review*, 1921). Curved = gentle/quiet; angular = agitating/hard/furious. +- **Aronoff, J., Barclay, A. M., & Stevenson, L. A.** — "The recognition of threatening facial stimuli" (*Journal of Personality and Social Psychology*, 1992). V-shapes → anger association. +- **Larson, C. L., Aronoff, J., & Stearns, J. J.** — "The shape of threat: Simple geometric forms evoke rapid and sustained capture of attention" (*Emotion*, 2007). Angular → anger; rounded → happiness. +- **Uher, J.** — (1991). On zigzag lines: antagonistic adjectives. On wavy lines: affiliative adjectives. +- **UXPA Journal** — "Rounded aesthetic beauty and warmth" (2024). N=187, between-subjects. Rounded corners → higher aesthetics, warmth, ease of use, satisfaction. +- **Ohio State University** — "Curves or angles: Shapes in businesses affect customer response" (news.osu.edu). Context-dependent shape preference: angular = competence in crowded settings; curved = warmth in uncrowded settings. +- **Frontiers in Psychology** — "The matching effect of consumer power state and shape preference" (2021). High-power consumers prefer angular; low-power prefer rounded. + +### Spacing, Grid & Proximity +- **Kubovy, M., & van den Berg, M.** — "The whole is equal to the sum of its parts: A probabilistic model of grouping by proximity and similarity in regular patterns" (*Psychological Review*, 2008). Mathematical model of proximity-based grouping. +- **Springer** — "Objective measurement of Gestalts using Tilt Aftereffect" (*Behavior Research Methods*, 2017). Proximity produces larger TAE (stronger grouping) than color similarity. +- **Wagemans, J., et al.** — "A century of Gestalt psychology in visual perception" (*Psychological Bulletin*, 2012, 138(6), 1172–1217). Comprehensive review of Gestalt principles. +- **Bringhurst, R.** — *The Elements of Typographic Style* (Hartley & Marks, 2004). Modular scales, vertical rhythm, line length (45–75 characters). +- **Gamma UX** — "Types of grids: The evolution toward the 4-point grid system" (2023). 4pt and 8pt grid systems, baseline alignment. +- **Mapletree Studio** — "The Psychology Behind Clean Website Design" (2024). Whitespace increases comprehension up to 20%. +- **Parallel HQ** — "Improving Visual Hierarchy" (2026). Grid-based alignment improves usability ratings. +- **MASTERCAWEB** — "Minimalism versus density in UI and UX" (Université de Strasbourg). Cultural factors: density signals capability in East Asian markets; minimalism signals premium in Western markets. +- **Nielsen Norman Group** — "4 Principles to Reduce Cognitive Load" (2024). Single-column layouts outperform multi-column for form completion. + +### Proportion & Visual Balance +- **Kurosu, M., & Kashimura, K.** — "Apparent usability vs. inherent usability" (*CHI '95 Conference Companion*, 1995). Aesthetically proportioned interfaces rated more usable. +- **Tractinsky, N., Katz, A. S., & Ikar, D.** — "What is beautiful is usable" (*Interacting with Computers*, 2000, 13(2), 127–145). Cross-cultural replication of aesthetic-usability effect. +- **Itten, J.** — *The Art of Color* (Reinhold Publishing, 1961). 60-30-10 proportion rule. +- **Frontiers in Psychology** — "Visual perception of symmetric patterns in humans and orangutans" (2016). Eye-tracking: symmetric patterns increase fixation duration. +- **Frontiers in Human Neuroscience** — "Visual saliency and pictorial balance in photographic cropping" (2015). Saliency center-of-mass closer to geometrical center in preferred compositions. + +### Target Size & Motor Performance +- **Fitts, P. M.** — "The information capacity of the human motor system in controlling the amplitude of movement" (*Journal of Experimental Psychology*, 1954, 47(6), 381–391). Foundational motor performance model. +- **ISO 9241-411** — Ergonomics of human-system interaction: Evaluation methods for the design of physical input devices. +- **WCAG 2.2 SC 2.5.8** — Target Size (Minimum): 24 × 24 CSS pixels (AA level). +- **WCAG 2.1 SC 2.5.5** — Target Size: 44 × 44 CSS pixels (AAA level). +- **Apple Human Interface Guidelines** — 44 × 44 pt minimum for iOS; 60 × 60 pt for visionOS. +- **Android Material Design** — 48 × 48 dp minimum touch target; 8dp spacing between targets. +- **Nielsen Norman Group** — "Touch targets on touchscreens" (2019). 10 × 10mm recommended physical size. +- **Lindgaard, G., Fernandes, G., Dudek, C., & Brown, J.** — "Attention web designers: You have 50 milliseconds to make a good first impression!" (*Behaviour & Information Technology*, 2006, 25(2), 115–126). First impressions formed in ~50ms, dominated by visual design. +- **Fogg, B. J., et al.** — Stanford Web Credibility Research (2003). N=2,684: design rated more important than any other feature for credibility. + +### Visual Weight & Hierarchy +- **Fox, D., Shaikh, A. D., & Chaparro, B. S.** — "Effect of typeface appropriateness on the perception of documents" (2007). 22% credibility loss for incongruent typography. Applicable to shape-emotion congruence. +- **WordPress Core** — "Noteworthy admin CSS changes in WordPress 5.3" (make.wordpress.org, 2019). Border contrast improvements for accessibility. +- **Smashing Magazine** — "Using shadows and blur effects in UI design" (2017). Elevation via shadows for interactive hierarchy. +- **Interaction Design Foundation** — Material Design documentation. Elevation, z-axis, and shadow computation. +- **Kota.co.uk** — "The texture of trust: How visual tactility sells online" (2024). Flat vs. elevated design trust perception. + +### Color Emotion (Cross-Referenced from COLOR_GUIDE.md) +- **Wilms, L., & Oberfeld, D.** — "Color and emotion" (*Psychological Research*, 2018). Saturation: dominant arousal lever (η² = .693). +- **Elliot, A. J., & Maier, M. A.** — "Color-in-context theory" (*Advances in Experimental Social Psychology*, 2012). Context modulates color emotion. +- **Valdez, P., & Mehrabian, A.** — "Effects of color on emotions" (*J. Experimental Psychology: General*, 1994). Brightness → pleasure; saturation → arousal. + +### Cognitive Load & Information Processing +- **SHIFT eLearning** — Color schemes amplify learning by 55–78%; cognitive overload from too many bright colors degrades comprehension. +- **Nature Scientific Reports** — "Optimizing waiting experience" (2025). Quantitative study: interface element density × animated indicators affect emotional experience and time perception. diff --git a/agentic-coding-brand-proposal.html b/agentic-coding-brand-proposal.html new file mode 100644 index 0000000..32f0892 --- /dev/null +++ b/agentic-coding-brand-proposal.html @@ -0,0 +1,2626 @@ + + + + + +Agentic Coding — Monochrome Brand System + + + + + +
+

Agentic Coding — Monochrome Brand System

+

Monochrome-first identity · 9 semantic accent colors · OKLCH-computed · WCAG AA validated · Geometry as brand

+
+ + +
+

Competitor Color Landscape

+
+ +
+
+
+
GitHub Copilot
+
270°
+
+
+ +
+
+
+
Cursor
+
280°
+
+
+ +
+
+
+
Windsurf/Codeium
+
170°
+
+
+ +
+
+
+
Replit
+
30°
+
+
+ +
+
+
+
OpenAI/ChatGPT
+
160°
+
+
+ +
+
+
+
Anthropic/Claude
+
30°
+
+
+ +
+
+
+
JetBrains AI
+
280°
+
+
+ +
+
+
+
Tabnine
+
260°
+
+
+ +
+
+
+
Sourcegraph Cody
+
280°
+
+
+ +
+
+
+
Pluralsight
+
330°
+
+
+ +
+
+
+
DeepLearning.AI
+
10°
+
+
+ +
+
+
+
Frontend Masters
+
+
+
+ +
+
+
+
Agentic Coding (Ours)
+
Monochrome — No primary hue
+
+
+
+

+ Every competitor owns a hue — we own the absence of one. Monochrome IS the most distinctive position on this map. + Per Sharp's framework, when every brand shouts color, restraint is the signal. Purple (260-290°) is particularly crowded — Copilot, Cursor, JetBrains, Tabnine, Sourcegraph all share it. Rather than compete for hue territory, we vacate it entirely. +

+
+ + +
+

Semantic Color Palette (OKLCH)

+

+ One achromatic neutral scale plus 9 chromatic hues at equal standing. No "primary" or "accent" hierarchy — each hue serves a semantic role in illustrations, diagrams, and callouts. All computed via OKLCH with WCAG AA validation. +

+ +
+
Neutral — Achromatic (C:0.000)
+
+
+
50
+
#f5f5f5
+
+
+
100
+
#e8e8e8
+
+
+
200
+
#d4d4d4
+
+
+
300
+
#b7b7b7
+
+
+
400
+
#9b9b9b
+
+
+
500
+
#808080
+
+
+
600
+
#666666
+
+
+
700
+
#505050
+
+
+
800
+
#3d3d3d
+
+
+
900
+
#2b2b2b
+
+
+
950
+
#222222
+
+
+
+ +
+
Error — H:25° C:0.16 · Danger / Critical / Destructive
+
+
+
50
+
#fff2f0
+
+
+
100
+
#ffdfdc
+
+
+
200
+
#ffc3bd
+
+
+
300
+
#ff958d
+
+
+
400
+
#ec7069
+
+
+
500
+
#ce514d
+
+
+
600
+
#ad3735
+
+
+
700
+
#8d2324
+
+
+
800
+
#701719
+
+
+
900
+
#520e10
+
+
+
950
+
#410b0c
+
+
+
+ +
+
Warning — H:70° C:0.13 · Caution / Attention / Deprecation
+
+
+
50
+
#fff3e6
+
+
+
100
+
#ffe3c3
+
+
+
200
+
#fcca91
+
+
+
300
+
#e6aa63
+
+
+
400
+
#cd8c37
+
+
+
500
+
#b17000
+
+
+
600
+
#8e5900
+
+
+
700
+
#704500
+
+
+
800
+
#573400
+
+
+
900
+
#402400
+
+
+
950
+
#331b00
+
+
+
+ +
+
Lime — H:110° C:0.14 · Progress / Growth / Output
+
+
+
50
+
#f7fac9
+
+
+
100
+
#eaedb0
+
+
+
200
+
#d8da8d
+
+
+
300
+
#bcbe5c
+
+
+
400
+
#a1a22b
+
+
+
500
+
#868600
+
+
+
600
+
#6b6b00
+
+
+
700
+
#535400
+
+
+
800
+
#404000
+
+
+
900
+
#2e2e00
+
+
+
950
+
#242400
+
+
+
+ +
+
Success — H:155° C:0.14 · Active / Validated / Complete
+
+
+
50
+
#ddffe8
+
+
+
100
+
#bef8d1
+
+
+
200
+
#9fe8b8
+
+
+
300
+
#72ce95
+
+
+
400
+
#48b475
+
+
+
500
+
#1c985a
+
+
+
600
+
#007a44
+
+
+
700
+
#006034
+
+
+
800
+
#004a27
+
+
+
900
+
#00361a
+
+
+
950
+
#002a13
+
+
+
+ +
+
Cyan — H:195° C:0.145 · Interactive / System / Code
+
+
+
50
+
#d4fffe
+
+
+
100
+
#a5faf9
+
+
+
200
+
#7aeae9
+
+
+
300
+
#2ad0d0
+
+
+
400
+
#00b2b2
+
+
+
500
+
#009393
+
+
+
600
+
#007576
+
+
+
700
+
#005c5c
+
+
+
800
+
#004747
+
+
+
900
+
#003333
+
+
+
950
+
#002828
+
+
+
+ +
+
Indigo — H:250° C:0.14 · Knowledge / Reference / Data
+
+
+
50
+
#eef6ff
+
+
+
100
+
#d7eaff
+
+
+
200
+
#b4d8ff
+
+
+
300
+
#7cbdff
+
+
+
400
+
#53a0ec
+
+
+
500
+
#3284d0
+
+
+
600
+
#1369b0
+
+
+
700
+
#005190
+
+
+
800
+
#003e71
+
+
+
900
+
#002c54
+
+
+
950
+
#002242
+
+
+
+ +
+
Violet — H:285° C:0.14 · AI / Transformation / Synthesis
+
+
+
50
+
#f4f4ff
+
+
+
100
+
#e5e5ff
+
+
+
200
+
#cfcfff
+
+
+
300
+
#b0adff
+
+
+
400
+
#938eeb
+
+
+
500
+
#7971d0
+
+
+
600
+
#6057af
+
+
+
700
+
#4b4290
+
+
+
800
+
#393172
+
+
+
900
+
#282254
+
+
+
950
+
#1f1b42
+
+
+
+ +
+
Magenta — H:320° C:0.14 · AI / Creative / Highlight
+
+
+
50
+
#fcf0ff
+
+
+
100
+
#f9ddff
+
+
+
200
+
#f2bffd
+
+
+
300
+
#da9de8
+
+
+
400
+
#c07ecf
+
+
+
500
+
#a462b4
+
+
+
600
+
#874895
+
+
+
700
+
#6d3579
+
+
+
800
+
#55265f
+
+
+
900
+
#3d1a45
+
+
+
950
+
#301436
+
+
+
+ +
+
Rose — H:355° C:0.13 · Emphasis / Priority / Human Actor
+
+
+
50
+
#fff1f6
+
+
+
100
+
#ffdde9
+
+
+
200
+
#ffbfd7
+
+
+
300
+
#f198bb
+
+
+
400
+
#d7799f
+
+
+
500
+
#bb5c84
+
+
+
600
+
#9b436a
+
+
+
700
+
#7e3053
+
+
+
800
+
#632240
+
+
+
900
+
#48172d
+
+
+
950
+
#391223
+
+
+
+ +

Hue Wheel Summary

+
+
+
+
Error
25° — Danger
+
+
+
+
Warning
70° — Caution
+
+
+
+
Lime
110° — Progress
+
+
+
+
Success
155° — Validation
+
+
+
+
Cyan
195° — System
+
+
+
+
Indigo
250° — Reference
+
+
+
+
Violet
285° — Synthesis
+
+
+
+
Magenta
320° — Creative
+
+
+
+
Rose
355° — Emphasis
+
+
+

+ Shade 600 = light mode semantic · Shade 400 = dark mode semantic · + Min pairwise distance: 30° (Rose↔Error) · Avg: 41° +

+
+ + +
+

Brand Identity Through Restraint

+

+ Most brands differentiate by choosing a color. We differentiate by choosing not to. The brand IS the geometry: the agent-loop logo mark, Space Grotesk + Inter + Monaspace Neon typography, disciplined whitespace, and flat construction. Color serves a single purpose: semantic meaning. +

+ + +
+
The Monochrome Principle in Practice
+
+
+
achromatic surface
+
Neutral container
+
+
+
color = semantic label
+
+ MODULE 1 — Hue carries meaning +
+
+
+
typographic interaction
+
Start Learning
+
Dark fill, no hue
+
+
+
+ + +
+
+
1
+
Achromatic Base
+
Surfaces, borders, text are pure gray (chroma 0). No tinted neutrals.
+
+
+
2
+
No Privileged Hue
+
All 9 chromatic colors have equal standing. None is "the brand color."
+
+
+
3
+
Color = Meaning
+
Color appears only in semantic callouts, diagrams, status indicators, and data viz. Every color use must answer: "what does this hue mean here?"
+
+
+
4
+
Typographic Interaction
+
Links use underlines + weight, buttons use dark/light fills. Interactive elements identified by typography and shape, not color.
+
+
+
5
+
Geometry as Brand
+
The logo mark, monospace type, whitespace rhythm, and flat construction are the Distinctive Brand Assets.
+
+
+
6
+
60-30-10 Color Budget
+
60% achromatic surfaces, 30% elevated gray, 10% semantic color. In practice most pages will be closer to 95/5.
+
+
+
+ + +
+

Brand Identity Assets

+

+ These are the Distinctive Brand Assets per Romaniuk's framework. They are geometry and typography, not color. +

+ +
+ +
+
</>
+
On white
+
+ +
+
</>
+
On dark
+
+ +
+
+ / +
+
Favicon 16px
+
+ +
+
</> Agentic Coding
+
Social card
+
+
+ + +
+
+
DISPLAY
+
Space Grotesk
+
The quick brown fox jumps over the lazy dog. ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789
+
+
+
BODY
+
Inter
+
The quick brown fox jumps over the lazy dog. ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789
+
+
+
CODE
+
Monaspace Neon
+
const agent = new Agent(); // 0O 1lI {[()]}
+
+
+ + +
+

Favicon Strategy

+
+
• SVG favicon with embedded @media (prefers-color-scheme: dark) for light/dark adaptation
+
• Pure monochrome mark — dark fill on light tab, light fill on dark tab
+
.ico fallback at 32×32
+
apple-touch-icon.png at 180×180 with solid background
+
+
+ +

+ Design action item: If the current logo geometry doesn't read at 16px in pure monochrome, the geometry needs simplification. Test and iterate. +

+
+ + +
+

Design System Elements

+
+
+

Buttons

+
+
Primary
+
Outline
+
Ghost Link
+
+
Dark fill, dark border, or underline — no color
+
+
+

9 Semantic Badges

+
+ Error + Warning + Lime + Success + Cyan + Indigo + Violet + Magenta + Rose +
+
All 9 hues at equal standing
+
+
+

Cards

+
+
Module 1: Fundamentals
+
LLM internals, context windows, and hallucination management.
+
+
+ TIP — Always verify agent output against source. +
+
Semantic color on callout border + label only
+
+
+

Inputs & Fields

+ + + + +
Focus → darker border (contrast shift, not color)
+
+
+

Neutral Interaction

+
+
Lesson 3: Grounding
+
File paths, line numbers, root cause analysis
+
Hover to see shift →
+
+
Neutral at rest. Border lightens on hover/focus/active.
+
+
+
+ + +
+

Homepage — Light & Dark Mode

+
+ +
+
+
+
+
+
agenticoding.ai
+
+
+ +
+
+ Open Source · MIT · 2.1k +
+

Master Agentic Coding

+

Structured methodology for enterprise codebases

+
+
Start Learning
+
Browse Prompts
+
+
+ + +
+
+
+
+
+
methodology.md
+
+
+ INVESTIGATE: Trace the code path
+ ANALYZE: Compare expected vs actual
+ EXPLAIN: File paths, line numbers, root cause + From the Prompt Library → +
+
+ +
+

What You'll Learn

+
+
+
Module 1
+

Fundamentals

+
    +
  • LLM internals & context
  • +
  • Hallucinations & drift
  • +
  • RAG integration
  • +
+
+
+
Module 2
+

Methodology

+
    +
  • Prompt structure
  • +
  • Grounding patterns
  • +
  • Plan → Execute → Verify
  • +
+
+
+
Module 3
+

Practical Techniques

+
    +
  • CI integration
  • +
  • Test generation
  • +
  • Debugging sessions
  • +
+
+
+
+
+

Learn Your Way

+
+
+ +

Reference Docs

+

Bookmark it. Jump back in.

+
+
+ +

Podcasts

+

Commute, gym, walking the dog.

+
+
+ +

Presentations

+

Share with your team.

+
+
+
+
+
+ + +
+
+
+
+
+
agenticoding.ai (dark)
+
+
+ +
+
+ Open Source · MIT · 2.1k +
+

Master Agentic Coding

+

Structured methodology for enterprise codebases

+
+
Start Learning
+
Browse Prompts
+
+
+ + +
+
+
+
+
+
methodology.md
+
+
+ INVESTIGATE: Trace the code path
+ ANALYZE: Compare expected vs actual
+ EXPLAIN: File paths, line numbers, root cause + From the Prompt Library → +
+
+ +
+

What You'll Learn

+
+
+
Module 1
+

Fundamentals

+
    +
  • LLM internals & context
  • +
  • Hallucinations & drift
  • +
  • RAG integration
  • +
+
+
+
Module 2
+

Methodology

+
    +
  • Prompt structure
  • +
  • Grounding patterns
  • +
  • Plan → Execute → Verify
  • +
+
+
+
Module 3
+

Practical Techniques

+
    +
  • CI integration
  • +
  • Test generation
  • +
  • Debugging sessions
  • +
+
+
+
+
+

Learn Your Way

+
+
+ +

Reference Docs

+

Bookmark it. Jump back in.

+
+
+ +

Podcasts

+

Commute, gym, walking the dog.

+
+
+ +

Presentations

+

Share with your team.

+
+
+
+
+
+
+
+ + +
+

Social Card & OG Image

+

+ Template: dark background (#111111), large white title, small monochrome logo mark, optional single semantic accent line chosen per content context. Variety without brand-color dependency. +

+
+ +
+
+
</> Agentic Coding
+
Lesson 5: Grounding & Validation
+
success accent — validated content
+
+ +
+
+
</> Agentic Coding
+
Lesson 9: AI Agent Patterns
+
violet accent — AI/transformation
+
+ +
+
+
</> Agentic Coding
+
Master Agentic Coding
+
neutral — default/homepage
+
+
+
+ + +
+

Illustration Examples — 9-Color System

+

+ The illustration system demonstrates monochrome-first at full capacity. These diagrams use all 9 semantic hues equally. No single color dominates. The achromatic base makes every accent equally prominent. +

+ +
+ + +
+

Architecture Diagram — 9 Entity Types

+
+ +
+
+
User
rose — human actor
+
+
+
API Gateway
indigo — reference
+
+
+
Auth
violet — transform
+
+
+ +
↓            ↓            ↓
+ +
+
+
LLM Agent
magenta — AI
+
+
+
Cache
lime — intermediate
+
+
+
Database
cyan — system
+
+
+ +
↓            ↓            ↓
+ +
+
+
Queue
success — active
+
+
+
Monitor
warning — caution
+
+
+
Error Handler
error — critical
+
+
+
+
+ + +
+

Data Visualization — 5-Series Bar Chart (dark mode)

+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
Indigo
+
Lime
+
Magenta
+
Violet
+
Rose
+
+
+ + +
+

Agent Pipeline — Multi-Phase Flow

+
+ +
+
1
+
User Input
+
rose
+
+
+ +
+
2
+
Context Retrieval
+
indigo
+
+
+ +
+
3
+
LLM Processing
+
magenta
+
+
+ +
+
4
+
Transformation
+
violet
+
+
+ +
+
5
+
Code Generation
+
cyan
+
+
+ +
+
6
+
Validation & Output
+
success
+
+
+
+ + +
+

Full 9-Color Badge System

+ + +
+
dark mode — solid fill
+
+ Error + Warning + Lime + Success + Cyan + Indigo + Violet + Magenta + Rose +
+
+ + +
+
light mode — tinted bg + colored text
+
+ Error + Warning + Lime + Success + Cyan + Indigo + Violet + Magenta + Rose +
+
+ + +
+
semantic application
+
+ Module 1 + AI Agent + Reference + Transform + User Input + In Progress + Complete + Caution + Breaking +
+
+
+
+ + +
+

Light Mode — Spec-to-Code Convergence Diagram

+
+ +
+
SPEC
+
Requirements
+
+ +
+
Generate
+
+
+
+ +
+
CODE
+
Implementation
+
+ +
+
Extract
+
+
+
+ +
+
TEST
+
Validation
+
+ +
+
Verify
+
+
+
+ +
+
PASS
+
Convergence
+
+
+ +
+
Spec (violet)
+
Generate (magenta)
+
Code (cyan)
+
Extract (success)
+
Test (indigo)
+
Verify (warning)
+
+
+ + +
+

Dark Mode — Knowledge Map (Concept Relationships)

+
+ +
+
Context Window
+
Token limits, attention
+
+
+
Transformer
+
Self-attention, layers
+
+
+
Prompt Engineering
+
System prompts, few-shot
+
+ +
+
RAG Pipeline
+
Retrieval, embeddings
+
+
+
Grounding
+
File paths, line numbers
+
+
+
Iteration Cycle
+
Plan → execute → verify
+
+ +
+
Developer Intent
+
Requirements, goals
+
+
+
Hallucination Risk
+
Drift, confabulation
+
+
+
Failure Modes
+
Context overflow, loops
+
+
+ +
+
↕ concepts connect by semantic color — same category = same hue family
+
+
+
+ + +
+

Semantic Token Mapping

+
+
+

Light Theme

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValueSource
Surfaces
--surface-page#ffffffwhite
--surface-raised#f5f5f5neutral-50
--surface-muted#e8e8e8neutral-100
Text
--text-heading#2b2b2bneutral-900
--text-body#505050neutral-700
--text-muted#808080neutral-500
Borders
--border-default#d4d4d4neutral-200
Semantic Colors
--visual-error#ad3735error-600
--visual-warning#8e5900warning-600
--visual-lime#6b6b00lime-600
--visual-success#007a44success-600
--visual-cyan#007576cyan-600
--visual-indigo#1369b0indigo-600
--visual-violet#6057afviolet-600
--visual-magenta#874895magenta-600
--visual-rose#9b436arose-600
+
+
+

Dark Theme

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenValueSource
Surfaces
--surface-page#0d1117GitHub dark
--surface-raised#161b22elevated
--surface-muted#3d3d3dneutral-800
Text
--text-heading#e8e8e8neutral-100
--text-body#d4d4d4neutral-200
--text-muted#9b9b9bneutral-400
Borders
--border-default#505050neutral-700
Semantic Colors
--visual-error#ec7069error-400
--visual-warning#cd8c37warning-400
--visual-lime#a1a22blime-400
--visual-success#48b475success-400
--visual-cyan#00b2b2cyan-400
--visual-indigo#53a0ecindigo-400
--visual-violet#938eebviolet-400
--visual-magenta#c07ecfmagenta-400
--visual-rose#d7799frose-400
+
+
+
+ + +
+

Spatial System

+

+ Computed spatial token system for the Agentic Coding brand. All values derived programmatically from the brand PAD profile: + Pleasure: Medium, + Arousal: Low-Medium, + Dominance: Medium. + Base unit: 8px. Type scale ratio: 1.200 (Minor Third). Surface strategy: Flat 2.0. +

+ + +

Spacing Scale

+

Base unit: 8px. Hybrid geometric progression — compresses at small values, expands at large values.

+ + + + + + + + + + + + + + + + + +
TokenStepMultiplierValue
--space-0000px
--space-1118px
--space-22216px
--space-33324px
--space-44432px
--space-55648px
--space-66864px
--space-771080px
--space-881296px
--space-9916128px
--space-101020160px
+ +

Spacing Assignment (Low-Medium Arousal)

+ + + + + + + + + +
PurposeStep RangeValue Range
Component paddingstep 2–316–24px
Section gapstep 5–648–64px
Page marginstep 7–880–96px
+ + +

Type Scale

+

Base: 16px, ratio: 1.200 (Minor Third). All sizes rounded to integers. Line-heights grid-snapped to 8px multiples.

+ + + + + + + + + + + + + + +
TokenStepRoleSizeLH RatioLine-Height
--text-xs-2Fine print, captions11px1.5024px
--text-sm-1Secondary, metadata13px1.5024px
--text-base0Body text16px1.5024px
--text-lg1Lead paragraphs19px1.3032px
--text-xl2h4 subheadings23px1.3032px
--text-2xl3h3 section headings28px1.1540px
--text-3xl4h2 major headings33px1.1540px
--text-4xl5h1 page titles40px1.1548px
+ + +

Vertical Rhythm

+

Formula: ceil(fontSize * lhRatio / 8) * 8. All line-heights snap to the 8px grid.

+ + + + + + + + + + + + + + + +
ContextFont SizeRatioRawSnapped
Body16px1.5024.0px24px
Body (small)14px1.5021.0px24px
Heading (large)28px1.1532.2px40px
Heading (medium)23px1.1526.5px32px
Heading (small)19px1.1521.9px24px
Code16px1.4022.4px24px
Code (small)14px1.4019.6px24px
Caption14px1.3518.9px24px
Caption (small)12px1.3516.2px24px
+ + +

Border Radius

+

Medium pleasure: multiplier 1.0, base_r = 8px. Curvature ratio 0.10–0.20 (professional, approachable).

+
+
+

Token Scale

+ + + + + + + + + + + + + +
TokenFormulaValue
--radius-none00px
--radius-smbase_r × 0.54px
--radius-mdbase_r8px
--radius-lgbase_r × 1.512px
--radius-xlbase_r × 216px
--radius-2xlbase_r × 324px
--radius-full9999px9999px
+
+
+

Semantic Adjustments (40px ref)

+ + + + + + + + + + + + +
RoleAdjustmentRatioRadius
Error/danger-50%0.0752px
Warning-25%0.1122px
Success+25%0.1884px
Neutral/info0%0.1503px
Avatarcircle1.00050%
Input fields-25%0.1122px
+
+
+ + +

Line Weight System

+

Medium dominance: 1–2px default borders, 3px for semantic accents. Perceived weight = thickness × ΔL.

+ + + + + + + + + + + +
TokenThicknessLight ColorΔL (light)PBW (light)Dark ColorΔL (dark)PBW (dark)
--border-subtle1px#d4d4d4 0.3420.342#3d3d3d 0.0410.041
--border-default1px#b7b7b7 0.5270.527#505050 0.0750.075
--border-emphasis1px#808080 0.7840.784#808080 0.2100.210
--border-strong2px#505050 0.9201.840#b7b7b7 0.4680.936
--border-accent3pxsemantic colorvariablesemantic colorvariable
+ + +

Surface Hierarchy

+

Flat 2.0 with medium dominance. Depth via luminance stepping only (no shadows/gradients). Distribution: 95/5 for content pages, 60-30-10 for diagram-heavy pages.

+
+
+

Light Mode

+ + + + + + + + + +
TierHexLuminanceΔL
Page#ffffff 1.0000
Raised#f5f5f5 0.91310.087
Muted#e8e8e8 0.80700.106
+
+
+

Dark Mode

+ + + + + + + + + +
TierHexLuminanceΔL
Page#0d1117 0.0055
Raised#161b22 0.01070.005
Muted#3d3d3d 0.04670.036
+
+
+
+
Weber Fraction Compliance
+
+
Light page→raised: ΔL = 0.087 (min 0.03) PASS
+
Dark page→raised: ΔL = 0.005 (min 0.01) WARN
+
Light raised→muted: ΔL = 0.106 (min 0.03) PASS
+
Dark raised→muted: ΔL = 0.036 (min 0.01) PASS
+
+
Note: Dark page→raised ΔL (0.005) is below the 0.01 Weber minimum. The #0d1117/#161b22 pair relies on the tinted blue channel of #161b22 for perceptual separation, which sRGB luminance does not fully capture. Functionally acceptable but at the threshold.
+
+ + +

Target Sizes

+

Interactive element sizing per Fitts' law and WCAG 2.2. Size hierarchy: primary ≥ secondary × 1.25.

+ + + + + + + + + + +
TokenHeightH-PaddingWCAG AA (≥24px)WCAG AAA (≥44px)Use
--target-sm32px16pxPASSTertiary actions, inline buttons, tags
--target-md40px24pxPASSSecondary actions, form inputs
--target-lg48px32pxPASSPASSPrimary actions, main CTAs
--target-xl56px48pxPASSPASSHero CTAs, prominent actions
+
+ Hierarchy: + primary(48) / secondary(40) = 1.20 + WARN < 1.25 + + secondary(40) / tertiary(32) = 1.25 + PASS ≥ 1.15 +
+ + +

Gestalt Proximity Validation

+ + + + + + + + + + + +
RelationshipStepValueRatio to Within-GroupResult
Tightly related18px
Related (within-group)216px1.0×
Loosely related432px2.0×
Between-group548px3.0×0.333 < 0.5 PASS
Unrelated (between sections)664px4.0×0.250 < 0.5 PASS
+ + +

Congruence Validation

+

Three-axis PAD alignment check: spatial vs. color vs. typography. Tolerance: ±1 tier.

+
+
+
Spatial PAD
+
+ P: 2.0 (Medium)
+ A: 1.5 (Low-Medium)
+ D: 2.0 (Medium) +
+
+
+
Color PAD
+
+ P: 2.0 (Medium)
+ A: 1.0 (Low)
+ D: 2.0 (Medium) +
+
+
+
Typography PAD
+
+ P: 2.0 (Medium)
+ A: 1.5 (Low-Medium)
+ D: 2.0 (Medium) +
+
+
+ + + + + + + + + + + + + + +
CheckAxisSpatialComparedΔResult
Spatial vs Color
Pleasure2.02.00.0PASS
Arousal1.51.00.5PASS
Dominance2.02.00.0PASS
Spatial vs Typography
Pleasure2.02.00.0PASS
Arousal1.51.50.0PASS
Dominance2.02.00.0PASS
+
+ All congruence checks pass. + Spatial tokens align with the monochrome-first achromatic color system and the Inter/Space Grotesk geometric typography stack across all three PAD axes. No incongruence flags. +
+
+ + +
+

Design Rationale

+
+
+

Why Monochrome-First?

+
    +
  • Industry precedent: Vercel, Linear, Stripe, Notion — monochrome-first is the premium signal in dev tools. It communicates craft and confidence.
  • +
  • Sharp's distinctiveness: Every competitor claimed a hue. Achromatic is the unoccupied position. Maximum uniqueness by occupying the space no one else wants.
  • +
  • Color emotion science: Per Wilms & Oberfeld 2018, saturation drives arousal (η²=.693). High-chroma accents against achromatic backgrounds create maximum perceptual contrast. Pure gray amplifies every hue equally.
  • +
  • Cognitive load: Per SHIFT eLearning, cognitive overload from color is a real risk in educational content. Monochrome base prevents it by design.
  • +
+
+
+

Why Typographic Interaction?

+
    +
  • Color reserved for meaning: If links and buttons consume a brand color, that color can no longer serve as a pure semantic signal. Typographic interaction frees all 9 hues for content meaning.
  • +
  • Universal affordance: Underlines and dark fills are universally recognized interactive patterns. They work without color vision. No accessibility workaround needed.
  • +
  • Precedent: Vercel and Linear use minimal color for interactive chrome. Dark fill buttons, underlined links. The interaction pattern is shape and weight, not hue.
  • +
  • Simplicity: One fewer decision per component — no "which color should this button be?" Every interactive element follows the same neutral rule.
  • +
+
+
+

Why 9 Semantic Hues?

+
    +
  • Expressiveness: 9 hues prevent color reuse across unrelated concepts — architecture nodes, flow phases, and chart series each get distinct slots without collision.
  • +
  • Maximum separation: Hues placed at the largest gaps in the 360° wheel. Min pairwise distance: 30° (Rose↔Error). Average: 41°.
  • +
  • Semantic coherence: Each hue maps to a consistent meaning — Rose for human actors, Indigo for data/reference, Violet for AI/transformation, Magenta for creative/highlight, Lime for progress.
  • +
  • WCAG validation: Every hue passes AA (≥4.5:1) at shade 600 on white and shade 400 on dark backgrounds. No accessibility compromise.
  • +
+
+
+

Hue Placement Logic

+
    +
  • Indigo (250°): Full 11-shade scale for knowledge, documentation, and reference content.
  • +
  • Violet (285°): Exact midpoint of 250°–320° gap. AI/intelligence — distinct from competitor purple (260–280°) in the Cemetery quadrant.
  • +
  • Magenta (320°): Full scale for creative processes and highlights.
  • +
  • Rose (355°): 30° from error red (25°), perceptually distinct warm-pink. Human actors and emphasis.
  • +
  • Lime (110°): Fills the largest gap (85° between warning 70° and success 155°). ≥40° from both. Progress and intermediate states.
  • +
+
+
+
+ + +
+

Implementation Roadmap

+

+ Phased build-out of the monochrome-first system. Each phase is independently deployable. +

+
+
+
1
+
Neutral Scale
+
Configure neutral scale in custom.css at chroma 0.000 (pure achromatic)
+
+
+
2
+
Semantic Tokens
+
Define --visual-* tokens for all 9 semantic hues in CSS custom properties
+
+
+
3
+
Infima Primary
+
Set Infima primary to neutral dark. Style links as typographic (underline + weight).
+
+
+
4
+
Logo SVG
+
Create monochrome logo SVG variants for light and dark contexts.
+
+
+
5
+
Favicon Set
+
Regenerate favicon set from monochrome source SVG.
+
+
+
6
+
Color Guide
+
Write COLOR_GUIDE.md documenting neutral scale, 9 semantic hues, and usage rules.
+
+
+
7
+
Components
+
Wire all VisualElement components to --visual-* semantic tokens.
+
+
+
+ + + \ No newline at end of file diff --git a/icon-style-showcase.html b/icon-style-showcase.html new file mode 100644 index 0000000..4001b8b --- /dev/null +++ b/icon-style-showcase.html @@ -0,0 +1,805 @@ + + + + + +Agentic Coding — Icon & Illustration Style Showcase + + + + + + + + +
+

Icon & Illustration Style Showcase

+

Three parametric directions for the Agentic Coding design system

+
+
P: 0.55 Medium+
+
A: 0.50 Medium
+
D: 0.50 Medium
+
+
+ +
+ + + + +
+

Three Parametric Directions

+
+ + +
+ D1 +

Blueprint Grid

+

IBM-style construction from strict grid shapes. High regularity, symmetry, precision. Rectangles and circles on 8px grid with orthogonal routing.

+
n=2 (circle) / n=∞ (rect) · 90° angles · BK 0.56
+
PAD
0.748
+
Frac D
1.44
+
DCM
0.23%
+
+ + +
+ D2 +

Smooth Circuit

+

Superellipse-based shapes with smooth continuous curves. Squircles for containers, Bezier connectors. Apple-adjacent organic feel.

+
n=3–5 (squircle) · curves · BK 0.64 (bouba-lean)
+
PAD
0.817
+
Frac D
1.43
+
DCM
5.04%
+
+ + +
+ D3 +

Terminal Geometry

+

Code-native angular vocabulary. Diamonds, chevrons, bracket syntax. Line-segment based with sharp intersections on monospace grid.

+
n=1–1.5 (diamond) · 45° angles · BK 0.35 (kiki)
+
PAD
0.827
+
Frac D
1.48
+
DCM
0.16%
+
+
+
+ + + + +
+

Icon Comparison

+
+ +
+
D1 Blueprint Grid
+
D2 Smooth Circuit
+
D3 Terminal Geometry
+ + +
Node
+
+ + + + +
+
+ + + + +
+
+ + + + +
+ + +
Loop
+
+ + + + +
+
+ + + + +
+
+ + + + +
+ + +
Code
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + +
+ + +
Flow
+
+ + + + + + +
+
+ + + + + + +
+
+ + + + + + +
+ + +
Stack
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+ + +
Check
+
+ + + + +
+
+ + + + +
+
+ + + + +
+ + +
Shield
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+ + +
Action
+
+ + + +
+
+ + + +
+
+ + + + +
+
+
+ + + + +
+

Illustration: Agent Workflow

+

+ Human → Agent → Output — each rendered in the three parametric styles. +

+
+ + +
+
Blueprint Grid
+ + + + + Human + + + + + + + + + + Agent + + + + + + + + + + + Output + +
+ + +
+
Smooth Circuit
+ + + + + Human + + + + + + + + + + Agent + + + + + + + + + + + Output + +
+ + +
+
Terminal Geometry
+ + + + + Human + + + + + + + + + + Agent + + + + + + + + + + + Output + +
+
+
+ + + + +
+

Semantic Color Application

+

+ Same icon form, colored by semantic role. Shown in D2 (Smooth Circuit) style. +

+
+
+ + + + + +
Error
+
+
+ + + + + +
Warning
+
+
+ + + + +
Success
+
+
+ + + + +
System
+
+
+ + + + + + +
Knowledge
+
+
+ + + + +
AI Transform
+
+
+ + + + +
AI Creative
+
+
+ + + + +
Human
+
+
+ + + + +
Progress
+
+
+ + + + + +
Info
+
+
+
+ + + + +
+

Computed Validation Metrics

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MetricThresholdD1 BlueprintD2 SmoothD3 Terminal
PAD Alignment> 0.800.7480.8170.827
Fractal Dimension D1.2 – 1.61.44 ✓1.43 ✓1.48 ✓
DCM (balance)< 10%0.23% ✓5.04% ✓0.16% ✓
Shannon Entropy H/Hmaxinfo0.8590.9100.877
Aesthetic Measure> 1.00.750.990.99
Berlyne Positionoptimalnear-optimaloptimal ✓optimal ✓
Bouba/Kiki0 = kiki, 1 = bouba0.56 (mixed)0.64 (bouba)0.35 (kiki)
Composite Score79.0100.4104.0
+
+
+ + + + +
+

Recommended Blend

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+

60% Smooth Circuit + 40% Terminal Geometry

+

+ Squircle containers (n=3–4) provide pleasure-axis warmth. Angular chevrons and diamond accents provide arousal-axis technical identity. + Curved forms for positive-valence elements (success, AI, system); angular forms for high-arousal states (error, warning, code). +

+
PAD alignment = 0.937 · P=0.55 A=0.51 D=0.52
+
+
+
+ + + + +
+

Parametric Reference

+
+
+

Superellipse

+
+ |x/a|n + |y/b|n = 1
+ n=1 → diamond
+ n=2 → ellipse
+ n=4 → squircle
+ n→∞ → rectangle +
+
+ + + + +
+
+
+

Angles

+
+ Preferred: 15° 30° 45° 60° 75° 90°
+ D1: 90° only (orthogonal)
+ D2: continuous (Bezier)
+ D3: 45° primary (diagonal grid) +
+
+ + + +
+
+
+

Grid

+
+ Base unit: 8px
+ Min feature: 8px (1 unit)
+ Min gap: 8px (1 unit)
+ Icon canvas: 48px (6 units)
+ All anchors grid-snapped +
+
+ + + + + +
+
+
+
+ +
+ + diff --git a/logo.svg b/logo.svg index 8f80d46..7c9d426 100644 --- a/logo.svg +++ b/logo.svg @@ -1,26 +1,18 @@ - AI Coding Course — Agent Loop Icon - Minimal agent loop with a node. Purple loop + fuchsia node, theme-aware. + Agentic Coding + Code glyph mark. Achromatic, theme-aware. - - - - + + + + - - - diff --git a/package.json b/package.json new file mode 100644 index 0000000..cdfe0d0 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "agenticoding", + "private": true, + "devDependencies": { + "@tauri-apps/cli": "^2.10.1" + } +} diff --git a/scripts/compute-agent-loop-arcs.js b/scripts/compute-agent-loop-arcs.js new file mode 100644 index 0000000..7e2737e --- /dev/null +++ b/scripts/compute-agent-loop-arcs.js @@ -0,0 +1,216 @@ +#!/usr/bin/env node +/** + * Compute geometrically correct arc paths for AgentLoopDiagram (pentagon orbit). + * + * Pentagon: 5 nodes clockwise from top (Perceive, Reason, Act, Observe, Verify). + * Center: (270, 185) Radius: 110 ViewBox: 0 0 520 352 + * + * Arc attachment points (face-center style, same as compute-execution-loop-arcs.js): + * Arc 1 Perceive.right → Reason.top (clockwise) + * Arc 2 Reason.bottom → Act.top (clockwise) + * Arc 3 Act.left → Observe.right (clockwise) + * Arc 4 Observe.top → Verify.bottom (clockwise) + * Arc 5 Verify.top → Perceive.left (iterate / "No") + * Exit Verify.left → Done.top-face (curved Bézier / "Yes") + */ + +const BULGE = 30; // perpendicular offset toward pentagon exterior +const REF_X = 5; // arrowhead tip retraction (px along arrival tangent) + +const CX = 270, CY = 185, R = 110; // pentagon center & radius +const toRad = deg => deg * Math.PI / 180; + +// Pentagon vertex angles in degrees, clockwise from top +const ANGLES = [-90, -18, 54, 126, 198]; +const NAMES = ['perceive', 'reason', 'act', 'observe', 'verify']; + +// Compute node centers (rounded to nearest integer for clean coords) +const centers = ANGLES.map(a => ({ + x: Math.round(CX + R * Math.cos(toRad(a))), + y: Math.round(CY + R * Math.sin(toRad(a))), +})); + +// Face centers for each 48×48 node box (centered on node center) +function faces(i) { + const { x, y } = centers[i]; + return { + top: { x, y: y - 24 }, + bottom: { x, y: y + 24 }, + left: { x: x - 24, y }, + right: { x: x + 24, y }, + }; +} + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +function unitVec(a, b) { + const dx = b.x - a.x, dy = b.y - a.y; + const len = Math.hypot(dx, dy); + return { x: dx / len, y: dy / len }; +} + +/** Perpendicular (CCW 90°) of a unit vector. */ +function perp(u) { return { x: -u.y, y: u.x }; } + +function r2(v) { return Math.round(v * 100) / 100; } + +// ── Arc computation ─────────────────────────────────────────────────────────── + +function computeArc(exitPt, entryPt, bulgeSign) { + const dir = unitVec(exitPt, entryPt); + const p = perp(dir); + const mid = { x: (exitPt.x + entryPt.x) / 2, y: (exitPt.y + entryPt.y) / 2 }; + + const ctrl = { + x: mid.x + bulgeSign * BULGE * p.x, + y: mid.y + bulgeSign * BULGE * p.y, + }; + + const arrivalDir = unitVec(ctrl, entryPt); + const arrivalAngleDeg = Math.atan2(arrivalDir.y, arrivalDir.x) * 180 / Math.PI; + + const retracted = { + x: entryPt.x - arrivalDir.x * REF_X, + y: entryPt.y - arrivalDir.y * REF_X, + }; + + return { + d: `M ${exitPt.x} ${exitPt.y} Q ${r2(ctrl.x)} ${r2(ctrl.y)} ${r2(retracted.x)} ${r2(retracted.y)}`, + exitPt, + ctrl: { x: r2(ctrl.x), y: r2(ctrl.y) }, + retractedEnd: { x: r2(retracted.x), y: r2(retracted.y) }, + arrowTip: entryPt, + arrivalAngle: arrivalAngleDeg, + }; +} + +// Pentagon centroid (used for exterior-bulge direction) +const centroid = { x: CX, y: CY }; + +function exteriorBulgeSign(exit, entry) { + const dir = unitVec(exit, entry); + const p = perp(dir); + const mid = { x: (exit.x + entry.x) / 2, y: (exit.y + entry.y) / 2 }; + const toMid = { x: mid.x - centroid.x, y: mid.y - centroid.y }; + return (p.x * toMid.x + p.y * toMid.y) >= 0 ? 1 : -1; +} + +// ── Define attachment points ────────────────────────────────────────────────── + +const F = [0, 1, 2, 3, 4].map(faces); // F[i] = faces of node i + +// Arc 1: Perceive.right → Reason.top +const arc1 = computeArc(F[0].right, F[1].top, exteriorBulgeSign(F[0].right, F[1].top)); +// Arc 2: Reason.bottom → Act.top +const arc2 = computeArc(F[1].bottom, F[2].top, exteriorBulgeSign(F[1].bottom, F[2].top)); +// Arc 3: Act.left → Observe.right +const arc3 = computeArc(F[2].left, F[3].right, exteriorBulgeSign(F[2].left, F[3].right)); +// Arc 4: Observe.top → Verify.bottom +const arc4 = computeArc(F[3].top, F[4].bottom, exteriorBulgeSign(F[3].top, F[4].bottom)); +// Arc 5: Verify.top → Perceive.left (iterate) +const arc5 = computeArc(F[4].top, F[0].left, exteriorBulgeSign(F[4].top, F[0].left)); + +// Done node: 48×48 squircle at center=(96,264) (below-left of verify, grid-snapped to 8px) +const DONE_S = 48, DONE_CX = 96, DONE_CY = 264; +const doneFaces = { + top: { x: DONE_CX, y: DONE_CY - DONE_S / 2 }, + bottom: { x: DONE_CX, y: DONE_CY + DONE_S / 2 }, + left: { x: DONE_CX - DONE_S / 2, y: DONE_CY }, + right: { x: DONE_CX + DONE_S / 2, y: DONE_CY }, +}; +const arcExit = computeArc(F[4].left, doneFaces.top, exteriorBulgeSign(F[4].left, doneFaces.top)); + +/** Quadratic Bézier point at parameter t. */ +function bezierPt(p0, p1, p2, t) { + const mt = 1 - t; + return { x: mt*mt*p0.x + 2*mt*t*p1.x + t*t*p2.x, y: mt*mt*p0.y + 2*mt*t*p1.y + t*t*p2.y }; +} +// Bézier midpoint at t=0.4 (for "Yes" label placement, offset toward interior) +const exitMid = bezierPt(F[4].left, arcExit.ctrl, doneFaces.top, 0.4); + +// ── Emoji positions (40×40 in 48×48 box, pad=4) ────────────────────────────── + +function emojiPos(i) { + const c = centers[i]; + return { x: c.x - 24 + 4, y: c.y - 24 + 4 }; // pad = (48-40)/2 = 4 +} + +// ── Label positions (radially outward, r + 38 from pentagon center) ─────────── + +const LABEL_R = R + 42; // node center radius + half-node + gap + text height + +function labelPos(i) { + const angle = toRad(ANGLES[i]); + return { + x: r2(CX + LABEL_R * Math.cos(angle)), + y: r2(CY + LABEL_R * Math.sin(angle)), + }; +} + +// ── Output ──────────────────────────────────────────────────────────────────── + +console.log('=== AgentLoopDiagram Arc Computation ===\n'); +console.log(`Pentagon center: (${CX}, ${CY}) radius: ${R}\n`); + +console.log('── Node centers & rects ──────────────────────────────'); +centers.forEach((c, i) => { + console.log(` ${NAMES[i].padEnd(8)}: center=(${c.x},${c.y}) rect x=${c.x-24} y=${c.y-24}`); +}); +console.log(); + +for (const [label, arc] of [ + ['Arc 1: perceive → reason', arc1], + ['Arc 2: reason → act', arc2], + ['Arc 3: act → observe', arc3], + ['Arc 4: observe → verify', arc4], + ['Arc 5: verify → perceive (iterate)', arc5], +]) { + console.log(`── ${label} ${'─'.repeat(Math.max(0, 44 - label.length))}`); + console.log(` d="${arc.d}"`); + console.log(` ctrl: (${arc.ctrl.x}, ${arc.ctrl.y})`); + console.log(` retractedEnd: (${arc.retractedEnd.x}, ${arc.retractedEnd.y})`); + console.log(` arrowTip: (${arc.arrowTip.x}, ${arc.arrowTip.y}) angle: ${arc.arrivalAngle.toFixed(2)}° rotate: ${Math.round(arc.arrivalAngle)}`); + console.log(); +} + +console.log('── Exit arc: verify → done ──────────────────────────────'); +console.log(` Done rect: x=${DONE_CX - DONE_S/2} y=${DONE_CY - DONE_S/2} w=${DONE_S} h=${DONE_S} top-face=(${doneFaces.top.x},${doneFaces.top.y})`); +console.log(` d="${arcExit.d}"`); +console.log(` ctrl: (${arcExit.ctrl.x}, ${arcExit.ctrl.y})`); +console.log(` retractedEnd: (${arcExit.retractedEnd.x}, ${arcExit.retractedEnd.y})`); +console.log(` arrowTip: (${arcExit.arrowTip.x}, ${arcExit.arrowTip.y}) rotate: ${Math.round(arcExit.arrivalAngle)}`); +console.log(` Bézier midpoint t=0.4: (${r2(exitMid.x)}, ${r2(exitMid.y)}) → "Yes" label ≈ (${r2(exitMid.x + 12)}, ${r2(exitMid.y)})`); +console.log(); + +console.log('── Emoji positions (size=40) ─────────────────────────'); +centers.forEach((_, i) => { + const ep = emojiPos(i); + console.log(` ${NAMES[i].padEnd(8)}: x=${ep.x} y=${ep.y}`); +}); +console.log(); + +console.log('── Label positions ────────────────────────────────────'); +centers.forEach((_, i) => { + const lp = labelPos(i); + console.log(` ${NAMES[i].padEnd(8)}: x=${lp.x} y=${lp.y}`); +}); +console.log(); + +console.log('── Summary (copy into TSX) ──────────────────────────'); +console.log(` // Arc paths`); +[arc1, arc2, arc3, arc4, arc5].forEach((arc, i) => { + console.log(` /* arc ${i+1} */ d="${arc.d}"`); +}); +console.log(` /* exit */ d="${arcExit.d}"`); +console.log(); +console.log(` // Arrowhead transforms`); +[arc1, arc2, arc3, arc4, arc5].forEach((arc, i) => { + console.log(` /* arc ${i+1} */ translate(${arc.arrowTip.x},${arc.arrowTip.y}) rotate(${Math.round(arc.arrivalAngle)})`); +}); +console.log(` /* exit */ translate(${arcExit.arrowTip.x},${arcExit.arrowTip.y}) rotate(${Math.round(arcExit.arrivalAngle)})`); +console.log(); +console.log(` // Node rects`); +centers.forEach((c, i) => { + console.log(` /* ${NAMES[i].padEnd(8)} */ x={${c.x-24}} y={${c.y-24}} width={48} height={48}`); +}); +console.log(` /* done */ x={${DONE_CX - DONE_S/2}} y={${DONE_CY - DONE_S/2}} width={${DONE_S}} height={${DONE_S}} (NotoEmoji x={${DONE_CX - DONE_S/2 + 4}} y={${DONE_CY - DONE_S/2 + 4}} size={40})`); diff --git a/scripts/compute-execution-loop-arcs.js b/scripts/compute-execution-loop-arcs.js new file mode 100644 index 0000000..cf49552 --- /dev/null +++ b/scripts/compute-execution-loop-arcs.js @@ -0,0 +1,164 @@ +#!/usr/bin/env node +/** + * Compute geometrically correct, symmetric arc paths for ExecutionLoopDiagram. + * + * Node rects (all 48×48, rx=12): + * Brain: x=216 y=32 → center (240, 56) + * Execute: x=312 y=152 → center (336, 176) + * Observe: x=120 y=152 → center (144, 176) + * + * Triangle is isoceles: Brain at top-center, Execute/Observe symmetric at bottom. + * + * Arc endpoints are pinned to edge-center coordinates (not ray-intersection), + * so diagonal arcs exit/enter at the midpoint of the respective face. + */ + +const BULGE = 44; // perpendicular offset toward triangle exterior +const REF_X = 5; // arrowhead tip retraction distance (px along arrival tangent) + +// ── Edge-center attachment points ──────────────────────────────────────────── +// Each arc is defined by the center of the source edge it exits from +// and the center of the destination edge it enters into. + +const EDGES = { + brain: { bottom: { x: 240, y: 80 }, right: { x: 264, y: 56 }, left: { x: 216, y: 56 } }, + execute: { top: { x: 336, y: 152 }, left: { x: 312, y: 176 } }, + observe: { top: { x: 144, y: 152 }, right: { x: 168, y: 176 } }, +}; + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +function unitVec(a, b) { + const dx = b.x - a.x; + const dy = b.y - a.y; + const len = Math.hypot(dx, dy); + return { x: dx / len, y: dy / len }; +} + +/** Perpendicular (CCW rotation 90°) of a unit vector. */ +function perp(u) { + return { x: -u.y, y: u.x }; +} + +function r2(v) { return Math.round(v * 100) / 100; } + +// ── Arc computation ─────────────────────────────────────────────────────────── + +/** + * Compute a quadratic Bezier arc between two explicit edge-center points. + * + * @param exitPt - source attachment point (edge center on source box) + * @param entryPt - destination attachment point (edge center on dest box) + * @param bulgeSign - +1 or -1, which side to bulge toward + */ +function computeArc(exitPt, entryPt, bulgeSign) { + const dir = unitVec(exitPt, entryPt); + const p = perp(dir); + + // Chord midpoint + const mid = { + x: (exitPt.x + entryPt.x) / 2, + y: (exitPt.y + entryPt.y) / 2, + }; + + // Control point: chord midpoint offset perpendicularly toward exterior + const ctrl = { + x: mid.x + bulgeSign * BULGE * p.x, + y: mid.y + bulgeSign * BULGE * p.y, + }; + + // Tangent at t=1 of quadratic Bezier = direction from ctrl to entryPt + const arrivalDir = unitVec(ctrl, entryPt); + const arrivalAngleDeg = Math.atan2(arrivalDir.y, arrivalDir.x) * 180 / Math.PI; + + // Retract path endpoint along arrival tangent by REF_X — no grid snap + const retracted = { + x: entryPt.x - arrivalDir.x * REF_X, + y: entryPt.y - arrivalDir.y * REF_X, + }; + + return { + d: `M ${exitPt.x} ${exitPt.y} Q ${r2(ctrl.x)} ${r2(ctrl.y)} ${r2(retracted.x)} ${r2(retracted.y)}`, + exitPt, + ctrl: { x: r2(ctrl.x), y: r2(ctrl.y) }, + retractedEnd: { x: r2(retracted.x), y: r2(retracted.y) }, + arrowTip: entryPt, // un-retracted edge center, for tip placement + arrivalAngle: arrivalAngleDeg, + }; +} + +// ── Bulge sign ──────────────────────────────────────────────────────────────── + +// Centroid of the three node centers: +const centroid = { + x: (240 + 336 + 144) / 3, + y: ( 56 + 176 + 176) / 3, +}; + +/** + * Determine bulge sign so the control point is pushed toward the exterior + * of the triangle (away from centroid). + */ +function exteriorBulgeSign(exit, entry) { + const dir = unitVec(exit, entry); + const p = perp(dir); + const mid = { x: (exit.x + entry.x) / 2, y: (exit.y + entry.y) / 2 }; + const toMid = { x: mid.x - centroid.x, y: mid.y - centroid.y }; + return (p.x * toMid.x + p.y * toMid.y) >= 0 ? 1 : -1; +} + +// ── Emoji centering ─────────────────────────────────────────────────────────── + +const NODES = { + brain: { x: 216, y: 32, w: 48, h: 48 }, + execute: { x: 312, y: 152, w: 48, h: 48 }, + observe: { x: 120, y: 152, w: 48, h: 48 }, +}; + +function emojiPos(node, emojiSize = 40) { + const pad = (node.w - emojiSize) / 2; + return { x: node.x + pad, y: node.y + pad }; +} + +// ── Main ────────────────────────────────────────────────────────────────────── + +const E = EDGES; + +const arc1Sign = exteriorBulgeSign(E.brain.right, E.execute.top); +const arc2Sign = exteriorBulgeSign(E.execute.left, E.observe.right); +const arc3Sign = exteriorBulgeSign(E.observe.top, E.brain.left); + +const arc1 = computeArc(E.brain.right, E.execute.top, arc1Sign); +const arc2 = computeArc(E.execute.left, E.observe.right, arc2Sign); +const arc3 = computeArc(E.observe.top, E.brain.left, arc3Sign); + +console.log('=== ExecutionLoopDiagram Arc Computation ===\n'); +console.log(`Centroid: (${centroid.x.toFixed(1)}, ${centroid.y.toFixed(1)})\n`); + +for (const [label, arc] of [['Arc 1: brain → execute', arc1], ['Arc 2: execute → observe', arc2], ['Arc 3: observe → brain', arc3]]) { + console.log(`── ${label} ${'─'.repeat(Math.max(0, 43 - label.length))}`); + console.log(` d="${arc.d}"`); + console.log(` exit: (${arc.exitPt.x}, ${arc.exitPt.y})`); + console.log(` ctrl: (${arc.ctrl.x}, ${arc.ctrl.y})`); + console.log(` retractedEnd: (${arc.retractedEnd.x}, ${arc.retractedEnd.y})`); + console.log(` arrowTip: (${arc.arrowTip.x}, ${arc.arrowTip.y}) angle: ${arc.arrivalAngle.toFixed(2)}° rotate: ${Math.round(arc.arrivalAngle)}`); + console.log(); +} + +console.log('── Emoji positions (size=40) ─────────────────────────'); +const brainEmoji = emojiPos(NODES.brain); +const executeEmoji = emojiPos(NODES.execute); +const observeEmoji = emojiPos(NODES.observe); +console.log(` Brain: x=${brainEmoji.x} y=${brainEmoji.y}`); +console.log(` Execute: x=${executeEmoji.x} y=${executeEmoji.y}`); +console.log(` Observe: x=${observeEmoji.x} y=${observeEmoji.y}`); +console.log(); + +console.log('── Summary (copy into TSX) ──────────────────────────'); +console.log(` d="${arc1.d}" // brain → execute`); +console.log(` d="${arc2.d}" // execute → observe`); +console.log(` d="${arc3.d}" // observe → brain`); +console.log(); +console.log(` translate(${arc1.arrowTip.x},${arc1.arrowTip.y}) rotate(${Math.round(arc1.arrivalAngle)}) // arc1 arrowhead`); +console.log(` translate(${arc2.arrowTip.x},${arc2.arrowTip.y}) rotate(${Math.round(arc2.arrivalAngle)}) // arc2 arrowhead`); +console.log(` translate(${arc3.arrowTip.x},${arc3.arrowTip.y}) rotate(${Math.round(arc3.arrivalAngle)}) // arc3 arrowhead`); diff --git a/scripts/fetch-emoji.js b/scripts/fetch-emoji.js new file mode 100644 index 0000000..b817ff9 --- /dev/null +++ b/scripts/fetch-emoji.js @@ -0,0 +1,76 @@ +#!/usr/bin/env node + +import { readFile, writeFile, mkdir, access } from 'fs/promises'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const cacheDir = join(__dirname, '.noto-cache'); +const outDir = join(__dirname, '..', 'website', 'static', 'img', 'emoji'); +const BASE_URL = 'https://raw.githubusercontent.com/adobe-fonts/noto-emoji-svg/refs/heads/main/svg'; + +async function exists(path) { + return access(path).then(() => true).catch(() => false); +} + +function clean(svg) { + // Strip XML prolog and comments + let s = svg + .replace(/<\?xml[^?]*\?>/g, '') + .replace(//g, ''); + + // Normalize root: keep only viewBox and xmlns + s = s.replace(/]*>/, (match) => { + const vb = (match.match(/viewBox="([^"]*)"/) ?? [])[1] ?? '0 0 128 128'; + return ``; + }); + + // Convert style="fill:..." to fill="..." attribute + s = s.replace(/style="fill:([^;"]+)[^"]*"/g, (_, color) => `fill="${color.trim()}"`); + + return s.trim(); +} + +async function fetchEmoji(codepoints) { + await mkdir(cacheDir, { recursive: true }); + await mkdir(outDir, { recursive: true }); + + let failed = false; + for (const cp of codepoints) { + const filename = `u${cp}.svg`; + const cachePath = join(cacheDir, filename); + const outPath = join(outDir, `u${cp}.svg`); + + let raw; + if (await exists(cachePath)) { + console.log(` cache hit: ${filename}`); + raw = await readFile(cachePath, 'utf8'); + } else { + const url = `${BASE_URL}/${filename}`; + console.log(` fetching: ${url}`); + const res = await fetch(url); + if (!res.ok) { + console.error(` error: ${filename} — HTTP ${res.status}`); + failed = true; + continue; + } + raw = await res.text(); + await writeFile(cachePath, raw, 'utf8'); + } + + const cleaned = clean(raw); + await writeFile(outPath, cleaned, 'utf8'); + console.log(` wrote: ${outPath}`); + } + + if (failed) process.exit(1); +} + +const args = process.argv.slice(2); +if (args.length === 0) { + console.log('Usage: node fetch-emoji.js [codepoint ...]'); + console.log('Example: node fetch-emoji.js 1f916 1f4a1'); + process.exit(0); +} + +fetchEmoji(args).catch(console.error); diff --git a/scripts/fonts/Inter-Variable.ttf b/scripts/fonts/Inter-Variable.ttf new file mode 100644 index 0000000..4ab79e0 Binary files /dev/null and b/scripts/fonts/Inter-Variable.ttf differ diff --git a/scripts/fonts/SpaceGrotesk-Bold.ttf b/scripts/fonts/SpaceGrotesk-Bold.ttf new file mode 100644 index 0000000..f8eb245 Binary files /dev/null and b/scripts/fonts/SpaceGrotesk-Bold.ttf differ diff --git a/scripts/generate-favicons.js b/scripts/generate-favicons.js new file mode 100644 index 0000000..4f9171c --- /dev/null +++ b/scripts/generate-favicons.js @@ -0,0 +1,47 @@ +#!/usr/bin/env node + +import sharp from 'sharp'; +import pngToIco from 'png-to-ico'; +import { readFile, writeFile } from 'fs/promises'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const imgDir = join(__dirname, '..', 'website', 'static', 'img'); + +async function generateFavicons() { + console.log('Generating favicons...'); + + // Read the source SVG and strip dark-mode media query for light-mode raster export + const svgSource = await readFile(join(imgDir, 'favicon-source.svg'), 'utf8'); + const lightSvg = svgSource.replace( + /@media\s*\(prefers-color-scheme:\s*dark\)\s*\{[^}]*\}/g, + '' + ); + const svgBuffer = Buffer.from(lightSvg); + + // Generate 32x32 PNG then convert to .ico + const png32 = await sharp(svgBuffer) + .resize(32, 32) + .png() + .toBuffer(); + + const ico = await pngToIco([png32]); + const icoPath = join(imgDir, 'favicon.ico'); + await writeFile(icoPath, ico); + console.log(` favicon.ico (32x32): ${(ico.length / 1024).toFixed(1)}KB`); + + // Generate 180x180 apple-touch-icon + const touchPath = join(imgDir, 'apple-touch-icon.png'); + await sharp(svgBuffer) + .resize(180, 180) + .png() + .toFile(touchPath); + + const { size } = await readFile(touchPath).then(b => ({ size: b.length })); + console.log(` apple-touch-icon.png (180x180): ${(size / 1024).toFixed(1)}KB`); + + console.log('Done.'); +} + +generateFavicons().catch(console.error); diff --git a/scripts/generate-social-card.js b/scripts/generate-social-card.js index 9d44ff6..43ca3a6 100644 --- a/scripts/generate-social-card.js +++ b/scripts/generate-social-card.js @@ -1,13 +1,23 @@ #!/usr/bin/env node -import { createCanvas, loadImage } from 'canvas'; -import { readFile, writeFile } from 'fs/promises'; +import { createCanvas, registerFont } from 'canvas'; +import { writeFile } from 'fs/promises'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +// Register design system fonts (TTF required by node-canvas) +registerFont(join(__dirname, 'fonts', 'SpaceGrotesk-Bold.ttf'), { + family: 'Space Grotesk', + weight: 'bold', +}); +registerFont(join(__dirname, 'fonts', 'Inter-Variable.ttf'), { + family: 'Inter', + weight: 'normal', +}); + // Open Graph standard dimensions const WIDTH = 1200; const HEIGHT = 630; @@ -15,79 +25,57 @@ const HEIGHT = 630; async function generateSocialCard() { console.log('Generating social card...'); - // Create canvas const canvas = createCanvas(WIDTH, HEIGHT); const ctx = canvas.getContext('2d'); - // Solid color background (professional minimalist approach) - ctx.fillStyle = '#7c3aed'; // Brand primary purple - ctx.fillRect(0, 0, WIDTH, HEIGHT); - - // Add subtle radial overlay for depth without over-saturation - const radialGradient = ctx.createRadialGradient( - WIDTH / 2, HEIGHT / 2, 0, - WIDTH / 2, HEIGHT / 2, WIDTH * 0.8 - ); - radialGradient.addColorStop(0, 'rgba(139, 92, 246, 0.3)'); // Lighter purple center - radialGradient.addColorStop(1, 'rgba(124, 58, 237, 0)'); // Transparent edges - - ctx.fillStyle = radialGradient; + // Solid dark background per design system spec + ctx.fillStyle = '#111111'; ctx.fillRect(0, 0, WIDTH, HEIGHT); - // Add a white/cream box in the center for the logo - const boxSize = 200; - const boxX = (WIDTH - boxSize) / 2; - const boxY = 135; - - ctx.fillStyle = '#f5f5f0'; - ctx.fillRect(boxX, boxY, boxSize, boxSize); - - // Load and draw the logo SVG - // For SVG, we need to use a workaround - load it as an image - // The logo SVG needs to be converted first, or we can embed a simplified version - - // Draw a simplified version of the logo directly - // Purple loop with fuchsia node + // Draw glyph — neutral-100 const centerX = WIDTH / 2; - const centerY = boxY + boxSize / 2; - const radius = 50; + const glyphY = 200; - // Draw the gapped circle (loop) - ctx.strokeStyle = '#7c3aed'; - ctx.lineWidth = 16; + ctx.strokeStyle = '#e8e8e8'; + ctx.lineWidth = 8; ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; - const startAngle = (Math.PI / 180) * 10; - const endAngle = (Math.PI / 180) * 280; - + // < bracket ctx.beginPath(); - ctx.arc(centerX, centerY, radius, startAngle, endAngle); + ctx.moveTo(centerX - 40, glyphY - 40); + ctx.lineTo(centerX - 80, glyphY); + ctx.lineTo(centerX - 40, glyphY + 40); ctx.stroke(); - // Draw the node (small square at top-right) - matches logo.svg positioning - const nodeSize = 22; - const nodeAngle = 36.9; // Match logo.svg (was 45°, causing 8.1° misalignment) - const nodeX = centerX + Math.cos((Math.PI / 180) * nodeAngle) * radius - nodeSize / 2; - const nodeY = centerY - Math.sin((Math.PI / 180) * nodeAngle) * radius - nodeSize / 2; + // / slash + ctx.beginPath(); + ctx.moveTo(centerX + 20, glyphY - 50); + ctx.lineTo(centerX - 20, glyphY + 50); + ctx.stroke(); - ctx.fillStyle = '#ec4899'; + // > bracket ctx.beginPath(); - ctx.roundRect(nodeX, nodeY, nodeSize, nodeSize, 5); - ctx.fill(); + ctx.moveTo(centerX + 40, glyphY - 40); + ctx.lineTo(centerX + 80, glyphY); + ctx.lineTo(centerX + 40, glyphY + 40); + ctx.stroke(); - // Add title text - ctx.fillStyle = '#ffffff'; - ctx.font = 'bold 90px sans-serif'; + // Title — Space Grotesk Bold (display font) + ctx.fillStyle = '#e8e8e8'; + ctx.font = 'bold 80px "Space Grotesk"'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; + ctx.fillText('Agentic Coding', centerX, 380); - const titleY = boxY + boxSize + 100; - ctx.fillText('AI Coding Course', centerX, titleY); + // Subtitle — Inter (body font) + ctx.fillStyle = '#9b9b9b'; + ctx.font = '32px "Inter"'; + ctx.fillText('Master AI-assisted software engineering', centerX, 450); - // Add subtitle text - ctx.font = '32px sans-serif'; - const subtitleY = titleY + 70; - ctx.fillText('Master AI-assisted software engineering for experienced developers', centerX, subtitleY); + // 3px accent line — cyan-400 + ctx.fillStyle = '#00b2b2'; + ctx.fillRect(centerX - 150, 500, 300, 3); // Convert to buffer and save const buffer = canvas.toBuffer('image/png'); @@ -95,7 +83,7 @@ async function generateSocialCard() { await writeFile(outputPath, buffer); - console.log(`✓ Social card generated: ${outputPath}`); + console.log(`Social card generated: ${outputPath}`); console.log(` Dimensions: ${WIDTH}x${HEIGHT}px`); console.log(` File size: ${(buffer.length / 1024).toFixed(1)}KB`); } diff --git a/scripts/package-lock.json b/scripts/package-lock.json index e08d319..41507e1 100644 --- a/scripts/package-lock.json +++ b/scripts/package-lock.json @@ -9,7 +9,19 @@ "version": "1.0.0", "dependencies": { "@google/generative-ai": "^0.24.1", - "canvas": "^3.2.0" + "canvas": "^3.2.0", + "png-to-ico": "^2.1.8", + "sharp": "^0.33.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@google/generative-ai": { @@ -21,6 +33,373 @@ "node": ">=18.0.0" } }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -96,6 +475,47 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "license": "ISC" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -191,6 +611,12 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -251,6 +677,32 @@ "wrappy": "1" } }, + "node_modules/png-to-ico": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/png-to-ico/-/png-to-ico-2.1.8.tgz", + "integrity": "sha512-Nf+IIn/cZ/DIZVdGveJp86NG5uNib1ZXMiDd/8x32HCTeKSvgpyg6D/6tUBn1QO/zybzoMK0/mc3QRgAyXdv9w==", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.36", + "minimist": "^1.2.6", + "pngjs": "^6.0.0" + }, + "bin": { + "png-to-ico": "bin/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "license": "MIT", + "engines": { + "node": ">=12.13.0" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -348,6 +800,45 @@ "node": ">=10" } }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -393,6 +884,15 @@ "simple-concat": "^1.0.0" } }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -439,6 +939,13 @@ "node": ">=6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/scripts/package.json b/scripts/package.json index 9448510..3153c9b 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -8,10 +8,15 @@ "generate-podcast": "node generate-podcast.js", "generate-podcast-batch": "node generate-podcast.js --all", "generate-podcast-script-only": "node generate-podcast.js --script-only", - "generate-podcast-audio-only": "node generate-podcast.js --audio-only" + "generate-podcast-audio-only": "node generate-podcast.js --audio-only", + "generate-social-card": "node generate-social-card.js", + "generate-favicons": "node generate-favicons.js", + "generate-brand-assets": "node generate-social-card.js && node generate-favicons.js", + "fetch-emoji": "node fetch-emoji.js" }, "dependencies": { "@google/generative-ai": "^0.24.1", - "canvas": "^3.2.0" + "png-to-ico": "^2.1.8", + "sharp": "^0.33.0" } } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock new file mode 100644 index 0000000..e6b70d4 --- /dev/null +++ b/src-tauri/Cargo.lock @@ -0,0 +1,4914 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "agentic-coding" +version = "0.1.0" +dependencies = [ + "tauri", + "tauri-build", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.11.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cc" +version = "1.2.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link 0.2.1", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.11.0", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.0", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.29.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "matches", + "phf 0.10.1", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dom_query" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d9c2e7f1d22d0f2ce07626d259b8a55f4a47cb0938d4006dd8ae037f17d585e" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash 0.2.0", + "html5ever 0.36.1", + "precomputed-hash", + "selectors 0.35.0", + "tendril", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "embed-resource" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.9.12+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.11.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" +dependencies = [ + "log", + "mac", + "markup5ever 0.14.1", + "match_token", +] + +[[package]] +name = "html5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +dependencies = [ + "log", + "markup5ever 0.36.1", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.11.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.8-speedreader" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" +dependencies = [ + "cssparser 0.29.6", + "html5ever 0.29.1", + "indexmap 2.13.0", + "selectors 0.24.0", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "libc", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" +dependencies = [ + "log", + "phf 0.11.3", + "phf_codegen 0.11.3", + "string_cache 0.8.9", + "string_cache_codegen 0.5.4", + "tendril", +] + +[[package]] +name = "markup5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.61.2", +] + +[[package]] +name = "muda" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png", + "serde", + "thiserror 2.0.18", + "windows-sys 0.60.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.11.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.11.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.11.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared 0.8.0", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.2", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plist" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +dependencies = [ + "base64 0.22.1", + "indexmap 2.13.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.4+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" +dependencies = [ + "bitflags 1.3.2", + "cssparser 0.29.6", + "derive_more 0.99.20", + "fxhash", + "log", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fdfed56cd634f04fe8b9ddf947ae3dc493483e819593d2ba17df9ad05db8b2" +dependencies = [ + "bitflags 2.11.0", + "cssparser 0.36.0", + "derive_more 2.1.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash", + "servo_arc 0.4.3", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_with" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "servo_arc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.34.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d52c379e63da659a483a958110bbde891695a0ecb53e48cc7786d5eda7bb" +dependencies = [ + "bitflags 2.11.0", + "block2", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "parking_lot", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da77cc00fb9028caf5b5d4650f75e31f1ef3693459dfca7f7e506d1ecef0ba2d" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.18", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "toml 0.9.12+spec-1.1.0", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a24476afd977c5d5d169f72425868613d82747916dd29e0a357c84c4bd6d29" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.117", + "tauri-utils", + "thiserror 2.0.18", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39b349a98dadaffebb73f0a40dcd1f23c999211e5a2e744403db384d0c33de7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-runtime" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2826d79a3297ed08cd6ea7f412644ef58e32969504bc4fbd8d7dbeabc4445ea2" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219a1f983a2af3653f75b5747f76733b0da7ff03069c7a41901a5eb3ace4557d" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dunce", + "glob", + "html5ever 0.29.1", + "http", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.3", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" +dependencies = [ + "dunce", + "embed-resource", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.13.0", + "serde_core", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.13.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime 1.0.0+spec-1.1.0", + "toml_parser", + "winnow 0.7.15", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ + "winnow 0.7.15", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.11.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png", + "serde", + "thiserror 2.0.18", + "windows-sys 0.60.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +dependencies = [ + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "wry" +version = "0.54.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24eda84b5d488f99344e54b807138896cee8df0b2d16c793f1f6b80e6d8df1f" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..21fa204 --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "agentic-coding" +version = "0.1.0" +edition = "2021" + +[lib] +name = "app_lib" + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } + +[profile.release] +strip = true +lto = true +codegen-units = 1 +panic = "abort" diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..261851f --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build(); +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..13bd86e --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,6 @@ +{ + "identifier": "default", + "description": "Default capabilities", + "windows": ["main"], + "permissions": ["core:default"] +} diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..470479b Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..dd3dded Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..80321f2 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/64x64.png b/src-tauri/icons/64x64.png new file mode 100644 index 0000000..535b84b Binary files /dev/null and b/src-tauri/icons/64x64.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..0133d68 Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..4812571 Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..8762526 Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..e19fb6f Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..7924a38 Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..0a1b549 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..e335df0 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..54c86bd Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..a346875 Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..b860ea3 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..2ffbf24 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src-tauri/icons/android/values/ic_launcher_background.xml b/src-tauri/icons/android/values/ic_launcher_background.xml new file mode 100644 index 0000000..ea9c223 --- /dev/null +++ b/src-tauri/icons/android/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #fff + \ No newline at end of file diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..9a0e140 Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..59397d6 Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..99f2380 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..0d94ef8 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,5 @@ +pub fn run() { + tauri::Builder::default() + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..c672c6a --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,5 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + app_lib::run(); +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..14025b0 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-cli/schema.json", + "productName": "Agentic Coding", + "version": "0.1.0", + "identifier": "ai.agenticoding.desktop", + "build": { + "beforeBuildCommand": "cd website && TAURI_BUILD=1 npm run build", + "beforeDevCommand": "cd website && npm run start", + "devUrl": "http://localhost:3000", + "frontendDist": "../website/build" + }, + "app": { + "windows": [ + { + "title": "Agentic Coding", + "width": 1200, + "height": 800, + "minWidth": 800, + "minHeight": 600 + } + ], + "security": { + "csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' asset:; connect-src 'self'" + } + }, + "bundle": { + "active": true, + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "macOS": { + "signingIdentity": "-" + } + } +} diff --git a/website/developer-tools/cli-coding-agents.md b/website/developer-tools/cli-coding-agents.md index d6514c7..1b47739 100644 --- a/website/developer-tools/cli-coding-agents.md +++ b/website/developer-tools/cli-coding-agents.md @@ -15,7 +15,7 @@ This page covers agents that operate primarily from the command line—distinct **Key differentiators:** -- **Hierarchical CLAUDE.md:** Multi-level context files (global → project → subdirectory) that [merge automatically based on working directory](/docs/practical-techniques/lesson-6-project-onboarding)—define coding standards at project root, override per-module, and set personal preferences globally +- **Hierarchical CLAUDE.md:** Multi-level context files (global → project → subdirectory) that [merge automatically based on working directory](/practical-techniques/lesson-6-project-onboarding)—define coding standards at project root, override per-module, and set personal preferences globally - **Sub-agents via Task(...):** Spawn isolated agent instances for parallel research, code exploration, or specialized tasks without polluting main context - **Planning mode:** Explicit plan-before-execute workflow for complex changes—align on approach before any files are modified - **Hooks system:** Deterministic pre/post-execution rules for validation, formatting, or custom workflows triggered at specific points diff --git a/website/developer-tools/cli-tools.md b/website/developer-tools/cli-tools.md index 7b32e54..497c5de 100644 --- a/website/developer-tools/cli-tools.md +++ b/website/developer-tools/cli-tools.md @@ -412,6 +412,6 @@ agent-browser's ref-based approach (`@e1`, `@e2`) produces deterministic element **Related Course Content:** -- [Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution) - Multi-worktree workflows leveraging these CLI tools +- [Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution) - Multi-worktree workflows leveraging these CLI tools - [Developer Tools: Terminals](/developer-tools/terminals) - Terminal recommendations for running these CLI tools efficiently - [Developer Tools: MCP Servers](/developer-tools/mcp-servers) - Extend CLI agents with code research and web grounding diff --git a/website/developer-tools/mcp-servers.md b/website/developer-tools/mcp-servers.md index c747764..53157ca 100644 --- a/website/developer-tools/mcp-servers.md +++ b/website/developer-tools/mcp-servers.md @@ -37,7 +37,7 @@ uv tool install chunkhound Requires Python 3.10+ and the uv package manager. See [ChunkHound on GitHub](https://github.com/chunkhound/chunkhound) for API key configuration and setup details. -**Learn more:** [Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#deep-dive-chunkhound-architecture) covers ChunkHound's architecture, pipeline design, and scale guidance in detail. +**Learn more:** [Lesson 5: Grounding](/methodology/lesson-5-grounding#deep-dive-chunkhound-architecture) covers ChunkHound's architecture, pipeline design, and scale guidance in detail. ## Web Research @@ -69,7 +69,7 @@ brew install arguseek Requires Go 1.23+ and Google API credentials. See [ArguSeek on GitHub](https://github.com/ArguSeek/arguseek) for detailed setup instructions and configuration options. -**Learn more:** [Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#deep-dive-arguseek-architecture) explains ArguSeek's architecture, semantic subtraction, and research patterns. +**Learn more:** [Lesson 5: Grounding](/methodology/lesson-5-grounding#deep-dive-arguseek-architecture) explains ArguSeek's architecture, semantic subtraction, and research patterns. ## Browser Automation @@ -91,5 +91,5 @@ Previous recommendations included Playwright MCP and Chrome DevTools MCP. These **Related Course Content:** -- [Lesson 5: Grounding](/docs/methodology/lesson-5-grounding) - Detailed architecture and use cases for ChunkHound and ArguSeek -- [Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution) - Multi-agent workflows that leverage MCP capabilities +- [Lesson 5: Grounding](/methodology/lesson-5-grounding) - Detailed architecture and use cases for ChunkHound and ArguSeek +- [Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution) - Multi-agent workflows that leverage MCP capabilities diff --git a/website/developer-tools/terminals.md b/website/developer-tools/terminals.md index 5f182ed..bc4e80c 100644 --- a/website/developer-tools/terminals.md +++ b/website/developer-tools/terminals.md @@ -102,4 +102,4 @@ Use ArguSeek to research best practices for your chosen terminal—session manag **Related:** - [Developer Tools: Modern CLI Tools](/developer-tools/cli-tools) - The ecosystem that completes your terminal-based development environment -- [Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution) - Multi-worktree workflows leveraging modern terminals +- [Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution) - Multi-worktree workflows leveraging modern terminals diff --git a/website/docs/CLAUDE.md b/website/docs/CLAUDE.md index 9cd61a9..7d8b37b 100644 --- a/website/docs/CLAUDE.md +++ b/website/docs/CLAUDE.md @@ -1,4 +1,4 @@ -# Course Content - Writing Standards +# Content - Writing Standards ## Target Audience @@ -15,7 +15,7 @@ ### Voice -**Coworker-level communication** - Talk to peers, not students +**Coworker-level communication** - Talk to peers, not readers learning basics - Direct and concise - Professional but conversational @@ -63,12 +63,6 @@ sidebar_position: X ## Content [Main lesson content with examples] - -## Key Takeaways - -- Summary point 1 -- Summary point 2 -- Summary point 3 ``` ## Code Examples @@ -149,4 +143,4 @@ Each lesson should reference prerequisites when needed: - Build on previous concepts - Reference earlier examples when relevant -- Link to related lessons: `[See Lesson 2: Agents Demystified](../fundamentals/lesson-2-how-agents-work.md)` +- Link to related lessons: `[See Lesson 2: Agents Demystified](../fundamentals/lesson-2-how-agents-work.mdx)` diff --git a/website/docs/about.md b/website/docs/about.md new file mode 100644 index 0000000..a00c187 --- /dev/null +++ b/website/docs/about.md @@ -0,0 +1,75 @@ +--- +title: About +sidebar_label: About +description: 'Ofri Wolfus — Senior Software Architect, ex-Google engineer, and creator of ChunkHound, ArguSeek, and GoatDB. Author of Agentic Coding, a technical reference for senior engineers on AI-assisted development.' +keywords: [Ofri Wolfus, agentic coding, ChunkHound, ArguSeek, GoatDB, AI-assisted development, software architect, open source, distributed systems, MCP] +--- + +import SchemaMarkup from '@site/src/components/SchemaMarkup'; +import { AuthorWaveNode } from '@site/src/components/VisualElements/ActorNodes'; + +export const personData = { + name: 'Ofri Wolfus', + jobTitle: 'Senior Software Architect', + worksFor: 'Applied Materials', + url: 'https://agenticoding.ai/about', + sameAs: [ + 'https://www.linkedin.com/in/ofriwolfus/', + 'https://github.com/ofriw', + 'https://goatdb.dev', + 'https://github.com/goatplatform/goatdb', + 'https://github.com/chunkhound/chunkhound', + 'https://github.com/ArguSeek/arguseek', + ], + knowsAbout: [ + 'agentic coding', + 'codebase intelligence', + 'distributed databases', + 'compilers', + 'LLM applications', + 'retrieval-augmented generation', + 'software architecture', + 'embedded systems', + ], + alumniOf: ['Google', 'Bar-Ilan University'], +}; + + + +## Author + +I'm **Ofri Wolfus**, Senior Software Architect at Applied Materials and an ex-Google engineer (Google APIs for Mac, WebKit renderer contributions, Chrome BiDi/RTL). I created [ChunkHound](https://chunkhound.github.io) and [ArguSeek](https://github.com/ArguSeek/arguseek) — the open-source tools used as reference implementations throughout this course. + +
+ +
+ +Prior to my current role, I was Co-Founder & CTO of Ovvio.io (real-time collaborative work management) and first employee at Photomyne, where I scaled cloud infrastructure to 100M photos and 10M installs and wrote on-device neural network inference pre-TensorFlow. I co-founded EasyFit Orthopedics, where I designed, built, and patented a smart prosthetic leg interface (WO2015103506A1) — hardware, electronics, and embedded software. I was also an early contributor to Growl and Adium, two foundational Mac open-source projects. + +BS Computer Science, Bar-Ilan University. 2 patents. [LinkedIn](https://www.linkedin.com/in/ofriwolfus/) · [GitHub](https://github.com/ofriw) + +## Open Source + +**[ChunkHound](https://chunkhound.github.io)** — Like having a core dev who's been on the project since day one, ready to answer any question — except it works across millions of lines and 30+ languages. Point your AI assistant at a codebase and ChunkHound gives it the kind of deep, structural understanding that normally takes months to build: where things live, why they're built that way, and what depends on what. Runs entirely on your machine. The code grounding tool used throughout this course. **1.1k GitHub stars.** ([GitHub](https://github.com/chunkhound/chunkhound)) + +**[ArguSeek](https://github.com/ArguSeek/arguseek)** — The colleague who always knows what's happening outside the building. When your AI assistant needs current information — is this library still maintained? what changed in the latest API version? any known CVEs? — ArguSeek runs parallel web searches, synthesizes 12+ sources in about 40 seconds, and flags promotional content automatically. Fast breadth over slow depth, for the hundreds of micro-decisions that make up a real coding day. The domain grounding tool used throughout this course. ([GitHub](https://github.com/ArguSeek/arguseek)) + +**[GoatDB](https://goatdb.dev)** — The entire backend in a single import. GoatDB collapses database, server, and sync layer into one TypeScript module — no SQL, no REST endpoints, no separate infrastructure to provision. Define a schema, call `useQuery`, and your UI stays in sync across devices, offline included. Under the hood: a Git-style commit graph with three-way merges gives every document a full version history and automatic conflict resolution, while cryptographic signatures let clients restore crashed servers from their own replicas. Ship a single Deno binary on a $5 VM and you have a production-grade, self-healing backend. Ideal for the kind of fast-iteration, single-tenant apps that agentic workflows produce — because the less operational surface area an AI has to manage, the more reliably it ships. **500+ GitHub stars.** ([GitHub](https://github.com/goatplatform/goatdb)) + +## Why This Reference + +I built this course on 20+ years of production experience across compiled languages, embedded systems, cloud infrastructure, and LLM/RAG applications — not in theory. ChunkHound and ArguSeek were built specifically to solve the grounding problems this course teaches: code context collapse, knowledge cutoff hallucination, and context window management at scale. See [Lesson 5](./methodology/lesson-5-grounding) for architecture deep-dives on both tools. + +I taught iOS, Android, and web development courses at John Bryce (2013–2016), including corporate training programs for client companies. + +## Built With + +**Platform:** [Docusaurus](https://docusaurus.io) — open-source static site generator by Meta. Presentations powered by [Reveal.js](https://revealjs.com) by Hakim El Hattab. Diagrams rendered with [Mermaid](https://mermaid.js.org) by Knut Sveidqvist. + +**Typography:** Body text set in [Inter](https://rsms.me/inter/) by Rasmus Andersson. Headings in [Space Grotesk](https://floriankarsten.github.io/space-grotesk/) by Florian Karsten. Code rendered in [Monaspace](https://monaspace.githubnext.com/) by GitHub Next — a superfamily of five metrics-compatible monospace typefaces (Neon, Argon, Xenon, Radon, Krypton) used as a semantic voice system throughout the course. All fonts self-hosted under SIL OFL 1.1. + +**Illustrations:** Diagrams derived from [Noto Emoji SVG](https://github.com/adobe-fonts/noto-emoji-svg) by Adobe. + +## Copyright + +© {new Date().getFullYear()} Ofri Wolfus diff --git a/website/docs/experience-engineering/lesson-14-design-tokens.md b/website/docs/experience-engineering/lesson-14-design-tokens.md new file mode 100644 index 0000000..a6d3a92 --- /dev/null +++ b/website/docs/experience-engineering/lesson-14-design-tokens.md @@ -0,0 +1,372 @@ +--- +sidebar_position: 1 +sidebar_label: 'Design Tokens & Visual Primitives' +sidebar_custom_props: + sectionNumber: 14 +title: 'Design Tokens & Visual Primitives' +--- + +[Lesson 12](/practical-techniques/lesson-12-spec-driven-development) established that specs are scaffolding — temporary thinking tools deleted after implementation. This section applies spec-driven development to user-facing interfaces. We call this **Experience Engineering** — specifying design tokens, component APIs, interaction flows, and accessibility architecture precisely enough for an agent to implement and browser automation to verify. For system-level specs, see [Lesson 13](/practical-techniques/lesson-13-systems-thinking-specs). + +The key insight: **the experience layer can be fully built and validated before any backend exists.** Design tokens, component layouts, interaction flows, accessibility constraints — none of these depend on API responses. An agent generates the UI with mocked data (behavior mocks via MSW), browser automation verifies it through the accessibility tree (`snapshot -ic`), and you iterate on the experience until it's right. Backend integration comes later, and by then the interface contract is locked. + +This lesson walks through building a production color palette using [Lesson 3](/methodology/lesson-3-high-level-methodology)'s four-phase workflow — **Research → Plan → Execute → Validate** — applied to experience engineering. The running example throughout these lessons: a **team dashboard for a SaaS billing product** — subscription plans, usage metrics, invoices, team management, and settings. + +### Why Agents Need You for UI + +**The review paradox.** UI code is the hardest to review because correctness is visual and behavioral, not just logical. You can't grep for "the button is in the wrong place" or "the modal doesn't feel right." This makes the spec → build → check loop even more critical for frontend work — the spec defines what "correct" means, browser automation checks it deterministically, and the accessibility tree makes it machine-readable. + +**The perceptual limitation.** Multimodal LLMs process screenshots through Vision Transformers that divide images into fixed patches — typically 16×16 pixels each. A 1920×1080 screenshot becomes roughly 1,100 tokens representing over two million pixels — each patch compressed into a single embedding vector. The model sees major structural features clearly: missing sections, wrong layout direction, a red error banner where a green success banner should be. But sub-patch details — a 3px spacing difference, a 1px border, the distinction between `gray-200` and `gray-300` — fall below the resolution floor. Vision is excellent for broad guidance during development. It cannot do exact verification or perceptual evaluation. + +This is why the four-phase workflow matters for experience engineering: the agent handles math and generation (Execute), but **you** provide perceptual judgment (Validate). The division of labor is driven by a known architectural limitation, not preference. + +## Three-Tier Token Architecture + +Design tokens are the foundational design decisions of the UI — colors, spacing, typography, shadows. When they change, the impact ripples through every component, layout, and flow. + +| Tier | Name | Example | Purpose | +|------|------|---------|---------| +| Primitive | Raw values | `blue-500: oklch(0.55 0.15 250)` | Color space, no semantics | +| Semantic | Intent | `color-bg-primary: {blue-500}` | Meaning, theme-switchable | +| Component | Scoped | `button-bg: {color-bg-primary}` | Component-specific | + +Components reference **semantic** tokens, never primitives. A theme swap changes only the primitive→semantic mapping. Components don't know or care. + +Brand colors are your judgment. Everything derivative — shade scales, harmony colors, dark mode variants, contrast validation — is computable math. + +## Research: Grounding Color Decisions + +[Lesson 3](/methodology/lesson-3-high-level-methodology)'s first phase: ground the agent in both your codebase and domain knowledge before planning changes. For color systems, that means understanding the color science, studying how competitors handle palettes, and confirming accessibility requirements — before you pick a single hue. + +### Domain Research with ArguSeek + +You wouldn't pick a database without researching the options. Same principle applies to color decisions. [ArguSeek](/methodology/lesson-5-grounding#arguseek-isolated-context--state)'s `research_iteratively` builds cumulative knowledge across queries through semantic subtraction — each follow-up skips already-covered content and advances the research: + +``` +Q1: "OKLCH color space best practices for design systems and token generation" + → 18 sources: perceptual uniformity, gamut mapping, CSS Color 4 spec + → Returns ~3,000 tokens + +Q2: "SaaS billing dashboard color palettes — Stripe, Linear, Vercel design approaches" + → 22 sources, skips OKLCH basics from Q1, focuses on competitor patterns + → Returns ~3,400 tokens (competitor-specific, no repeated theory) + +Q3: "WCAG 2.1 AA contrast requirements for design token systems" + → 15 sources, skips color theory and competitor patterns already covered + → Returns ~2,800 tokens (accessibility-specific) + +Total: 55 sources scanned, ~9,200 tokens to your orchestrator +``` + +After three queries you have grounded knowledge on the color space (OKLCH over HSL), competitor precedent (what works in production SaaS dashboards), and the accessibility constraints your palette must satisfy (WCAG AA ≥ 4.5:1). All without polluting your main context — ArguSeek runs in isolated context per [Lesson 5](/methodology/lesson-5-grounding)'s sub-agent pattern. + +### Visual Research with agent-browser + +Text research tells you *about* competitor palettes. Screenshots show you the actual result. Use agent-browser to capture competitor dashboards for **your** visual analysis: + +``` +open "https://stripe.com/billing" +screenshot /tmp/stripe-billing.png + +open "https://linear.app" +screenshot /tmp/linear-dashboard.png +``` + +The agent captures the screenshots. **You** evaluate them — which color temperature feels right for a billing product? How do competitors handle the neutral scale? Does Stripe's blue feel more trustworthy than Linear's purple? These are perceptual judgments the agent cannot make (the VT limitation above). But the agent saved you from manually navigating, waiting for page loads, and managing screenshot files. + +| Actor | Research Phase Responsibility | +|-------|-------------------------------| +| Human | Decide what to research, evaluate visual inspiration, form brand opinions | +| Agent | Retrieve and synthesize domain knowledge (ArguSeek), capture competitor screenshots (agent-browser) | + +## Plan: Source Hues and Token Spec + +Research complete. Now lock in the design decisions that drive everything downstream. This is [Lesson 3](/methodology/lesson-3-high-level-methodology)'s Plan phase — and for color palettes, it's "Exact Planning": the solution structure is known (OKLCH shade scales), so be directive with specificity and constraints. + +### Source Hue Selection (Human Judgment) + +The shade-scale algorithm works for any hue. A production palette applies it to **multiple independent source hues** — not computed harmonies, but convention-fixed colors that users already associate with specific meanings. + +**Source hues for the billing dashboard:** + +| Role | Hue | Peak Chroma | Rationale | +|------|-----|-------------|-----------| +| Primary | 250° | 0.15 | Brand blue — identity, CTAs, active states | +| Neutral | 250° | 0.015 | Brand-tinted gray — same hue, ~10% chroma | +| Error | 25° | 0.15 | Convention: red-family for danger | +| Warning | 70° | 0.12 | Convention: amber for caution | +| Success | 155° | 0.13 | Teal-green — colorblind-safe vs. error red | + +Error is red because users expect red for danger — not because red is a harmony of blue. Success uses teal-green rather than pure green because teal remains distinguishable from error-red under protanopia and deuteranopia (the two most common forms of color vision deficiency). Neutral is the brand hue with chroma stripped to ~0.015: this produces a brand-tinted gray that feels cohesive without being colorful. This project's own `custom.css` uses exactly this pattern: `--semantic-success: #06b6d4` (cyan), `--semantic-error: #e11d48` (rose-red) — independently chosen, not derived from `--brand-primary: #007576`. + +These five hue choices are **the** human judgment calls in this lesson. Everything downstream — shade generation, contrast checking, semantic mapping, dark mode derivation — is math the agent computes. + +### The Shade Scale Spec (Agent Math) + +HSL looks intuitive but isn't perceptually uniform — `hsl(60,100%,50%)` (yellow) appears far brighter than `hsl(240,100%,50%)` (blue) despite identical lightness values. OKLCH fixes this by design, which is why the primitive tokens above use it. From a single brand color, the agent writes and executes code to derive the entire primitive tier. This is [Lesson 4](/methodology/lesson-4-prompting-101)'s math-as-code principle in action: LLMs can't do arithmetic reliably, but they write code that does — color math is a perfect application. + +**What one brand color yields:** + +| Step | Output | Method | +|------|--------|--------| +| Shade scale (50–950) | 11 perceptually even shades | Non-linear lightness curve in OKLCH L channel | +| Hue compensation | Corrected hue per shade | Bezold-Brücke shift (colors drift toward yellow/purple with lightness) | +| Chroma curve | Balanced saturation | Parabolic — peak at midtones, taper at extremes | +| Harmony colors | Decorative accents, data-viz variety | Hue rotation in OKLCH H channel (not semantic roles — [see below](#color-harmony-for-decorative-variety)) | +| Dark mode | Inverted palette | Lightness inversion with chroma preservation | +| Contrast pairs | Valid text/bg combinations | WCAG AA ≥ 4.5:1 validation (A-010) | + +This table is the spec: it tells the agent exactly what to compute and how. Five hues × 11 shades = 55 primitive tokens, plus semantic mappings and contrast pairs. + +### Color Harmony for Decorative Variety + +Harmony rotation produces visually cohesive hue sets for data visualization, illustration palettes, and decorative accents — anywhere you need multiple distinct hues that feel related. It is **not** how semantic roles (error, warning, success) are assigned. Those are convention-fixed. + +| Harmony | Rotation | Best For | +|---------|----------|----------| +| Monochromatic | 0° (shade scale only) | Single-brand UIs, dashboards | +| Analogous | ±30° | Harmonious palettes, gradients | +| Complementary | 180° | CTAs, alerts, high-contrast accents | +| Split-complementary | 150° + 210° | Balanced accent pairs | +| Triadic | ±120° | Multi-brand, data visualization | +| Tetradic | 90° intervals | Complex UIs needing 4+ distinct hues | + +Which harmonies to include is design judgment. The calculation is the agent's job. + +### Token Assumptions Table + +| Token Assumption | Source | Drives | +|------------------|--------|--------| +| Spacing base = 8px, scale = 0.5x/1x/1.5x/2x/3x/4x | Design system | All component padding, margins, gaps | +| Type scale ratio = 1.25 (Major Third) | Typography standards | All font sizes: base × ratio^n | +| Min contrast = 4.5:1 (WCAG AA) | Accessibility requirement | A-010, all text/bg combinations | +| OKLCH gamut = sRGB with fallbacks | Browser support | All color tokens need sRGB fallback | +| Every semantic background has a paired foreground token | Production design systems (M3, shadcn) | A-010, all text/bg combinations across themes | + +The **Drives** column creates traceability from token decision to spec element. When a design decision changes (new brand colors, updated spacing scale, tighter accessibility requirements), the Drives column tells both you and the agent which constraints to re-verify. + +## Execute: Agent-Generated Palette + +Plan locked. The agent now writes and runs TypeScript to compute the entire palette — [Lesson 3](/methodology/lesson-3-high-level-methodology)'s Execute phase. The prompt below feeds the source hues and algorithm spec directly to the agent. This is [Lesson 4](/methodology/lesson-4-prompting-101)'s math-as-code principle: the agent can't do color arithmetic reliably, but it writes code that does. + +### The Prompt + +Demonstrates [Lesson 4](/methodology/lesson-4-prompting-101) principles — imperative commands, constraints as guardrails, chain-of-thought, structure, math-as-code: + +``` +Write a TypeScript script that generates a production color palette from +source hues. Use OKLCH for all calculations; output both OKLCH values +and sRGB hex fallbacks. + +## Input — Source Hues +| Role | Hue | Peak Chroma | Notes | +|---------|-----|-------------|------------------------------| +| primary | 250 | 0.15 | Brand blue | +| neutral | 250 | 0.015 | Brand-tinted gray | +| error | 25 | 0.15 | Red-family for danger | +| warning | 70 | 0.12 | Amber for caution | +| success | 155 | 0.13 | Teal-green, colorblind-safe | + +## Steps +1. For each source hue, generate shade scale (50–950, 11 stops): + - Map lightness non-linearly: L=0.97 at 50, L=0.25 at 950 + - Apply Bezold-Brücke hue compensation per shade + - Use parabolic chroma curve: peak at mid-lightness, taper at extremes + - Neutral scale uses same curve but capped at peak chroma 0.015 +2. Build semantic mapping for light and dark themes: + - Light: bg-page=neutral-50, text-primary=neutral-900, + primary=primary-600, error=error-600, etc. + - Dark: bg-page=neutral-900, text-primary=neutral-50, + primary=primary-400, error=error-400, etc. + - Use shade 600 for light-mode semantic colors (passes AA on white) + - Use shade 400 for dark-mode semantic colors (passes AA on dark bg) +3. Compute paired foreground tokens: + - For every background semantic token, find the shade with + highest contrast ≥ 4.5:1 (prefer white/black, then lightest/darkest) + - Output as --on-primary, --on-error, etc. +4. Validate every semantic text/background pair: + - WCAG AA ≥ 4.5:1 for normal text, ≥ 3:1 for large text + - Flag failing pairs with suggested fix (adjust shade until passing) +5. Output as CSS custom properties organized by tier: + - Primitives: --color-primary-500: oklch(...) + - Semantic (light): --color-primary: var(--color-primary-600) + - Semantic (dark): --color-primary: var(--color-primary-400) + - Include sRGB fallback for every primitive + +## Constraints +- All math in OKLCH — sRGB only for fallback output +- Gamut-map out-of-range values by clamping chroma, preserving hue +- Every generated value must include both OKLCH and hex +- 5 roles × 11 shades = 55 primitives + ~15 semantic tokens per theme +``` + +The prompt structure maps directly to [Lesson 4](/methodology/lesson-4-prompting-101) principles: imperative commands ("Write a script that..."), constraints as guardrails (WCAG thresholds, color space, gamut), chain-of-thought (numbered steps), markdown structure for information density, and math-as-code — the agent writes and runs the calculation rather than guessing color values. The input table separates designer judgment (which hues, which roles) from agent work (shade generation, contrast checking, semantic mapping). + +### What the Agent Produces + +The interactive component below demonstrates what the agent outputs from ~80 lines of TypeScript. Your job at this stage: run it, look at the result, and form a perceptual opinion before moving to the Validate phase. Try changing the hue from 250 to 30 (orange) — the entire palette recalculates. + +import ColorPaletteGenerator from '@site/src/components/VisualElements/ColorPaletteGenerator'; + + + +**Concrete example.** Here's what one brand color (`oklch(0.55 0.15 250)` — the blue-500 already in the token table) produces: + +export const shadeExample = [ + { shade: '50', oklch: '0.91 0.042 254', hex: '#cfe5ff', vsW: '1.3:1', vsB: '16.3:1', text: 'Black' }, + { shade: '100', oklch: '0.87 0.066 253', hex: '#b6d7ff', vsW: '1.5:1', vsB: '14.1:1', text: 'Black' }, + { shade: '200', oklch: '0.79 0.111 252', hex: '#85beff', vsW: '1.9:1', vsB: '10.8:1', text: 'Black' }, + { shade: '300', oklch: '0.71 0.150 252', hex: '#53a6fc', vsW: '2.6:1', vsB: '8.2:1', text: 'Black' }, + { shade: '400', oklch: '0.64 0.150 251', hex: '#398fe3', vsW: '3.4:1', vsB: '6.2:1', text: 'Black' }, + { shade: '500', oklch: '0.57 0.150 250', hex: '#1b7acb', vsW: '4.5:1', vsB: '4.7:1', text: 'Either' }, + { shade: '600', oklch: '0.50 0.140 249', hex: '#0067b0', vsW: '5.9:1', vsB: '3.6:1', text: 'White' }, + { shade: '700', oklch: '0.44 0.121 248', hex: '#005591', vsW: '7.8:1', vsB: '2.7:1', text: 'White' }, + { shade: '800', oklch: '0.37 0.084 248', hex: '#14446b', vsW: '10.2:1', vsB: '2.1:1', text: 'White' }, + { shade: '900', oklch: '0.31 0.031 247', hex: '#243240', vsW: '13.0:1', vsB: '1.6:1', text: 'White' }, + { shade: '950', oklch: '0.28 0.000 246', hex: '#292929', vsW: '14.6:1', vsB: '1.4:1', text: 'White' }, +]; + + + + +{shadeExample.map(s => ( + + + + + + + + +))} + +
ShadeOKLCHColorvs Whitevs BlackText
{s.shade}oklch({s.oklch}){s.hex}{s.vsW}{s.vsB}{s.text}
+ +Note the three algorithms at work: lightness drops non-linearly (0.91→0.28), chroma peaks at 300–400 then tapers (parabolic curve), and hue drifts subtly from 254→246 (Bezold-Brücke compensation). Every shade has a valid WCAG AA text color. The crossover at shade 500–600 is where text switches from black to white — this is computed, not chosen. + +## Validate: The Perceptual Loop + +[Lesson 3](/methodology/lesson-3-high-level-methodology)'s Validate phase closes the loop — and for experience engineering, this is where the human-agent division of labor matters most. The agent handles automated validation (contrast math, gamut checks). You handle perceptual validation (does it look right?). Neither can do the other's job. + +### Automated Validation (Agent) + +The agent validates every generated token against the spec constraints. Feed each source hue through the same shade-scale algorithm. Five hues × 11 shades = 55 primitive tokens. Here's a cross-section (shades 50, 500, 900): + +export const primitiveOverview = [ + { role: 'Primary', shade: '50', oklch: '0.97 0.068 250', hex: '#d3faff' }, + { role: 'Primary', shade: '500', oklch: '0.60 0.150 250', hex: '#2784d5' }, + { role: 'Primary', shade: '900', oklch: '0.29 0.092 250', hex: '#002c57' }, + { role: 'Neutral', shade: '50', oklch: '0.97 0.007 250', hex: '#f2f6fa' }, + { role: 'Neutral', shade: '500', oklch: '0.60 0.015 250', hex: '#7a8189' }, + { role: 'Neutral', shade: '900', oklch: '0.29 0.009 250', hex: '#282c30' }, + { role: 'Error', shade: '50', oklch: '0.97 0.068 25', hex: '#ffe4de' }, + { role: 'Error', shade: '500', oklch: '0.60 0.150 25', hex: '#ca5551' }, + { role: 'Error', shade: '900', oklch: '0.29 0.092 25', hex: '#501212' }, + { role: 'Warning', shade: '50', oklch: '0.97 0.054 70', hex: '#fff0ce' }, + { role: 'Warning', shade: '500', oklch: '0.60 0.120 70', hex: '#ad721c' }, + { role: 'Warning', shade: '900', oklch: '0.29 0.074 70', hex: '#422300' }, + { role: 'Success', shade: '50', oklch: '0.97 0.059 155', hex: '#d7ffe4' }, + { role: 'Success', shade: '500', oklch: '0.60 0.130 155', hex: '#2c965d' }, + { role: 'Success', shade: '900', oklch: '0.29 0.080 155', hex: '#003618' }, +]; + + + + +{primitiveOverview.map((s, i) => ( + + + + + + +))} + +
RoleShadeOKLCHColor
{s.role}{s.shade}oklch({s.oklch}){s.hex}
+ +Same algorithm, different inputs. The neutral scale's low chroma (~0.015 vs. 0.15) produces near-gray with a subtle blue undertone. Error, warning, and success scales are as saturated as primary — their 600 shades all pass WCAG AA on white, making them safe for text and icons in light mode. + +**Semantic mapping.** Primitives are raw values; semantic tokens assign meaning. The agent maps shades to roles for both light and dark themes: + +| Semantic Token | Light | Dark | Purpose | +|----------------|-------|------|---------| +| `--bg-page` | `neutral-50` | `neutral-900` | Page background | +| `--bg-surface` | `white` | `neutral-800` | Cards, popovers, modals | +| `--bg-muted` | `neutral-100` | `neutral-700` | Secondary surfaces, disabled | +| `--text-primary` | `neutral-900` | `neutral-50` | Body text | +| `--text-secondary` | `neutral-500` | `neutral-400` | Placeholder, muted text | +| `--border` | `neutral-200` | `neutral-700` | Borders, dividers | +| `--primary` | `primary-600` | `primary-400` | CTAs, links, active states | +| `--on-primary` | `white` | `primary-950` | Text on primary backgrounds | +| `--error` | `error-600` | `error-400` | Error text, icons | +| `--on-error` | `white` | `error-950` | Text on error background | +| `--error-subtle` | `error-50` | `error-950` | Error banner background | +| `--warning` | `warning-600` | `warning-400` | Warning text, icons | +| `--warning-subtle` | `warning-50` | `warning-950` | Warning banner background | +| `--success` | `success-600` | `success-400` | Success text, icons | +| `--success-subtle` | `success-50` | `success-950` | Success banner background | + +Note light mode uses shade 600 (not 500) for semantic text roles — 500 shades hover around 4:1 contrast on white, just below WCAG AA. Shade 600 clears the threshold consistently across all hues. Dark mode uses 400 shades for the same reason against dark backgrounds. This crossover is computed by the same contrast-checking algorithm from the shade scale, not guessed. + +**Paired foreground tokens.** Every background semantic token needs a paired foreground with guaranteed contrast. Material Design 3 calls these `on-primary`, `on-error`. shadcn/ui uses `--primary-foreground`, `--destructive-foreground`. The naming convention varies; the constraint is universal. Without explicit pairs, agents pick arbitrary text colors and contrast breaks silently. + +### Visual Validation (Human + agent-browser) + +Automated checks confirm the math is correct. They can't tell you whether the palette *feels* right. This is where you close the loop — and where the VT perceptual limitation from earlier becomes a workflow constraint. + +Have the agent render the generated tokens into a preview page, then capture it for your review: + +``` +open "http://localhost:3000/token-preview" +screenshot /tmp/palette-light.png + +# toggle theme +click @dark-mode-toggle +screenshot /tmp/palette-dark.png +``` + +You now have side-by-side screenshots of the full palette in both themes. The agent produced these artifacts, but it cannot evaluate them — it can verify "the page rendered without errors" via `snapshot -ic`, but not "the warning yellow is too muted" or "the neutral scale feels too warm." That's your judgment call. + +### The Iteration Loop + +When you spot a perceptual issue, feed it back to the agent in natural language: *"The warning yellow feels too muted at shades 400-600. Increase peak chroma from 0.12 to 0.14."* The agent regenerates the warning scale, re-validates WCAG contrast automatically, and screenshots the result. You review again. This loop continues until the palette satisfies both the math constraints (agent) and your visual standards (human). + +| Step | Human | Agent | +|------|:-----:|:-----:| +| Pick source hues and roles | ✓ | | +| Generate 55-token shade scales | | ✓ | +| Validate WCAG AA contrast pairs | | ✓ | +| Render and screenshot palette | | ✓ | +| Evaluate visual quality and harmony | ✓ | | +| Provide perceptual feedback | ✓ | | +| Regenerate from adjusted parameters | | ✓ | + +This is [Lesson 3](/methodology/lesson-3-high-level-methodology)'s "iterate back to research as needed" made concrete. The cycle isn't linear — a perceptual issue in Validate might send you back to Plan (different source hue) or even Research (what chroma range do competitors use for warnings?). The four phases are a cycle, not a waterfall. + +### Theming as Validation + +Theme switching is another validation axis. The palette must hold up under inversion. + +**Light/dark themes.** Same semantic token names, different primitive references — the Light/Dark columns in the semantic mapping table above. A theme is a complete primitive→semantic mapping. The agent generates all shade scales from the source hues, and components never change. + +**Custom themes / user-provided.** Token override layer. Feed a customer's brand color through the same derivation pipeline — the agent generates a full palette and validates it passes contrast checks (A-010). White-label skins and branded portals, once expensive custom design work, become a single prompt execution. + +**Platform adaptations.** Scale tokens per device class (mobile/tablet/desktop). Base unit changes; proportional relationships stay constant. + +## Key Takeaways + +- **The four-phase workflow applies to experience engineering** — Research grounds color decisions in competitors and standards, Plan captures human judgment (source hues), Execute generates the math (55 primitives + semantic mapping), Validate closes the perceptual loop. Same [Lesson 3](/methodology/lesson-3-high-level-methodology) cycle, different domain. + +- **Visual quality is the human's job; contrast math is the agent's** — the VT perceptual floor means agents can screenshot what they can't evaluate. You provide the feedback that drives iteration; the agent regenerates and re-validates instantly. + +- **Brand colors are design judgment; everything derivative is computable math** — shade scales, harmony colors, dark mode variants, and contrast validation are all derived by agent-written code from a handful of source hues. + +- **Three-tier architecture enables theme switching without component changes** — primitives hold raw values, semantic tokens assign meaning, component tokens scope to specific elements. A theme swap changes only the primitive→semantic mapping. + +- **OKLCH over HSL** — perceptually uniform color space produces accurate shade scales. HSL's non-uniform lightness makes "identical" lightness values appear drastically different across hues. + +- **Every semantic background needs a paired foreground token** — without explicit `on-primary`, `on-error` pairs with guaranteed contrast, agents pick arbitrary text colors and contrast breaks silently. + +- **Token assumptions table creates traceability** — the Drives column maps each design decision to the spec constraints it affects, telling you and the agent exactly what to re-verify when a token changes. + +- **Design tokens are architectural constraints** — they affect every component and must be specified upfront. Retrofitting tokens is an order of magnitude harder than building them in. [Lesson 15](/experience-engineering/lesson-15-ui-specs) builds components that consume these tokens. diff --git a/website/docs/experience-engineering/lesson-15-ui-specs.md b/website/docs/experience-engineering/lesson-15-ui-specs.md new file mode 100644 index 0000000..a1d3c92 --- /dev/null +++ b/website/docs/experience-engineering/lesson-15-ui-specs.md @@ -0,0 +1,195 @@ +--- +sidebar_position: 2 +sidebar_label: 'UI Specs' +sidebar_custom_props: + sectionNumber: 15 +title: 'UI Specs — Components, Flows, and State' +--- + +With design tokens established in [Lesson 14](/experience-engineering/lesson-14-design-tokens), this lesson defines the components, flows, and state transitions that consume them. These three sections are the minimum viable UI spec — enough to generate a first pass. The rest emerges as the code pulls depth from you. + +The running example: a **team dashboard for a SaaS billing product** — subscription plans, usage metrics, invoices, team management, and settings. Rich enough to exercise components, flows, state, responsive layouts, and view states. + +## User Story & Success Metrics + +**Why before what.** Every UI spec starts with the user story and measurable KPIs — the stopping condition for iteration. + +| Element | Example | +|---------|---------| +| Problem | "Team admins can't understand billing status at a glance" | +| Success metric | Task completion rate > 90%, time-to-insight < 5s | +| Failure metric | Rage clicks > 2%, abandonment rate > 15% | + +KPI categories: + +| Category | Examples | Measured By | +|----------|----------|-------------| +| **Efficiency** | Task completion time, clicks-to-goal | Analytics events, session recordings | +| **Effectiveness** | Success rate, error rate | Form submissions, retry counts | +| **Satisfaction** | Rage clicks, abandonment, NPS | Heatmaps, exit rate, surveys | + +Accessibility and internationalization KPIs (WCAG compliance, locale coverage, RTL correctness) live in their respective architecture sections in [Lesson 16](/experience-engineering/lesson-16-accessibility-i18n) — they're architectural constraints, not standalone metrics. + +Metrics are the completion test. When the agent's implementation meets these thresholds as measured by browser automation, iteration stops. + +## Components: Props, Responsibilities, and Rules + +Components are units of UI responsibility with explicit APIs. Props define what a component **accepts**. Rendered output defines what it **guarantees**. Accessibility attributes are **non-negotiable** — always present, never conditional. + +Throughout the Experience Engineering lessons and the [UI spec template](/prompts/specifications/experience-spec-template), you'll see IDs like `A-001` or `L-003`. These are constraint IDs — labels you put in the spec that the agent carries into code comments during implementation, making rules grep-able and verifiable across the codebase. For the code-level mechanics, see [Lesson 11](/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents). + +### Component Table + +| Component | Responsibility | Boundary | API | +|-----------|---------------|----------|-----| +| `SubscriptionCard` | Display plan name, price, status | Never fetches data directly | Props: `plan: Plan`, emits: `onUpgrade(planId)` | +| `UsageChart` | Render usage over time | No business logic | Props: `data: TimeSeriesPoint[]`, `period: DateRange` | +| `TeamMemberList` | List members with roles | No direct API calls | Props: `members: Member[]`, emits: `onRoleChange`, `onRemove` | +| `InvoiceTable` | Display invoices with sort/filter | No direct API calls | Props: `invoices: Invoice[]`, emits: `onDownload(id)` | +| `SettingsForm` | Edit user/team settings | No direct persistence | Props: `settings: Settings`, emits: `onSave(settings)` | + +All text-bearing components accept translation keys, never hardcoded strings (L-001). + +### Component Interfaces + +| Parent | Child | Interface | +|--------|-------|-----------| +| `DashboardPage` | `SubscriptionCard` | `plan: Plan` — expects: plan object has `name`, `price`, `status` | +| `SubscriptionCard` | `DashboardPage` | `onUpgrade(planId: string)` — fires only when user clicks upgrade CTA | +| Any component | Screen reader | `role`, `aria-label`, `aria-live` — non-negotiable: always present on interactive elements (A-001) | + +### Component Responsibilities + +- `SubscriptionCard` — NEVER imports from `TeamMemberList` or `UsageChart` +- `SubscriptionCard` — NEVER calls APIs directly — receives data via props +- Shared kernel: `src/components/primitives/` — buttons, inputs, typography (design token consumers) + +Responsibility leaks are the first thing to check when an agent's implementation diverges from spec. If the agent wired `SubscriptionCard` directly to an API, the component responsibility split is wrong — fix the spec or make it more explicit. + +### Component Libraries as Agent Context + +When building on an established component library (shadcn/ui, Radix, Ant Design, Material UI), the library already defines primitive behavior — accessible button, modal, tooltip, form controls. The spec only needs to define **domain-specific** behavior on top. + +Example: `SubscriptionCard` uses `` from shadcn/ui. The agent already knows Card's API from library documentation. Your spec defines the domain props (`plan: Plan`, `onUpgrade`) and responsibility rules, not how a card renders a shadow. + +Include component library references in your project's CLAUDE.md / AGENTS.md ([Lesson 6](/practical-techniques/lesson-6-project-onboarding)): import conventions, preferred primitives, and any overrides. This dramatically reduces spec surface and improves agent output because the agent operates within a constrained, well-documented design system. + +## Flows: Interaction Traces + +Flows trace a user's path through the interface step by step — what happens on success, what happens on failure, and what the user sees at each transition. Each flow step maps to a **component state transition** and an **API call**. + +### Flow: Upgrade Subscription + +``` +1. User clicks "Upgrade" on SubscriptionCard + → Show plan comparison modal + → Focus moves to modal heading (A-006) + → On cancel: close modal, return focus to trigger button (A-007), no state change + +2. User selects new plan, clicks "Confirm" + → Show loading state (skeleton in confirmation area) + → Call billing API: POST /subscriptions/upgrade + → On success: show success toast (announced via aria-live="polite", A-008), + update SubscriptionCard, close modal, return focus to SubscriptionCard + → On API error (4xx): show inline error in modal, keep modal open + → On network error: show retry banner, keep modal open + +3. Dashboard refreshes subscription data + → On success: metrics update in real-time + → On stale data: show "Refreshing..." indicator +``` + +### Flow: Team Member Management + +``` +1. Admin clicks "Add Member" in TeamMemberList + → Show invite form (email, role selector) + → Focus moves to email input (A-006) + → On cancel: close form, return focus to "Add Member" button (A-007) + +2. Admin enters email, selects role, clicks "Send Invite" + → Validate email format (client-side) + → On invalid: show inline validation error, keep form open, + announce error via aria-live="assertive" (A-009) + → Call team API: POST /team/invites + → On success: show success toast, add pending member to list + → On 409 (already invited): show inline error "Already invited" + → On 403 (insufficient permissions): show error, + should not reach here (CTA hidden for non-admins) +``` + +All user-visible strings in flows use translation keys. Pluralization follows ICU MessageFormat (L-003). + +### Behavioral Scenarios + +Each flow step above is a behavioral scenario waiting to happen. The [UI spec template](/prompts/specifications/experience-spec-template) includes a Given/When/Then table for converting flow steps into concrete, automatable test cases. At minimum, cover five edge categories: boundary values (min/max inputs), null/empty states (no data loaded), error propagation (API failures), concurrency (rapid clicks, simultaneous updates), and temporal edge cases (slow network, stale data). + +Agents handle the happy path correctly and miss the error and edge branches. Annotating each flow step with all three outcomes (success, expected error, unexpected error) prevents the agent from generating optimistic-only implementations. + +## The UI Stack: Five View States[^1] + +Every data-bound view has exactly five states. Omitting any is a bug: + +| State | What User Sees | Screen Reader Announcement | Constraint | +|-------|---------------|---------------------------|------------| +| **Ideal** | Data rendered correctly | Content available via landmarks and labels | Must match design spec | +| **Loading** | Skeleton/spinner | Container has `aria-busy="true"` (A-002) | Must appear within 100ms of trigger; skeleton preferred for layout stability | +| **Error** | Error message + recovery action | Error announced via `aria-live="assertive"` (A-003) | Must have actionable CTA (retry, contact support); never show raw error | +| **Empty** | Helpful empty state | Guidance text perceivable, not just visual (A-004) | Must guide user to action ("No invoices yet. Create your first invoice.") | +| **Partial** | Some data loaded, some failed | Failed section announced, rest navigable (A-005) | Must not block entire view for one failed section | + +### UI Stack Constraints + +| ID | Rule | Verified By | +|----|------|-------------| +| V-001 | NEVER show a blank screen — always show one of the five states | Browser automation: disconnect network mid-load, verify error state renders | +| V-002 | NEVER show raw error messages to users | Browser automation: trigger 500 from mock, verify user-friendly message | +| V-003 | ALWAYS show loading state within 100ms of async action | Browser automation: throttle network, measure time-to-skeleton | + +Agents consistently miss the **Partial** and **Empty** states. If the spec doesn't enumerate all five explicitly with examples, the agent will implement only Ideal and Loading — then the first empty dataset or partial API failure renders a blank screen. + +**Choosing a state model.** Pick one model per view entity, not per app. Three options: **Declarative** — you define the desired state, a reconciler (React, for example) figures out how to get there. Best for data display (tables, charts, read-only views). **State machine** — you enumerate every legal state and transition. Best for multi-step flows (forms, wizards, modals with distinct phases). **Event-driven** — you react to incoming events as they arrive. Best for real-time data (WebSocket feeds, collaborative editing, live notifications). For the system-level perspective on these models (event sourcing, CQRS, entity lifecycles), see [Lesson 13: State Models](/practical-techniques/lesson-13-systems-thinking-specs#state-models). + +## Layouts and Responsiveness + +Layouts define **how components are arranged** — the spatial structure of the page. + +### Layout Table + +| Layout | Structure | Breakpoints | Components | +|--------|-----------|-------------|------------| +| `DashboardGrid` | 12-col grid, sidebar + main | Desktop: sidebar+main, Tablet: stacked, Mobile: single column | `Sidebar`, `MetricsRow`, `ContentArea` | +| `SettingsLayout` | 2-col: nav + panel | Desktop: side-by-side, Mobile: nav→panel drill-down | `SettingsNav`, `SettingsPanel` | + +RTL layouts mirror the entire grid — sidebar switches sides, reading order reverses. Use logical properties (`inline-start`/`block-start`) for all spatial tokens (L-002). See [Lesson 16: Accessibility & Internationalization](/experience-engineering/lesson-16-accessibility-i18n#internationalization-architecture) for the full RTL model. + +### Responsiveness as Constraint + +| Breakpoint | Viewport | Layout Adaptation | Token Scale | +|------------|----------|-------------------|-------------| +| Mobile | < 768px | Single column, bottom nav | spacing.base = 8px | +| Tablet | 768–1024px | Collapsed sidebar, 2-col content | spacing.base = 8px | +| Desktop | > 1024px | Full sidebar, 3-col dashboard | spacing.base = 8px | +| Large | > 1440px | Max-width container, increased spacing | spacing.base = 10px | + +**Accessibility constraints:** Touch targets ≥ 44px on mobile (WCAG 2.5.5, A-012). Tab order must follow visual reading order per breakpoint (A-013). Zoom to 200% must not cause horizontal scroll (A-014). Animation duration scales down on `prefers-reduced-motion` preference. + +Agents implement desktop-first by default. If your layout spec requires mobile-first breakpoints, state this explicitly: "implement mobile layout first, then add tablet and desktop as progressive enhancements." Without this constraint, the agent generates desktop CSS and retrofits mobile as an afterthought — producing brittle media queries. + +The [UI spec template](/prompts/specifications/experience-spec-template) includes a **Performance Budget** section with Core Web Vitals targets (FCP, LCP, CLS, INP) and bundle size limits. Include these when performance is a constraint — particularly on mobile where bundle size and INP thresholds are tighter. + +## Key Takeaways + +- **UI specs define component APIs, not screens** — components, flows, and state transitions are the specification units, not mockups or wireframes. + +- **Start with three sections** — Components, Flows, and State are the minimum viable spec. Everything else is pulled by the code. + +- **Every view has five states** — ideal, loading, error, empty, partial (the UI Stack[^1]). Agents consistently miss empty and partial — enumerate all five explicitly, or the first empty dataset renders a blank screen. + +- **Component libraries are agent context** — reference library primitives by name in the spec; define only domain-specific behavior on top. Include library docs and import conventions in CLAUDE.md. + +- **Agents build desktop-first, happy-path-only by default** — UI specs exist to constrain these failure modes through explicit breakpoint tables, error state enumeration, and non-negotiable accessibility rules. + +--- + +[^1]: Hurff, Scott (2015) — ["Why Your User Interface Is Awkward: You're Ignoring the UI Stack"](https://www.scotthurff.com/posts/why-your-user-interface-is-awkward-youre-ignoring-the-ui-stack) — Introduced the five UI states (Ideal, Empty, Error, Partial, Loading) as a framework for designing complete interfaces. diff --git a/website/docs/experience-engineering/lesson-16-accessibility-i18n.md b/website/docs/experience-engineering/lesson-16-accessibility-i18n.md new file mode 100644 index 0000000..b0b0c3b --- /dev/null +++ b/website/docs/experience-engineering/lesson-16-accessibility-i18n.md @@ -0,0 +1,63 @@ +--- +sidebar_position: 3 +sidebar_label: 'Accessibility & i18n' +sidebar_custom_props: + sectionNumber: 16 +title: 'Accessibility & Internationalization' +--- + +Accessibility and internationalization are architectural constraints on the components from [Lesson 15](/experience-engineering/lesson-15-ui-specs) — structural decisions that affect every component, flow, and state transition. Both are specified up front, not bolted on after implementation, and both are verifiable through `snapshot -ic` — the same tool that validates agent output. + +## Accessibility Architecture + +Accessibility is architecture, not a checklist. Landmark structure, keyboard model, focus management, and live region strategy are structural decisions that affect every component, flow, and state transition. They are specified up front, not bolted on after implementation. + +Five architectural decisions for every UI spec: + +| Decision | Question | Example | +|----------|----------|---------| +| Semantic HTML | Can a native element do this? Use it before reaching for ARIA — incorrect ARIA creates *more* errors than no ARIA | `` over `
`, `
-This template provides a solid baseline for any planning task. In [Lesson 4: Prompting 101](./lesson-4-prompting-101.md), you'll learn the prompt engineering principles behind this structure so you can construct your own custom planning prompts rather than relying on templates. +This template provides a solid baseline for any planning task. In [Lesson 4: Prompting 101](./lesson-4-prompting-101.mdx), you'll learn the prompt engineering principles behind this structure so you can construct your own custom planning prompts rather than relying on templates. ::: -## Phase 3: Execute (Two Execution Modes) +## Phase 3: Execute (Two Execution Modes) {#phase-3-execute-two-execution-modes} @@ -205,13 +186,15 @@ This template provides a solid baseline for any planning task. In [Lesson 4: Pro + + With your plan complete, you execute—but how you interact with the agent during execution fundamentally changes your productivity. There are two modes: supervised (actively watching and steering) and autonomous (fire-and-forget). Most engineers start with supervised mode to build trust, then gradually shift to autonomous mode as they develop stronger grounding and planning skills. Here's the counterintuitive truth: the real productivity gain isn't about finishing individual tasks faster. It's about working on multiple projects simultaneously and maintaining extremely long work stretches. That's where 10x productivity actually hides. -### Supervised Mode ("Babysitting") +### Supervised Mode ("Babysitting") In supervised mode, you actively monitor the agent as it works. You watch each action, review intermediate outputs, steer when it drifts, and intervene when it makes mistakes. This gives you maximum control and precision—you catch issues immediately and guide the agent toward the right solution in real time. The cost is massive: your throughput tanks because you're blocked while the agent works. You can't context-switch to another task, you can't step away, and you're burning your most valuable resource (attention) on implementation details. Use this mode when you're learning how agents behave, when working on critical security-sensitive code, or when tackling complex problems where you need to build your mental model as the agent explores. This is your training ground for developing the trust and intuition that eventually allows you to let go. -### Autonomous Mode ("Autopilot" / "YOLO") +### Autonomous Mode ("Autopilot" / "YOLO") In autonomous mode, you give the agent a well-defined task from your plan, let it run, and check the results when it's done. You're not watching it work. You're doing other things—working on a different project, attending a meeting, cooking dinner, running errands. You might check your phone occasionally to see if it's blocked or needs clarification, but mostly you're away. This is where the real productivity transformation happens, and it's not what most people think. Yes, sometimes the agent finishes a task faster than you would manually. But that's not the point. The point is **parallel work** and **continuous output**. You can have three agents running simultaneously on different projects. You can maintain 8-hour stretches of productive output while only spending 2 hours at your keyboard. You can genuinely multitask in software development for the first time in history. Even if you could hand-code something in 20 minutes and the agent takes 30, autonomous mode wins if it means you're cooking dinner instead of being blocked. This mode depends entirely on excellent grounding (Phase 1) and planning (Phase 2). If you skip those phases, the agent will drift, hallucinate, and produce garbage. If you do them well, you can trust the agent to execute correctly without supervision. Your goal is to maximize time in autonomous mode—that's where you become genuinely more productive, not just slightly faster. @@ -219,7 +202,7 @@ In autonomous mode, you give the agent a well-defined task from your plan, let i Autonomous mode isn't about speed per task. It's about working on multiple tasks simultaneously while living your life. A senior engineer running three autonomous agents in parallel while attending meetings and cooking dinner ships more code than the same engineer babysitting one agent through a single task. That's the actual game changer. ::: -## Phase 4: Validate (The Iteration Decision) +## Phase 4: Validate (The Iteration Decision) {#phase-4-validate-the-iteration-decision} The agent completed. Here's the reality: **LLMs are probabilistic machines that almost never produce 100% perfect output on first pass.** This isn't failure—it's expected behavior. @@ -253,4 +236,4 @@ This workflow is the strategic framework. But strategy means nothing without exe --- -**Next:** [Lesson 4: Prompting 101](./lesson-4-prompting-101.md) - Learn the specific techniques for crafting effective prompts that get reliable results. +**Next:** [Lesson 4: Prompting 101](./lesson-4-prompting-101.mdx) - Learn the specific techniques for crafting effective prompts that get reliable results. diff --git a/website/docs/methodology/lesson-4-prompting-101.md b/website/docs/methodology/lesson-4-prompting-101.mdx similarity index 64% rename from website/docs/methodology/lesson-4-prompting-101.md rename to website/docs/methodology/lesson-4-prompting-101.mdx index c13150d..8f718cd 100644 --- a/website/docs/methodology/lesson-4-prompting-101.md +++ b/website/docs/methodology/lesson-4-prompting-101.mdx @@ -1,13 +1,25 @@ --- sidebar_position: 2 -sidebar_label: 'Lesson 4: Prompting 101' +sidebar_label: 'Prompting 101' +sidebar_custom_props: + sectionNumber: 4 title: 'Prompting 101' --- +import PromptExample from '@site/src/components/PromptExample'; +import AutocompleteDiagram from '@site/src/components/VisualElements/AutocompleteDiagram'; +import CrossEmoji from '@site/src/components/VisualElements/CrossEmoji'; +import CheckEmoji from '@site/src/components/VisualElements/CheckEmoji'; + AI coding assistants aren't conversational partners—they're sophisticated pattern completion engines. Understanding this fundamental distinction changes how you prompt. Think of prompting as drawing the beginning of a pattern. The model predicts and completes what comes next based on statistical patterns from its training data. Your prompt isn't a request; it's the start of a sequence the model will finish. +
+ +
The model scores every possible next token against the full context and selects the highest-probability candidate — one token at a time.
+
+ ## Clear Instruction-Based Prompting Skip pleasantries. AI models don't need "please" or "thank you"—these tokens dilute your signal without adding clarity. @@ -16,24 +28,24 @@ Skip pleasantries. AI models don't need "please" or "thank you"—these tokens d Start the pattern you want completed. Use direct, action-oriented language. -**Ineffective:** - -``` -Could you help me write a function to validate email addresses? -Thanks in advance! -``` + **Ineffective:** -**Effective:** + +Could you help me write a function to validate email addresses?
+Thanks in advance!
+
-``` -Write a TypeScript function that validates email addresses per RFC 5322. -Handle edge cases: -- Multiple @ symbols (invalid) -- Missing domain (invalid) -- Plus addressing (valid) + **Effective:** -Return { valid: boolean, reason?: string } -``` + +Write a TypeScript function that validates email addresses per RFC 5322.
+Handle edge cases:
+- Multiple @ symbols (invalid)
+- Missing domain (invalid)
+- Plus addressing (valid)
+
+Return {"{ valid: boolean, reason?: string }"} +
The effective prompt draws the beginning of a precise pattern: TypeScript function signature, validation rules, return type. The model completes this pattern with matching code. @@ -74,13 +86,13 @@ Strong verbs establish clear patterns: **Specificity compounds effectiveness:** -``` -Refactor UserRepository to use dependency injection: -1. Extract database connection to IDatabaseAdapter interface -2. Inject adapter via constructor -3. Update all 7 query methods to use adapter.execute() + +Refactor UserRepository to use dependency injection:
+1. Extract database connection to IDatabaseAdapter interface
+2. Inject adapter via constructor
+3. Update all 7 query methods to use adapter.execute()
4. Add unit tests mocking the adapter -``` +
This defines the refactoring pattern completely. No guessing required. @@ -88,25 +100,25 @@ This defines the refactoring pattern completely. No guessing required. Without constraints, the model fills gaps with assumptions. Define boundaries explicitly. -**Unconstrained:** + **Unconstrained:** -``` + Add authentication to the API -``` + What authentication? JWT? OAuth? Session tokens? Which endpoints? -**Constrained:** + **Constrained:** -``` -Add JWT authentication to the API: -- Do NOT modify existing session middleware -- Use jsonwebtoken library -- Protect all /api/v1/* endpoints except /api/v1/auth/login -- Token expiry: 24 hours -- Store user ID and role in payload -- Return 401 for missing/invalid tokens -``` + +Add JWT authentication to the API:
+- Do NOT modify existing session middleware
+- Use jsonwebtoken library
+- Protect all /api/v1/* endpoints except /api/v1/auth/login
+- Token expiry: 24 hours
+- Store user ID and role in payload
+- Return 401 for missing/invalid tokens +
Now the completion space is well-defined. @@ -118,20 +130,20 @@ Personas work by biasing vocabulary distribution. Writing "You are a security en **Example: Generic prompt** -``` + Review this authentication code for issues. -``` + Result: Generic advice like "Check for proper validation and error handling" **Example: Security-focused prompt** -``` -You are a security engineer conducting a code review. -Review this authentication code. Flag vulnerabilities: -SQL injection, XSS, auth bypasses, secrets in code. + +You are a security engineer conducting a code review.
+Review this authentication code. Flag vulnerabilities:
+SQL injection, XSS, auth bypasses, secrets in code.
Assume adversarial input and untrusted networks. -``` +
Result: Targeted security analysis identifying specific vulnerabilities with mitigation strategies @@ -145,25 +157,25 @@ When tasks require multiple steps, you often need control over the execution pat CoT defines each step the model must execute in sequence. You're not asking for reasoning—you're dictating the path. -**Without CoT:** + **Without CoT:** -``` -Debug the failing test in UserService.test.ts -``` - -**With CoT:** + +Debug the failing test in UserService.test.ts + -``` -Debug the failing test in UserService.test.ts: - -1. Read the test file, identify which test is failing -2. Analyze test assertion: expected vs actual values -3. Trace code path through UserService to find the bug -4. Explain root cause -5. Propose fix + **With CoT:** + +Debug the failing test in UserService.test.ts:
+
+1. Read the test file, identify which test is failing
+2. Analyze test assertion: expected vs actual values
+3. Trace code path through UserService to find the bug
+4. Explain root cause
+5. Propose fix
+
Provide your conclusions with evidence. -``` +
**Why CoT gives you control:** @@ -186,35 +198,35 @@ This matters for token efficiency and grounding. Well-structured prompts help th ### Markdown for Hierarchical Organization -```markdown -# Task: Implement OAuth 2.0 Client Credentials Flow - -## Requirements - -- Support multiple authorization servers (configurable) -- Cache tokens until expiry (Redis) -- Auto-retry on 401 with token refresh -- Expose as Express middleware - -## Implementation Steps - -1. Create OAuthClient class with getToken() method -2. Implement token caching with TTL -3. Add retry logic with exponential backoff -4. Write middleware injecting token into req.context - -## Testing - -- Unit tests for OAuthClient -- Integration tests against mock OAuth server -- Error cases: network failure, invalid credentials, expired tokens - -## Constraints - -- Use axios for HTTP requests -- Use ioredis for caching + +# Task: Implement OAuth 2.0 Client Credentials Flow
+
+## Requirements
+
+- Support multiple authorization servers (configurable)
+- Cache tokens until expiry (Redis)
+- Auto-retry on 401 with token refresh
+- Expose as Express middleware
+
+## Implementation Steps
+
+1. Create OAuthClient class with getToken() method
+2. Implement token caching with TTL
+3. Add retry logic with exponential backoff
+4. Write middleware injecting token into req.context
+
+## Testing
+
+- Unit tests for OAuthClient
+- Integration tests against mock OAuth server
+- Error cases: network failure, invalid credentials, expired tokens
+
+## Constraints
+
+- Use axios for HTTP requests
+- Use ioredis for caching
- No global state—client must be instantiated -``` +
The structure makes requirements scannable and draws attention to distinct sections: what to build, how to build it, how to test it, what to avoid. @@ -226,25 +238,25 @@ AI models have predictable failure modes. Understanding these helps you prompt d LLMs struggle with negation because attention mechanisms treat "NOT" as just another token competing for weight. When "NOT" receives low attention during processing, the model focuses on the concepts mentioned ("passwords", "plain text") rather than their negation—a phenomenon called "affirmation bias." The model's token generation fundamentally leans toward positive selection (what to include) rather than negative exclusion (what to avoid). -**Risky:** + **Risky:** -``` -Write a user registration endpoint. -Do NOT store passwords in plain text. -``` + +Write a user registration endpoint.
+Do NOT store passwords in plain text. +
The model might miss "NOT" and generate plain text password storage because attention focuses on "passwords" + "plain text" while ignoring the negation. -**Better—Negation then positive opposite:** + **Better—Negation then positive opposite:** -``` -Write a user registration endpoint. - -Password handling: -Do NOT store passwords in plain text. -Instead, always store passwords as hashed values. -Use bcrypt with 10 salt rounds before storing. -``` + +Write a user registration endpoint.
+
+Password handling:
+Do NOT store passwords in plain text.
+Instead, always store passwords as hashed values.
+Use bcrypt with 10 salt rounds before storing. +
This pattern works by: @@ -256,30 +268,30 @@ This pattern works by: LLMs are probabilistic text predictors, not calculators. They're terrible at arithmetic. -**Don't rely on LLMs for math:** + **Don't rely on LLMs for math:** -``` -Calculate the optimal cache size for 1M users with 2KB average data per user, + +Calculate the optimal cache size for 1M users with 2KB average data per user,
assuming 80% hit rate and 4GB available memory. -``` +
The model will generate plausible-sounding numbers that may be completely wrong. -**Instead, have the model write code:** - -``` -Write a Python function that calculates optimal cache size. - -Inputs: -- user_count: number of users -- avg_data_per_user_kb: average data size in KB -- hit_rate: cache hit rate (0.0 to 1.0) -- available_memory_gb: available memory in GB - -Return optimal cache size in MB with reasoning. - + **Instead, have the model write code:** + + +Write a Python function that calculates optimal cache size.
+
+Inputs:
+- user_count: number of users
+- avg_data_per_user_kb: average data size in KB
+- hit_rate: cache hit rate (0.0 to 1.0)
+- available_memory_gb: available memory in GB
+
+Return optimal cache size in MB with reasoning.
+
Include unit tests validating the calculation. -``` +
## Key Takeaways diff --git a/website/docs/methodology/lesson-5-grounding.md b/website/docs/methodology/lesson-5-grounding.md index 7999f24..34414ee 100644 --- a/website/docs/methodology/lesson-5-grounding.md +++ b/website/docs/methodology/lesson-5-grounding.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 -sidebar_label: 'Lesson 5: Grounding' +sidebar_label: 'Grounding' +sidebar_custom_props: + sectionNumber: 5 title: 'Grounding: Anchoring Agents in Reality' --- @@ -9,7 +11,7 @@ import GroundingComparison from '@site/src/components/VisualElements/GroundingCo You ask your agent to fix the authentication bug. It confidently generates a solution using JWT verification patterns... that don't exist in your codebase. You use sessions, not JWTs. The agent just hallucinated a plausible implementation based on common patterns from its training data. -Here's the fundamental issue: **The agent doesn't know your codebase exists.** It doesn't know your architecture, your patterns, or your constraints. As covered in [Lesson 2: Agents Demystified](/docs/fundamentals/lesson-2-how-agents-work), the context window is the agent's entire world—everything else doesn't exist. Without explicit grounding in your actual code and current documentation, agents generate statistically plausible solutions that may be completely wrong for your system. +Here's the fundamental issue: **The agent doesn't know your codebase exists.** It doesn't know your architecture, your patterns, or your constraints. As covered in [Lesson 2: Agents Demystified](/fundamentals/lesson-2-how-agents-work), the context window is the agent's entire world—everything else doesn't exist. Without explicit grounding in your actual code and current documentation, agents generate statistically plausible solutions that may be completely wrong for your system. **Grounding is how you inject reality into the context window.** You retrieve relevant external information—your codebase patterns, current docs, best practices—and feed it to the agent before generation. This lesson covers the engineering techniques that anchor agents in your actual system instead of hypothetical ones. diff --git a/website/docs/practical-techniques/lesson-10-debugging.md b/website/docs/practical-techniques/lesson-10-debugging.md index c822a9a..3d97d80 100644 --- a/website/docs/practical-techniques/lesson-10-debugging.md +++ b/website/docs/practical-techniques/lesson-10-debugging.md @@ -1,7 +1,9 @@ --- sidebar_position: 5 -sidebar_label: 'Lesson 10: Debugging' -title: 'Lesson 10: Debugging with AI Agents' +sidebar_label: 'Debugging' +sidebar_custom_props: + sectionNumber: 10 +title: 'Debugging with AI Agents' --- import EvidenceBasedDebug from '@site/shared-prompts/\_evidence-based-debug.mdx'; @@ -46,7 +48,7 @@ For complex systems, use Docker to create isolated reproduction environments. Sn ## Closing the Loop: Place Agents Inside Failing Environments -With good grounding, agents can always explore your codebase and research online issues—that's what [Lesson 5](/docs/methodology/lesson-5-grounding) teaches. **But closing the loop means the agent can test its fixes and verify its reasoning actually works.** Without environment access, the agent proposes solutions it can't validate. With closed-loop access, it applies fixes, re-runs reproduction, and proves they work—or iterates on new hypotheses when they don't. +With good grounding, agents can always explore your codebase and research online issues—that's what [Lesson 5](/methodology/lesson-5-grounding) teaches. **But closing the loop means the agent can test its fixes and verify its reasoning actually works.** Without environment access, the agent proposes solutions it can't validate. With closed-loop access, it applies fixes, re-runs reproduction, and proves they work—or iterates on new hypotheses when they don't. The difference: An open-loop agent researches your code and online issues, then reports: "The bug is likely missing RS256 signature verification at jwt.ts:67—try adding algorithm validation." A closed-loop agent does the same research, then **applies that fix, re-runs the failing request, observes it now returns 401 correctly, and reports: "Fixed and verified—RS256 validation added at jwt.ts:67, reproduction now passes."** @@ -65,7 +67,7 @@ This is where CLI agents (Claude Code, Codex, Copilot CLI) shine over IDE assist **4. INVESTIGATE** - Agent leverages grounding techniques to form hypotheses by correlating: - **Runtime behavior**: Execute diagnostic commands, inspect responses, analyze logs -- **Codebase**: Use ChunkHound's code research for comprehensive investigation with architectural context and cross-module relationships. For smaller codebases, [agentic search](/docs/methodology/lesson-5-grounding#the-discovery-problem-agentic-search) (Grep, Read) works well. See [Lesson 5](/docs/methodology/lesson-5-grounding#code-grounding-choosing-tools-by-scale) for scale guidance. +- **Codebase**: Use ChunkHound's code research for comprehensive investigation with architectural context and cross-module relationships. For smaller codebases, [agentic search](/methodology/lesson-5-grounding#the-discovery-problem-agentic-search) (Grep, Read) works well. See [Lesson 5](/methodology/lesson-5-grounding#code-grounding-choosing-tools-by-scale) for scale guidance. - **Known issues**: Research error patterns, CVEs, and similar bugs using tools like ArguSeek **5. VERIFY** - Agent applies the fix, re-runs reproduction, and confirms the bug is resolved—or forms a new hypothesis and iterates @@ -76,7 +78,7 @@ This workflow transforms debugging from "research and guess" to "research, fix, When you can't reproduce bugs locally or access the failing environment—customer deployments, edge infrastructure, locked-down production—you face limited information and no iteration cycle. This is where AI agents' **probabilistic reasoning** becomes a feature, not a limitation. Combined with their code generation capabilities, agents turn remote diagnosis from "send me logs and wait" into an active investigation workflow. -The workflow follows the research-first pattern from [Lesson 5](/docs/methodology/lesson-5-grounding): ground yourself in the codebase (understand the architecture around the failing component using code research) and in known issues (search for similar problems in the ecosystem). With this context, the agent generates ranked hypotheses based on evidence—not generic patterns. Then it produces targeted diagnostic scripts that collect evidence for each hypothesis: configuration states, version mismatches, timing data, whatever's needed to validate or refute each theory. +The workflow follows the research-first pattern from [Lesson 5](/methodology/lesson-5-grounding): ground yourself in the codebase (understand the architecture around the failing component using code research) and in known issues (search for similar problems in the ecosystem). With this context, the agent generates ranked hypotheses based on evidence—not generic patterns. Then it produces targeted diagnostic scripts that collect evidence for each hypothesis: configuration states, version mismatches, timing data, whatever's needed to validate or refute each theory. The key is trading developer time for compute time. Writing a comprehensive diagnostic script takes humans days but takes agents 30 minutes. More importantly, agents generate thorough diagnostics trivially—scripts that check dozens of potential issues, cross-reference configuration, and output structured data. Send the script to the customer, load the output into the agent's context, and it correlates evidence with hypotheses to identify the root cause. What would be tedious manual work becomes a simple prompt. diff --git a/website/docs/practical-techniques/lesson-11-agent-friendly-code.md b/website/docs/practical-techniques/lesson-11-agent-friendly-code.md index 4228390..14433e9 100644 --- a/website/docs/practical-techniques/lesson-11-agent-friendly-code.md +++ b/website/docs/practical-techniques/lesson-11-agent-friendly-code.md @@ -1,7 +1,9 @@ --- sidebar_position: 6 -sidebar_label: 'Lesson 11: Agent-Friendly Code' -title: 'Lesson 11: Writing Agent-Friendly Code' +sidebar_label: 'Agent-Friendly Code' +sidebar_custom_props: + sectionNumber: 11 +title: 'Writing Agent-Friendly Code' --- import CompoundQualityVisualization from '@site/src/components/VisualElements/CompoundQualityVisualization'; @@ -14,7 +16,7 @@ Every piece of code you accept today becomes pattern context for tomorrow's agen ## The Compounding Mechanism -During code research ([Lesson 5](/docs/methodology/lesson-5-grounding)), agents grep for patterns, read implementations, and load examples into context. The code they find becomes the pattern context for generation. +During code research ([Lesson 5](/methodology/lesson-5-grounding)), agents grep for patterns, read implementations, and load examples into context. The code they find becomes the pattern context for generation. @@ -42,11 +44,11 @@ You can't eliminate these random errors with better prompts or cleaner patterns Both problems feed the same exponential curve. When you accept a random AI error during code review, it becomes a pattern that gets copied. One hallucinated API call in iteration 1 becomes the template for 5 similar hallucinations by iteration 3. -**Your Critical Role:** Code review ([Lesson 9](/docs/practical-techniques/lesson-9-reviewing-code)) is where you break the cycle. Reject bad patterns before they multiply. Reject random errors before they become patterns. Every acceptance decision affects every future generation. +**Your Critical Role:** Code review ([Lesson 9](/practical-techniques/lesson-9-reviewing-code)) is where you break the cycle. Reject bad patterns before they multiply. Reject random errors before they become patterns. Every acceptance decision affects every future generation. ## Co-locate Related Constraints -From [Lesson 5](/docs/methodology/lesson-5-grounding), agents discover your codebase through **agentic search**—Grep, Read, Glob. **Agents only see code they explicitly find.** When constraints scatter across files, search determines what the agent sees and what it misses. +From [Lesson 5](/methodology/lesson-5-grounding), agents discover your codebase through **agentic search**—Grep, Read, Glob. **Agents only see code they explicitly find.** When constraints scatter across files, search determines what the agent sees and what it misses. **Anti-pattern (scattered constraints):** @@ -80,7 +82,7 @@ function createUser(email: string, password: string) { ### Semantic Bridges When DRY Requires Separation -When constraints must be shared across modules, create **semantic bridges**—comments with related semantic keywords enabling semantic search and code research tools ([Lesson 5](/docs/methodology/lesson-5-grounding#solution-1-semantic-search)) to discover relationships: +When constraints must be shared across modules, create **semantic bridges**—comments with related semantic keywords enabling semantic search and code research tools ([Lesson 5](/methodology/lesson-5-grounding#solution-1-semantic-search)) to discover relationships: ```typescript // File: shared/constants.ts @@ -105,15 +107,15 @@ The comments use different words with overlapping meaning—semantic breadcrumbs ### Automate Through Prompting -Rather than manually managing discoverability strategies, configure your agent to handle this automatically. Add instructions like `"Document inline when necessary"` and `"Match surrounding patterns and style"` to your `CLAUDE.md` or `AGENTS.md` ([Lesson 6](/docs/practical-techniques/lesson-6-project-onboarding)). These phrases make agents automatically add semantic bridge comments during generation, follow existing code conventions, and maintain consistency without explicit oversight. The agent reads your co-located constraints and semantic bridges during code research, then generates new code that follows the same patterns—turning discoverability into a self-reinforcing system rather than manual organizational work. +Rather than manually managing discoverability strategies, configure your agent to handle this automatically. Add instructions like `"Document inline when necessary"` and `"Match surrounding patterns and style"` to your `CLAUDE.md` or `AGENTS.md` ([Lesson 6](/practical-techniques/lesson-6-project-onboarding)). These phrases make agents automatically add semantic bridge comments during generation, follow existing code conventions, and maintain consistency without explicit oversight. The agent reads your co-located constraints and semantic bridges during code research, then generates new code that follows the same patterns—turning discoverability into a self-reinforcing system rather than manual organizational work. -**Caveat:** You'll need to occasionally remind the agent about these instructions in your task-specific prompts. Due to the [U-shaped attention curve](/docs/methodology/lesson-5-grounding#the-scale-problem-context-window-limits), instructions buried in configuration files can fall into the ignored middle of the context window during long interactions. A quick reminder like "document inline where necessary and match surrounding style" at the end of your prompt ensures these constraints stay in the high-attention zone. +**Caveat:** You'll need to occasionally remind the agent about these instructions in your task-specific prompts. Due to the [U-shaped attention curve](/methodology/lesson-5-grounding#the-scale-problem-context-window-limits), instructions buried in configuration files can fall into the ignored middle of the context window during long interactions. A quick reminder like "document inline where necessary and match surrounding style" at the end of your prompt ensures these constraints stay in the high-attention zone. ## Comments as Context Engineering: Critical Sections for Agents **Advanced technique—use sparingly.** In concurrent programming, critical sections protect shared resources through mutual exclusion. Comments can serve a similar role for AI agents, creating "agent-critical sections" that guard sensitive code from accidental modification. Apply this **only** to genuinely high-risk code: authentication/authorization, cryptographic operations, payment processing, database migrations, audit logging, PII handling. Do NOT use for general business logic, CRUD operations, or frequently-changing code. The trade-off: protection creates friction. If every function has "CRITICAL" warnings, the signal becomes noise and legitimate agent work slows down. -When agents research your codebase ([Lesson 5](/docs/methodology/lesson-5-grounding)), they read files and load every comment into their context window. This means comments become prompts. Write them like prompts using techniques from [Lesson 4](/docs/methodology/lesson-4-prompting-101): imperative directives (NEVER, MUST, ALWAYS), explicit negation patterns ("Do NOT X. Instead, always Y"), numbered steps for complex operations (Step 1, Step 2), and concrete consequences. When the agent generates password handling code and reads "NEVER store passwords in plain text" with implementation alternatives, that violation becomes far less likely. You're exploiting prompt injection—the good kind. +When agents research your codebase ([Lesson 5](/methodology/lesson-5-grounding)), they read files and load every comment into their context window. This means comments become prompts. Write them like prompts using techniques from [Lesson 4](/methodology/lesson-4-prompting-101): imperative directives (NEVER, MUST, ALWAYS), explicit negation patterns ("Do NOT X. Instead, always Y"), numbered steps for complex operations (Step 1, Step 2), and concrete consequences. When the agent generates password handling code and reads "NEVER store passwords in plain text" with implementation alternatives, that violation becomes far less likely. You're exploiting prompt injection—the good kind. ```typescript // Standard comment @@ -139,18 +141,18 @@ function createUser(password: string) { This creates deliberate friction. An agent tasked with "add OAuth login" will work slower around password hashing code with heavy constraints—it must navigate all those NEVER/MUST directives carefully. That's the protection mechanism: forced caution for critical paths. But overuse is counterproductive. Mark too many functions as CRITICAL and agents struggle with routine work, slowing down legitimate changes as much as dangerous ones. Reserve this technique for code where accidental modification genuinely costs more than the development slowdown. -These constraint IDs (C-001, I-001) originate in [spec tables](/docs/practical-techniques/lesson-13-systems-thinking-specs#constraints-and-invariants-defining-correctness) and migrate into code during implementation. Once inlined, the code carries the constraint—not just the implementation, but the *rule* it enforces. This is what makes it safe to [delete the spec](/docs/practical-techniques/lesson-12-spec-driven-development) after implementation: the WHY has migrated into the codebase. +These constraint IDs (C-001, I-001) originate in [spec tables](/practical-techniques/lesson-13-systems-thinking-specs#constraints-and-invariants-defining-correctness) and migrate into code during implementation. Once inlined, the code carries the constraint—not just the implementation, but the *rule* it enforces. This is what makes it safe to [delete the spec](/practical-techniques/lesson-12-spec-driven-development) after implementation: the WHY has migrated into the codebase. ## The Knowledge Cache Anti-Pattern -You've extracted architectural knowledge from your codebase with an agent—clean diagrams, comprehensive API documentation, detailed component relationships. You save it as `ARCHITECTURE.md` and commit it. Now you have a cache invalidation problem: code changes (always), documentation doesn't (usually), and future agents find both during code research ([Lesson 5](/docs/methodology/lesson-5-grounding)). The diagram below shows the divergence. +You've extracted architectural knowledge from your codebase with an agent—clean diagrams, comprehensive API documentation, detailed component relationships. You save it as `ARCHITECTURE.md` and commit it. Now you have a cache invalidation problem: code changes (always), documentation doesn't (usually), and future agents find both during code research ([Lesson 5](/methodology/lesson-5-grounding)). The diagram below shows the divergence. ```mermaid sequenceDiagram participant KB as 🗄️ Codebase
(Persistent) participant Agent as ⚡ Agent
(Stateless) - rect rgb(167, 139, 250, 0.1) + rect rgb(147, 142, 235, 0.1) Note over Agent: 1. RESEARCH Agent->>KB: Read source code KB->>Agent: Knowledge extracted @@ -158,7 +160,7 @@ sequenceDiagram alt ✅ Good Path Note over Agent: Knowledge stays in context - rect rgb(167, 139, 250, 0.1) + rect rgb(147, 142, 235, 0.1) Note over Agent: 2. PLAN Note over Agent: 3. EXECUTE Agent->>KB: Edit code @@ -169,7 +171,7 @@ sequenceDiagram else ❌ Bad Path: Cache Research Agent->>KB: Save ARCHITECTURE.md Note over KB: Cache committed - rect rgb(167, 139, 250, 0.1) + rect rgb(147, 142, 235, 0.1) Note over Agent: 2. PLAN Note over Agent: 3. EXECUTE Agent->>KB: Edit code @@ -193,11 +195,11 @@ The moment you commit extracted knowledge, every code change requires documentat - **Comments as agent-critical sections (use sparingly)** - For genuinely high-risk code (authentication, cryptography, payments, PII), write comments as prompts using imperative directives (NEVER, MUST, ALWAYS) to create deliberate friction. This protection mechanism guards sensitive code from accidental modification. **Overuse is counterproductive**—if everything is marked CRITICAL, the signal becomes noise and legitimate work slows down. -- **Constraint IDs migrate from spec to code** — When specs use IDs like C-001 or I-001 ([Lesson 13](/docs/practical-techniques/lesson-13-systems-thinking-specs#constraints-and-invariants-defining-correctness)), agents inline them into code comments during implementation. The code then carries the constraint rule, making it safe to delete the spec ([Lesson 12](/docs/practical-techniques/lesson-12-spec-driven-development)). +- **Constraint IDs migrate from spec to code** — When specs use IDs like C-001 or I-001 ([Lesson 13](/practical-techniques/lesson-13-systems-thinking-specs#constraints-and-invariants-defining-correctness)), agents inline them into code comments during implementation. The code then carries the constraint rule, making it safe to delete the spec ([Lesson 12](/practical-techniques/lesson-12-spec-driven-development)). -- **You are the quality circuit breaker** - Code review ([Lesson 9](/docs/practical-techniques/lesson-9-reviewing-code)) prevents negative compounding. Accepting bad patterns lets them enter pattern context for future agents. Rejecting them breaks the negative feedback loop. +- **You are the quality circuit breaker** - Code review ([Lesson 9](/practical-techniques/lesson-9-reviewing-code)) prevents negative compounding. Accepting bad patterns lets them enter pattern context for future agents. Rejecting them breaks the negative feedback loop. -- **Avoid knowledge cache anti-patterns** - Code research tools (Explore, ChunkHound, semantic search) extract architectural knowledge dynamically from source code every time you need it. Saving extracted knowledge to .md files creates unnecessary caches that become stale, pollute future grounding with duplicated information, and create impossible cache invalidation problems. Trust the grounding process ([Lesson 5](/docs/methodology/lesson-5-grounding)) to re-extract knowledge on-demand from the single source of truth. +- **Avoid knowledge cache anti-patterns** - Code research tools (Explore, ChunkHound, semantic search) extract architectural knowledge dynamically from source code every time you need it. Saving extracted knowledge to .md files creates unnecessary caches that become stale, pollute future grounding with duplicated information, and create impossible cache invalidation problems. Trust the grounding process ([Lesson 5](/methodology/lesson-5-grounding)) to re-extract knowledge on-demand from the single source of truth. --- diff --git a/website/docs/practical-techniques/lesson-12-spec-driven-development.md b/website/docs/practical-techniques/lesson-12-spec-driven-development.md index 7527853..a571ee3 100644 --- a/website/docs/practical-techniques/lesson-12-spec-driven-development.md +++ b/website/docs/practical-techniques/lesson-12-spec-driven-development.md @@ -1,7 +1,9 @@ --- sidebar_position: 7 -sidebar_label: 'Lesson 12: Spec Driven Development' -title: 'Lesson 12: Spec Driven Development' +sidebar_label: 'Spec-Driven Development' +sidebar_custom_props: + sectionNumber: 12 +title: 'Spec-Driven Development' --- import KnowledgeExpansionDiamond from '@site/src/components/VisualElements/KnowledgeExpansionDiamond'; @@ -24,19 +26,19 @@ This mirrors traditional waterfall, but with AI agents, it happens in hours rath ## The Duplicate Knowledge Problem -Once implementation is complete and the code passes tests ([Lesson 8](/docs/practical-techniques/lesson-8-tests-as-guardrails)) and review ([Lesson 9](/docs/practical-techniques/lesson-9-reviewing-code)), you have a problem: the same knowledge exists in three places—the spec, the design, and the code. +Once implementation is complete and the code passes tests ([Lesson 8](/practical-techniques/lesson-8-tests-as-guardrails)) and review ([Lesson 9](/practical-techniques/lesson-9-reviewing-code)), you have a problem: the same knowledge exists in three places—the spec, the design, and the code. Which is correct? The spec says "users can have at most 5 active sessions." The code implements a limit of 10. The design document mentions "configurable session limits." Three sources, three different truths. -This is exactly the [Knowledge Cache Anti-Pattern](/docs/practical-techniques/lesson-11-agent-friendly-code#the-knowledge-cache-anti-pattern) from Lesson 11. Saved specifications become stale the moment code changes. Future agents researching your codebase find both the outdated spec and the current code, leading to confusion and conflicting implementations. +This is exactly the [Knowledge Cache Anti-Pattern](/practical-techniques/lesson-11-agent-friendly-code#the-knowledge-cache-anti-pattern) from Lesson 11. Saved specifications become stale the moment code changes. Future agents researching your codebase find both the outdated spec and the current code, leading to confusion and conflicting implementations. -Here's the key insight from [Lesson 5](/docs/methodology/lesson-5-grounding): **grounding tools already perform knowledge extraction.** When ChunkHound's [code research](https://chunkhound.github.io/code-research) processes 50,000 tokens of raw code and returns a 3,000-token synthesis, that synthesis IS a spec—extracted on-demand from the source of truth. You don't need to maintain spec files because you can regenerate them from code whenever needed. +Here's the key insight from [Lesson 5](/methodology/lesson-5-grounding): **grounding tools already perform knowledge extraction.** When ChunkHound's [code research](https://chunkhound.github.io/code-research) processes 50,000 tokens of raw code and returns a 3,000-token synthesis, that synthesis IS a spec—extracted on-demand from the source of truth. You don't need to maintain spec files because you can regenerate them from code whenever needed. -But not all spec knowledge is equal. **HOW** knowledge—implementation details, data flows, edge cases—gets fully encoded in code during implementation. Once the code exists, the spec's HOW is redundant. **WHY** knowledge—rejected alternatives, business rationale for a constraint, compliance mandates—can't be expressed in code. A constraint comment like `// C-001: NEVER process duplicate webhook` ([Lesson 11](/docs/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents)) tells the agent *what* to enforce, but not *why* you chose idempotency-via-database over idempotency-via-cache. That rationale is the **residual**—the part of the spec that doesn't migrate into code. +But not all spec knowledge is equal. **HOW** knowledge—implementation details, data flows, edge cases—gets fully encoded in code during implementation. Once the code exists, the spec's HOW is redundant. **WHY** knowledge—rejected alternatives, business rationale for a constraint, compliance mandates—can't be expressed in code. A constraint comment like `// C-001: NEVER process duplicate webhook` ([Lesson 11](/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents)) tells the agent *what* to enforce, but not *why* you chose idempotency-via-database over idempotency-via-cache. That rationale is the **residual**—the part of the spec that doesn't migrate into code. -**The solution: DELETE the HOW spec after implementation.** The code is the single source of truth for implementation. For the small WHY residual (rejected alternatives, business context that drove constraints), have the agent diff the original spec against the final code—what's expressed in code is now redundant. The residual gets committed as a decision record ([Lesson 11](/docs/practical-techniques/lesson-11-agent-friendly-code#the-knowledge-cache-anti-pattern)). Everything else is regenerable from code via grounding tools. +**The solution: DELETE the HOW spec after implementation.** The code is the single source of truth for implementation. For the small WHY residual (rejected alternatives, business context that drove constraints), have the agent diff the original spec against the final code—what's expressed in code is now redundant. The residual gets committed as a decision record ([Lesson 11](/practical-techniques/lesson-11-agent-friendly-code#the-knowledge-cache-anti-pattern)). Everything else is regenerable from code via grounding tools. ## Mainstream SDD vs This Approach @@ -59,13 +61,13 @@ Agentic search, semantic search, [ChunkHound](https://chunkhound.github.io)'s co - "What's the API contract?" → Interface documentation - "What are the architectural boundaries?" → Component diagram -Same source of truth, different compression targets. The grounding process ([Lesson 5](/docs/methodology/lesson-5-grounding)) extracts exactly what you need, when you need it—no stale caches, no conflicting sources. +Same source of truth, different compression targets. The grounding process ([Lesson 5](/methodology/lesson-5-grounding)) extracts exactly what you need, when you need it—no stale caches, no conflicting sources. ## Modifying Existing Code When modifying existing code, the knowledge already lives in the implementation. Your job is to extract it, reshape it for whoever needs to approve or understand the change, modify it, then re-implement. -**Extract the spec from code.** Use [grounding techniques](/docs/methodology/lesson-5-grounding) to surface the current implementation as a spec. The shape of that spec depends on who needs it: +**Extract the spec from code.** Use [grounding techniques](/methodology/lesson-5-grounding) to surface the current implementation as a spec. The shape of that spec depends on who needs it: - **Product stakeholders** need behavioral specs: what users can do, what business rules apply, what outcomes to expect – `Users can have up to 5 concurrent sessions. When exceeded, the oldest session is terminated.` @@ -77,7 +79,7 @@ Same source code, different compression targets. Extract the right shape for you **Modify the spec, then gap-analyze.** Once you've edited the spec with your desired changes, perform gap analysis: compare the modified spec against the current implementation. What exists? What's missing? What conflicts? This bridges "where we are" to "where we want to be." -**Follow the four-phase workflow.** With the gap identified, apply the standard [Research → Plan → Execute → Validate](/docs/methodology/lesson-3-high-level-methodology) workflow. The modified spec feeds your planning phase; the gap analysis tells you exactly what needs to change. +**Follow the four-phase workflow.** With the gap identified, apply the standard [Research → Plan → Execute → Validate](/methodology/lesson-3-high-level-methodology) workflow. The modified spec feeds your planning phase; the gap analysis tells you exactly what needs to change. **Clean up.** Delete temporary spec files and any test scaffolding. The code is now the source of truth again. @@ -92,7 +94,7 @@ If you've followed this course, you've been practicing SDD all along. Every time ...you were doing Spec Driven Development. The spec lived in your context window—RAM, not disk. -What makes this safe is [constraint migration](/docs/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents): during implementation, the agent inlines spec constraints—with their IDs—into code comments. The WHY moves from spec to code. When you close the conversation, nothing is lost. +What makes this safe is [constraint migration](/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents): during implementation, the agent inlines spec constraints—with their IDs—into code comments. The WHY moves from spec to code. When you close the conversation, nothing is lost. ### When to Persist Specs diff --git a/website/docs/practical-techniques/lesson-13-systems-thinking-specs.md b/website/docs/practical-techniques/lesson-13-systems-thinking-specs.md index d2c5039..6f6a3e3 100644 --- a/website/docs/practical-techniques/lesson-13-systems-thinking-specs.md +++ b/website/docs/practical-techniques/lesson-13-systems-thinking-specs.md @@ -1,14 +1,16 @@ --- sidebar_position: 8 -sidebar_label: 'Lesson 13: Thinking in Systems' -title: 'Lesson 13: Thinking in Systems' +sidebar_label: 'Thinking in Systems' +sidebar_custom_props: + sectionNumber: 13 +title: 'Thinking in Systems' --- import SystemFlowDiagram from '@site/src/components/VisualElements/SystemFlowDiagram'; import SystemBoundaryDiagram from '@site/src/components/VisualElements/SystemBoundaryDiagram'; import SpecCodeZoomDiagram from '@site/src/components/VisualElements/SpecCodeZoomDiagram'; -[Lesson 12](/docs/practical-techniques/lesson-12-spec-driven-development) established that specs are scaffolding—temporary thinking tools deleted after implementation. But what makes a spec *good enough* to produce quality code? +[Lesson 12](/practical-techniques/lesson-12-spec-driven-development) established that specs are scaffolding—temporary thinking tools deleted after implementation. But what makes a spec *good enough* to produce quality code? Think of a spec as a zoom lens. Zoomed out, you see architecture—modules, boundaries, invariants. Zoomed in, you see implementation—edge cases, error handling, concurrency. You oscillate between views, and the spec sharpens through contact with implementation[^5]. @@ -29,9 +31,9 @@ This has a practical consequence for debugging. When implementation diverges fro -Start with three sections: **Architecture**, **Interfaces**, and **State**—enough to generate a first pass. The spec is a hypothesis. The code is an experiment. Implementation reveals what the spec missed: a state transition you didn't anticipate, a concurrency constraint, an unrealistic performance budget. Zoom out—extract the updated understanding from code via [ChunkHound code research](https://chunkhound.github.io/code-research). Fix the architecture. Zoom back in—regenerate. Repeat until convergence, then [delete the spec](/docs/practical-techniques/lesson-12-spec-driven-development). +Start with three sections: **Architecture**, **Interfaces**, and **State**—enough to generate a first pass. The spec is a hypothesis. The code is an experiment. Implementation reveals what the spec missed: a state transition you didn't anticipate, a concurrency constraint, an unrealistic performance budget. Zoom out—extract the updated understanding from code via [ChunkHound code research](https://chunkhound.github.io/code-research). Fix the architecture. Zoom back in—regenerate. Repeat until convergence, then [delete the spec](/practical-techniques/lesson-12-spec-driven-development). -This is [Lesson 3's four-phase cycle](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow) applied fractally. At the spec level: research the domain, plan architecture, write spec, validate completeness. At the code level: research codebase, plan changes, execute, validate tests. Each zoom transition—spec→code or code→spec—is itself a Research→Plan→Execute→Validate loop. The depth of iteration scales with complexity: a simple feature converges in one pass; a complex architectural change might take five. +This is [Lesson 3's four-phase cycle](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow) applied fractally. At the spec level: research the domain, plan architecture, write spec, validate completeness. At the code level: research codebase, plan changes, execute, validate tests. Each zoom transition—spec→code or code→spec—is itself a Research→Plan→Execute→Validate loop. The depth of iteration scales with complexity: a simple feature converges in one pass; a complex architectural change might take five. The sections below are the questions this process surfaces. You won't answer them all upfront—you'll discover which ones matter because the code reveals gaps there. @@ -208,7 +210,7 @@ Constraints limit *actions* (NEVER do X). Invariants describe *state* (X is alwa The **Data** and **Stress** columns transform a constraint from a wish into a testable requirement. "NEVER process duplicates" is a policy. "NEVER process duplicates, verified with 10K events at 100 concurrent deliveries" is an engineering requirement with a verification plan. (Note that C-001 and C-002 trace back to [third-party assumptions](#third-party-assumptions)—they exist *because* of Stripe's delivery semantics and signing behavior, not as arbitrary security choices.) -During implementation, these IDs migrate into code as structured comments ([Lesson 11](/docs/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents)): +During implementation, these IDs migrate into code as structured comments ([Lesson 11](/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents)): ```typescript // C-001: NEVER process duplicate webhook — idempotency via unique constraint on stripe_event_id @@ -220,7 +222,7 @@ export async function handleWebhook(req: Request): Promise { } ``` -The spec table is the authoritative source during design. The code comments become the authoritative source after implementation. This is what makes [deleting the spec](/docs/practical-techniques/lesson-12-spec-driven-development) safe—the constraints have migrated. +The spec table is the authoritative source during design. The code comments become the authoritative source after implementation. This is what makes [deleting the spec](/practical-techniques/lesson-12-spec-driven-development) safe—the constraints have migrated. ### Invariants @@ -348,7 +350,7 @@ Each loop through this cycle reveals what the spec missed. The first pass might **You're done when the loop produces no new gaps:** the code passes all behavioral scenarios, the spec accounts for all constraints the code revealed, and the last pass surfaces nothing new. That's a testable termination condition. A simple feature converges in one loop. A complex architectural change might take five. But you discover which you're dealing with *by running the loop*, not by predicting it. -**Iteration speed is the multiplier.** Code generation is approaching post-scarcity[^1]—the scarce resource is your judgment about *what* to build. The engineer who runs ten hypothesis→experiment→verify loops per day outperforms the one who runs two with a more thorough upfront spec[^4][^5]. This is the same insight that made Agile outperform Waterfall, compressed from weeks-per-iteration to minutes. Use [exploration planning](/docs/methodology/lesson-3-high-level-methodology#phase-2-plan-strategic-decision) (Lesson 3) and [ArguSeek](/docs/methodology/lesson-5-grounding#arguseek-isolated-context--state) (Lesson 5) to research before each loop. For system-level work, start from the [full template](/prompts/specifications/spec-template). Validate through the [SDD workflow](/docs/practical-techniques/lesson-12-spec-driven-development)—gap-analyze, implement, then delete the spec. What survives deletion: constraint IDs inlined in code ([Lesson 11](/docs/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents)), and the small WHY residual (rejected alternatives, business rationale) committed as decision records. +**Iteration speed is the multiplier.** Code generation is approaching post-scarcity[^1]—the scarce resource is your judgment about *what* to build. The engineer who runs ten hypothesis→experiment→verify loops per day outperforms the one who runs two with a more thorough upfront spec[^4][^5]. This is the same insight that made Agile outperform Waterfall, compressed from weeks-per-iteration to minutes. Use [exploration planning](/methodology/lesson-3-high-level-methodology#phase-2-plan-strategic-decision) (Lesson 3) and [ArguSeek](/methodology/lesson-5-grounding#arguseek-isolated-context--state) (Lesson 5) to research before each loop. For system-level work, start from the [full template](/prompts/specifications/spec-template). Validate through the [SDD workflow](/practical-techniques/lesson-12-spec-driven-development)—gap-analyze, implement, then delete the spec. What survives deletion: constraint IDs inlined in code ([Lesson 11](/practical-techniques/lesson-11-agent-friendly-code#comments-as-context-engineering-critical-sections-for-agents)), and the small WHY residual (rejected alternatives, business rationale) committed as decision records. :::info Template Sections Not Covered The [full spec template](/prompts/specifications/spec-template) includes sections not taught in this lesson: **Background** (problem statement + baseline metrics), **Caching** (strategy/TTL/invalidation), **Endpoints** (REST contract details), **Cleanup Flows** (teardown/rollback sequences), **Code Traceability** (file:line evidence columns). Use these when the code pulls them from you—not before. diff --git a/website/docs/practical-techniques/lesson-6-project-onboarding.md b/website/docs/practical-techniques/lesson-6-project-onboarding.md index 1ea9467..f3ff23a 100644 --- a/website/docs/practical-techniques/lesson-6-project-onboarding.md +++ b/website/docs/practical-techniques/lesson-6-project-onboarding.md @@ -1,7 +1,9 @@ --- sidebar_position: 1 -sidebar_label: 'Lesson 6: Project Onboarding' -title: 'Lesson 6: Project Onboarding' +sidebar_label: 'Project Onboarding' +sidebar_custom_props: + sectionNumber: 6 +title: 'Project Onboarding' --- import Tabs from '@theme/Tabs'; @@ -220,11 +222,11 @@ npm run deploy # Deploy to GitHub Pages ## Automated Generation: Bootstrap with AI -**The meta-move: Apply lessons 3-5 to generate context files automatically.** Instead of manually drafting `AGENTS.md` or `CLAUDE.md`, use the four-phase workflow ([Lesson 3](/docs/methodology/lesson-3-high-level-methodology)) to let agents bootstrap their own context. **Research phase:** Use ChunkHound's `code_research()` tool to understand your project's architecture, patterns, and conventions—query for architecture, coding styles, module responsibilities, and testing conventions, etc to build a comprehensive architectural understanding. Use ArguSeek's `research_iteratively()` and `fetch_url()` to retrieve framework documentation, best practices, and security guidelines relevant to your tech stack. **Plan phase:** The agent synthesizes codebase insights (from ChunkHound) and domain knowledge (from ArguSeek) into a structured context file plan. **Execute phase:** Generate the context file using prompt optimization techniques specific to your model. **Validate phase:** Test the generated context with a typical task, iterate based on gaps. +**The meta-move: Apply lessons 3-5 to generate context files automatically.** Instead of manually drafting `AGENTS.md` or `CLAUDE.md`, use the four-phase workflow ([Lesson 3](/methodology/lesson-3-high-level-methodology)) to let agents bootstrap their own context. **Research phase:** Use ChunkHound's `code_research()` tool to understand your project's architecture, patterns, and conventions—query for architecture, coding styles, module responsibilities, and testing conventions, etc to build a comprehensive architectural understanding. Use ArguSeek's `research_iteratively()` and `fetch_url()` to retrieve framework documentation, best practices, and security guidelines relevant to your tech stack. **Plan phase:** The agent synthesizes codebase insights (from ChunkHound) and domain knowledge (from ArguSeek) into a structured context file plan. **Execute phase:** Generate the context file using prompt optimization techniques specific to your model. **Validate phase:** Test the generated context with a typical task, iterate based on gaps. -This prompt demonstrates grounding ([Lesson 5](/docs/methodology/lesson-5-grounding)): ChunkHound provides codebase-specific context, ArguSeek provides current ecosystem knowledge, and structured Chain-of-Thought ensures the agent follows a methodical path. The result: production-ready context files generated in one iteration, not manually curated over weeks. Add tribal knowledge manually afterward—production incidents, team conventions, non-obvious gotchas that only humans know. +This prompt demonstrates grounding ([Lesson 5](/methodology/lesson-5-grounding)): ChunkHound provides codebase-specific context, ArguSeek provides current ecosystem knowledge, and structured Chain-of-Thought ensures the agent follows a methodical path. The result: production-ready context files generated in one iteration, not manually curated over weeks. Add tribal knowledge manually afterward—production incidents, team conventions, non-obvious gotchas that only humans know. :::tip Reference See the complete prompt template with validation guidance and adaptations: [Generate AGENTS.md](/prompts/onboarding/generate-agents-md) diff --git a/website/docs/practical-techniques/lesson-7-planning-execution.md b/website/docs/practical-techniques/lesson-7-planning-execution.md index d62544f..ac1d713 100644 --- a/website/docs/practical-techniques/lesson-7-planning-execution.md +++ b/website/docs/practical-techniques/lesson-7-planning-execution.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 -sidebar_label: 'Lesson 7: Planning & Execution' +sidebar_label: 'Planning & Execution' +sidebar_custom_props: + sectionNumber: 7 title: 'Planning & Execution' --- @@ -75,7 +77,7 @@ Now the agent must read the endpoint implementation, trace execution, and cite s **Combining with Chain-of-Thought:** -Evidence requirements work independently or combined with step-by-step instructions. For complex debugging, use both—[Chain-of-Thought](../methodology/lesson-4-prompting-101.md#chain-of-thought-paving-a-clear-path) controls the execution path while evidence requirements ensure each step is grounded: +Evidence requirements work independently or combined with step-by-step instructions. For complex debugging, use both—[Chain-of-Thought](../methodology/lesson-4-prompting-101.mdx#chain-of-thought-paving-a-clear-path) controls the execution path while evidence requirements ensure each step is grounded: ``` Debug the failing test in UserService.test.ts: @@ -128,7 +130,7 @@ Use ArguSeek, validate the approach against current security best practices. ### Glance Over Suggested Changes -Before autonomous execution, review the plan to catch architectural mismatches and scope issues. This applies [Lesson 4's constraint principles](../methodology/lesson-4-prompting-101.md#constraints-as-guardrails) during plan review—you're validating that the agent's proposed approach is sufficiently constrained and grounded before it executes. +Before autonomous execution, review the plan to catch architectural mismatches and scope issues. This applies [Lesson 4's constraint principles](../methodology/lesson-4-prompting-101.mdx#constraints-as-guardrails) during plan review—you're validating that the agent's proposed approach is sufficiently constrained and grounded before it executes. **What you're checking:** diff --git a/website/docs/practical-techniques/lesson-8-tests-as-guardrails.md b/website/docs/practical-techniques/lesson-8-tests-as-guardrails.md index 1252930..10d3654 100644 --- a/website/docs/practical-techniques/lesson-8-tests-as-guardrails.md +++ b/website/docs/practical-techniques/lesson-8-tests-as-guardrails.md @@ -1,7 +1,9 @@ --- sidebar_position: 3 -sidebar_label: 'Lesson 8: Tests as Guardrails' -title: 'Lesson 8: Tests as Guardrails' +sidebar_label: 'Tests as Guardrails' +sidebar_custom_props: + sectionNumber: 8 +title: 'Tests as Guardrails' --- import ThreeContextWorkflow from '@site/src/components/VisualElements/ThreeContextWorkflow'; @@ -42,7 +44,7 @@ When tests become the optimization target, agents optimize for passing tests rat ::: -Apply the same planning and execution methodology from [Lesson 7](./lesson-7-planning-execution.md) to each step—writing code, writing tests, and triaging failures. Each follows the same pattern: research requirements, plan approach, execute, verify. The critical difference: use **fresh contexts** for each step. This leverages the stateless nature from [Lesson 1: LLMs Demystified](../fundamentals/lesson-1-how-llms-work.md) and [Lesson 2: Agents Demystified](../fundamentals/lesson-2-how-agents-work.md)—the agent doesn't carry assumptions or defend prior decisions between contexts. +Apply the same planning and execution methodology from [Lesson 7](./lesson-7-planning-execution.md) to each step—writing code, writing tests, and triaging failures. Each follows the same pattern: research requirements, plan approach, execute, verify. The critical difference: use **fresh contexts** for each step. This leverages the stateless nature from [Lesson 1: LLMs Demystified](../fundamentals/lesson-1-how-llms-work.mdx) and [Lesson 2: Agents Demystified](../fundamentals/lesson-2-how-agents-work.mdx)—the agent doesn't carry assumptions or defend prior decisions between contexts. ## The three-context workflow: @@ -120,7 +122,7 @@ AI agents can simulate actual user behavior by giving them a task and the tools **The workflow:** Use agents for discovery, then solidify findings into deterministic tests. Agents explore the unknown; deterministic tests prevent backsliding on the known. -When prompting agents for simulation testing, apply the techniques from [Lesson 4](../methodology/lesson-4-prompting-101.md)—clear instructions, specific constraints, expected outputs—to craft effective exploration prompts that guide agents toward high-value edge case discovery. +When prompting agents for simulation testing, apply the techniques from [Lesson 4](../methodology/lesson-4-prompting-101.mdx)—clear instructions, specific constraints, expected outputs—to craft effective exploration prompts that guide agents toward high-value edge case discovery. :::tip Connecting Agents to Your Product @@ -169,17 +171,17 @@ When tests fail, apply the same four-phase workflow from [Lesson 3](../methodolo ### The Diagnostic Prompt Pattern -This diagnostic prompt applies techniques from [Lesson 4](../methodology/lesson-4-prompting-101.md): [Chain-of-Thought](../methodology/lesson-4-prompting-101.md#chain-of-thought-paving-a-clear-path) sequential steps, [constraints](../methodology/lesson-4-prompting-101.md#constraints-as-guardrails) requiring evidence, and [structured format](../methodology/lesson-4-prompting-101.md#applying-structure-to-prompts). Understanding why each element exists lets you adapt this pattern for other diagnostic tasks. +This diagnostic prompt applies techniques from [Lesson 4](../methodology/lesson-4-prompting-101.mdx): [Chain-of-Thought](../methodology/lesson-4-prompting-101.mdx#chain-of-thought-paving-a-clear-path) sequential steps, [constraints](../methodology/lesson-4-prompting-101.mdx#constraints-as-guardrails) requiring evidence, and [structured format](../methodology/lesson-4-prompting-101.mdx#applying-structure-to-prompts). Understanding why each element exists lets you adapt this pattern for other diagnostic tasks. **Why this works:** -- **Fenced code block** (``````) preserves error formatting and prevents the LLM from interpreting failure messages as instructions ([structured format](../methodology/lesson-4-prompting-101.md#applying-structure-to-prompts)) -- **"Use the code research"** is an explicit grounding directive—forces codebase search instead of hallucination from training patterns ([constraints](../methodology/lesson-4-prompting-101.md#constraints-as-guardrails)) -- **DIAGNOSE numbered steps** implement [Chain-of-Thought](../methodology/lesson-4-prompting-101.md#chain-of-thought-paving-a-clear-path), forcing sequential analysis (can't jump to "root cause" without examining test intent first) -- **"Understand the intention"** (step 2) ensures the agent articulates WHY the test exists, not just WHAT it does—critical for [CoT reasoning](../methodology/lesson-4-prompting-101.md#chain-of-thought-paving-a-clear-path) -- **DETERMINE binary decision** [constrains output](../methodology/lesson-4-prompting-101.md#constraints-as-guardrails) to "bug vs outdated test" instead of open-ended conclusions +- **Fenced code block** (``````) preserves error formatting and prevents the LLM from interpreting failure messages as instructions ([structured format](../methodology/lesson-4-prompting-101.mdx#applying-structure-to-prompts)) +- **"Use the code research"** is an explicit grounding directive—forces codebase search instead of hallucination from training patterns ([constraints](../methodology/lesson-4-prompting-101.mdx#constraints-as-guardrails)) +- **DIAGNOSE numbered steps** implement [Chain-of-Thought](../methodology/lesson-4-prompting-101.mdx#chain-of-thought-paving-a-clear-path), forcing sequential analysis (can't jump to "root cause" without examining test intent first) +- **"Understand the intention"** (step 2) ensures the agent articulates WHY the test exists, not just WHAT it does—critical for [CoT reasoning](../methodology/lesson-4-prompting-101.mdx#chain-of-thought-paving-a-clear-path) +- **DETERMINE binary decision** [constrains output](../methodology/lesson-4-prompting-101.mdx#constraints-as-guardrails) to "bug vs outdated test" instead of open-ended conclusions - **"Provide evidence"** requires file paths and line numbers—concrete proof via [require evidence](./lesson-7-planning-execution.md#require-evidence-to-force-grounding), not vague assertions You can adapt this for performance issues, security vulnerabilities, or deployment failures by changing the diagnostic steps while preserving the structure: sequential CoT → constrained decision → evidence requirement. diff --git a/website/docs/practical-techniques/lesson-9-reviewing-code.md b/website/docs/practical-techniques/lesson-9-reviewing-code.md index 562d5dc..6a59732 100644 --- a/website/docs/practical-techniques/lesson-9-reviewing-code.md +++ b/website/docs/practical-techniques/lesson-9-reviewing-code.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 -sidebar_label: 'Lesson 9: Reviewing Code' +sidebar_label: 'Reviewing Code' +sidebar_custom_props: + sectionNumber: 9 title: 'Reviewing Code' --- @@ -11,7 +13,7 @@ You've completed the implementation. Tests pass. The agent executed your plan su This is the **Validate** phase from [Lesson 3's four-phase workflow](../methodology/lesson-3-high-level-methodology.md)—the systematic quality gate before shipping. Code review catches the probabilistic errors that agents inevitably introduce: subtle logic bugs, architectural mismatches, edge cases handled incorrectly, patterns that don't quite fit your codebase. -The key insight: **review in a fresh context, separate from where the code was written.** This prevents confirmation bias and leverages the stateless nature of agents from [Lesson 1: LLMs Demystified](../fundamentals/lesson-1-how-llms-work.md) and [Lesson 2: Agents Demystified](../fundamentals/lesson-2-how-agents-work.md). An agent reviewing its own work in the same conversation will defend its decisions. An agent in a fresh context analyzes objectively, without attachment to prior choices. +The key insight: **review in a fresh context, separate from where the code was written.** This prevents confirmation bias and leverages the stateless nature of agents from [Lesson 1: LLMs Demystified](../fundamentals/lesson-1-how-llms-work.mdx) and [Lesson 2: Agents Demystified](../fundamentals/lesson-2-how-agents-work.mdx). An agent reviewing its own work in the same conversation will defend its decisions. An agent in a fresh context analyzes objectively, without attachment to prior choices. :::info Agent-Only vs Mixed Codebases: A Critical Distinction @@ -27,7 +29,7 @@ The same engineering standards—DRY, YAGNI, architecture, maintainability, read ## The Review Prompt Template -This template integrates techniques from [Lesson 4: Prompting 101](../methodology/lesson-4-prompting-101.md). Understanding **why** each element exists lets you adapt this pattern for other review tasks (security audits, performance analysis, architectural review). +This template integrates techniques from [Lesson 4: Prompting 101](../methodology/lesson-4-prompting-101.mdx). Understanding **why** each element exists lets you adapt this pattern for other review tasks (security audits, performance analysis, architectural review). ```markdown You are an expert code reviewer. Analyze the current changeset and provide a critical review. @@ -100,7 +102,7 @@ Traditional PR descriptions optimize for one audience or the other—too verbose ### The Advanced Prompt Pattern -This prompt demonstrates multiple techniques from [Lesson 4 (Prompting 101)](../methodology/lesson-4-prompting-101.md), [Lesson 5 (Grounding)](../methodology/lesson-5-grounding.md), and [Lesson 7 (Planning & Execution)](./lesson-7-planning-execution.md): +This prompt demonstrates multiple techniques from [Lesson 4 (Prompting 101)](../methodology/lesson-4-prompting-101.mdx), [Lesson 5 (Grounding)](../methodology/lesson-5-grounding.md), and [Lesson 7 (Planning & Execution)](./lesson-7-planning-execution.md): @@ -114,7 +116,7 @@ This sub-agent capability is unique to [Claude Code CLI](/developer-tools/cli-co **Multi-source grounding ([Lesson 5](../methodology/lesson-5-grounding.md#production-pattern-multi-source-grounding)):** ArguSeek researches PR best practices while ChunkHound grounds descriptions in your actual codebase architecture and coding style. -**Structured prompting ([Lesson 4](../methodology/lesson-4-prompting-101.md)):** Persona, communication constraints, format boundaries, and structural requirements direct the agent to produce dual-optimized outputs. +**Structured prompting ([Lesson 4](../methodology/lesson-4-prompting-101.mdx)):** Persona, communication constraints, format boundaries, and structural requirements direct the agent to produce dual-optimized outputs. **Evidence requirements ([Lesson 7](./lesson-7-planning-execution.md#require-evidence-to-force-grounding)):** The prompt forces grounding through "explore the changes" and "learn the architecture"—the agent cannot draft accurate descriptions without reading actual commits and code. diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 5d3b674..50b8ea1 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -1,9 +1,13 @@ -import { themes as prismThemes } from 'prism-react-renderer'; import type { Config } from '@docusaurus/types'; import type * as Preset from '@docusaurus/preset-classic'; +import { lightTheme, darkTheme } from './src/prism-theme'; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) +const isTauri = process.env.TAURI_BUILD === '1'; +const editUrl = isTauri ? undefined : + 'https://github.com/agenticoding/agenticoding.github.io/tree/main/website/'; + const config: Config = { title: 'Agentic Coding', tagline: 'A structured methodology for agentic development', @@ -20,27 +24,8 @@ const config: Config = { // For GitHub pages deployment, it is often '//' baseUrl: '/', - // Modern favicon setup (SVG + Apple touch icon) - headTags: [ - { - tagName: 'link', - attributes: { - rel: 'icon', - href: '/img/icon.svg', - type: 'image/svg+xml', - }, - }, - { - tagName: 'link', - attributes: { - rel: 'apple-touch-icon', - href: '/img/apple-touch-icon.png', - }, - }, - ], - - // Analytics - scripts: [ + // Analytics (stripped in Tauri builds — no network assumption) + scripts: isTauri ? [] : [ { src: 'https://cloud.umami.is/script.js', defer: true, @@ -55,7 +40,7 @@ const config: Config = { deploymentBranch: 'gh-pages', trailingSlash: false, - onBrokenLinks: 'warn', + onBrokenLinks: 'throw', // Even if you don't use internationalization, you can use this field to set // useful metadata like html lang. For example, if your site is Chinese, you @@ -65,6 +50,41 @@ const config: Config = { locales: ['en'], }, + // Favicon + theme-color meta tags + headTags: [ + { + tagName: 'link', + attributes: { + rel: 'icon', + href: '/img/icon.svg', + type: 'image/svg+xml', + }, + }, + { + tagName: 'link', + attributes: { + rel: 'apple-touch-icon', + href: '/img/apple-touch-icon.png', + }, + }, + { + tagName: 'meta', + attributes: { + name: 'theme-color', + content: '#ffffff', + media: '(prefers-color-scheme: light)', + }, + }, + { + tagName: 'meta', + attributes: { + name: 'theme-color', + content: '#0d1117', + media: '(prefers-color-scheme: dark)', + }, + }, + ], + markdown: { mermaid: true, preprocessor: ({ fileContent }) => { @@ -85,6 +105,7 @@ const config: Config = { { docs: { sidebarPath: './sidebars.ts', + routeBasePath: '/', exclude: [ '**/_*.{js,jsx,ts,tsx,md,mdx}', '**/_*/**', @@ -92,8 +113,7 @@ const config: Config = { '**/__tests__/**', '**/CLAUDE.md', // Exclude AI agent instructions from build ], - editUrl: - 'https://github.com/agenticoding/agenticoding.github.io/tree/main/website/', + editUrl, showLastUpdateTime: false, showLastUpdateAuthor: false, }, @@ -115,8 +135,7 @@ const config: Config = { path: 'prompts', routeBasePath: 'prompts', sidebarPath: './sidebarsPrompts.ts', - editUrl: - 'https://github.com/agenticoding/agenticoding.github.io/tree/main/website/', + editUrl, showLastUpdateTime: false, showLastUpdateAuthor: false, }, @@ -128,8 +147,7 @@ const config: Config = { path: 'developer-tools', routeBasePath: 'developer-tools', sidebarPath: './sidebarsDeveloperTools.ts', - editUrl: - 'https://github.com/agenticoding/agenticoding.github.io/tree/main/website/', + editUrl, showLastUpdateTime: false, showLastUpdateAuthor: false, }, @@ -143,17 +161,19 @@ const config: Config = { explicitSearchResultPath: true, indexBlog: false, indexDocs: true, - docsRouteBasePath: '/docs', + docsRouteBasePath: '/', }, ], [ '@docusaurus/plugin-client-redirects', { createRedirects(existingPath) { - // For all paths, create redirect from old /AI-Coding-Course/ prefixed version - // Example: /docs/intro gets redirect from /AI-Coding-Course/docs/intro - // For root: / gets redirect from /AI-Coding-Course/ - return `/AI-Coding-Course${existingPath}`; + const redirects = [`/AI-Coding-Course${existingPath}`]; + // Redirect old /docs/* URLs to new /* URLs after routeBasePath change + if (!existingPath.startsWith('/docs')) { + redirects.push(`/docs${existingPath}`); + } + return redirects; }, }, ], @@ -178,88 +198,11 @@ const config: Config = { alt: 'Agentic Coding Logo', src: 'img/logo.svg', }, - items: [ - { - type: 'docSidebar', - sidebarId: 'tutorialSidebar', - position: 'left', - label: 'Course', - }, - { - type: 'doc', - docId: 'index', - position: 'left', - label: 'Prompt Library', - docsPluginId: 'prompts', - }, - { - type: 'doc', - docId: 'cli-coding-agents', - position: 'left', - label: 'Toolbox', - docsPluginId: 'developer-tools', - }, - { - type: 'doc', - docId: 'faq', - position: 'left', - label: 'FAQ', - }, - { - href: 'https://github.com/agenticoding/agenticoding.github.io', - label: 'GitHub', - position: 'right', - }, - ], - }, - footer: { - style: 'dark', - links: [ - { - title: 'Course', - items: [ - { - label: 'FAQ', - to: '/docs/faq', - }, - { - label: 'Getting Started', - to: '/docs', - }, - { - label: 'Course Modules', - to: '/docs/fundamentals/lesson-1-how-llms-work', - }, - ], - }, - { - title: 'Community', - items: [ - { - label: 'GitHub Discussions', - href: 'https://github.com/agenticoding/agenticoding.github.io/discussions', - }, - { - label: 'Report Issues', - href: 'https://github.com/agenticoding/agenticoding.github.io/issues', - }, - ], - }, - { - title: 'More', - items: [ - { - label: 'GitHub', - href: 'https://github.com/agenticoding/agenticoding.github.io', - }, - ], - }, - ], - copyright: `Copyright © ${new Date().getFullYear()} Agentic Coding. Built with Docusaurus.`, + items: [], // All navigation moved to sidebar }, prism: { - theme: prismThemes.github, - darkTheme: prismThemes.dracula, + theme: lightTheme, + darkTheme: darkTheme, additionalLanguages: [ 'bash', 'python', @@ -273,8 +216,7 @@ const config: Config = { theme: { light: 'base', dark: 'dark' }, options: { maxTextSize: 50000, - fontFamily: - 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace', + fontFamily: "'Monaspace Neon', monospace", }, }, } satisfies Preset.ThemeConfig, diff --git a/website/package-lock.json b/website/package-lock.json index f22f5bf..64f614e 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -35,6 +35,7 @@ "husky": "^9.0.0", "lint-staged": "^15.0.0", "prettier": "^3.0.0", + "puppeteer": "^24.37.5", "typescript": "~5.6.2" }, "engines": { @@ -5355,6 +5356,28 @@ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "license": "MIT" }, + "node_modules/@puppeteer/browsers": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz", + "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.3", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.4", + "tar-fs": "^3.1.1", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -5681,6 +5704,13 @@ "node": ">=14.16" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -6348,6 +6378,17 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "license": "MIT" }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.46.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", @@ -6891,6 +6932,16 @@ "node": ">= 10.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -7282,6 +7333,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/astring": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", @@ -7354,6 +7418,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/babel-loader": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", @@ -7444,6 +7523,99 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.5.tgz", + "integrity": "sha512-XvwYM6VZqKoqDll8BmSww5luA5eflDzY0uEFfBJtFKe4PAAtxBjU3YIxzIBzhyaEQBy1VXEQBto4cpN5RZJw+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.7.0.tgz", + "integrity": "sha512-64Rcwj8qlnTZU8Ps6JJEdSmxBEUGgI7g8l+lMtsJLl4IsfTcHMTfJ188u2iGV6P6YPRZrtv72B2kjn+hp+Yv3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.8.0.tgz", + "integrity": "sha512-reUN0M2sHRqCdG4lUK3Fw8w98eeUIZHL5c3H7Mbhk2yVBL+oofgaIp0ieLfD5QXwPCypBpmEEKU2WZKzbAk8GA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.21.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.9.10", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.10.tgz", @@ -7453,6 +7625,16 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/basic-ftp": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", + "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -7622,6 +7804,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7962,6 +8154,30 @@ "node": ">=6.0" } }, + "node_modules/chromium-bidi": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", + "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -8141,6 +8357,61 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -9536,6 +9807,16 @@ "lodash-es": "^4.17.21" } }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -9764,6 +10045,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/delaunator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", @@ -9837,6 +10133,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/devtools-protocol": { + "version": "0.0.1566079", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz", + "integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -10084,6 +10388,16 @@ "node": ">=0.10.0" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -10109,6 +10423,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/environment": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", @@ -10382,6 +10706,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", @@ -10959,6 +11326,16 @@ "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/eventsource-parser": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", @@ -11103,12 +11480,56 @@ "node": ">=0.10.0" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -11188,6 +11609,16 @@ "node": ">=0.8.0" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -11621,6 +12052,16 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-east-asian-width": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", @@ -11707,6 +12148,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/github-slugger": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", @@ -12536,7 +12992,21 @@ "requires-port": "^1.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, "node_modules/http-proxy-middleware": { @@ -12588,6 +13058,20 @@ "node": ">=10.19.0" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -12791,6 +13275,16 @@ "loose-envify": "^1.0.0" } }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -16948,6 +17442,13 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, "node_modules/mlly": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", @@ -17056,6 +17557,16 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -17561,6 +18072,40 @@ "node": ">=8" } }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/package-json": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", @@ -17822,6 +18367,13 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -19448,6 +20000,16 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -19510,6 +20072,54 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -19534,6 +20144,96 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/puppeteer": { + "version": "24.37.5", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.37.5.tgz", + "integrity": "sha512-3PAOIQLceyEmn1Fi76GkGO2EVxztv5OtdlB1m8hMUZL3f8KDHnlvXbvCXv+Ls7KzF1R0KdKBqLuT/Hhrok12hQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.13.0", + "chromium-bidi": "14.0.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1566079", + "puppeteer-core": "24.37.5", + "typed-query-selector": "^2.12.0" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "24.37.5", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.37.5.tgz", + "integrity": "sha512-ybL7iE78YPN4T6J+sPLO7r0lSByp/0NN6PvfBEql219cOnttoTFzCWKiBOjstXSqi/OKpwae623DWAsL7cn2MQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.13.0", + "chromium-bidi": "14.0.0", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1566079", + "typed-query-selector": "^2.12.0", + "webdriver-bidi-protocol": "0.4.1", + "ws": "^8.19.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/puppeteer/node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -20328,6 +21028,16 @@ "node": ">=0.10" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -20776,9 +21486,9 @@ } }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -21274,6 +21984,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -21295,6 +22016,36 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/sort-css-media-queries": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", @@ -21435,6 +22186,18 @@ "node": ">= 0.4" } }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -21861,6 +22624,44 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tar-fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, "node_modules/terser": { "version": "5.44.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", @@ -21948,6 +22749,16 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -22275,6 +23086,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-query-selector": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz", + "integrity": "sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==", + "dev": true, + "license": "MIT" + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -22927,6 +23745,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", + "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webpack": { "version": "5.102.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", @@ -23680,6 +24505,16 @@ "xml-js": "bin/cli.js" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -23699,6 +24534,68 @@ "node": ">= 14.6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yocto-queue": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", diff --git a/website/package.json b/website/package.json index 2a44145..ea68150 100644 --- a/website/package.json +++ b/website/package.json @@ -48,6 +48,7 @@ "husky": "^9.0.0", "lint-staged": "^15.0.0", "prettier": "^3.0.0", + "puppeteer": "^24.37.5", "typescript": "~5.6.2" }, "browserslist": { diff --git a/website/prompts/code-review/comprehensive-review.md b/website/prompts/code-review/comprehensive-review.md index e405618..6f2f997 100644 --- a/website/prompts/code-review/comprehensive-review.md +++ b/website/prompts/code-review/comprehensive-review.md @@ -9,17 +9,17 @@ import ComprehensiveReview from '@site/shared-prompts/\_comprehensive-review.mdx ### Overview -**Why four-category framework works:** [Persona directive](/docs/methodology/lesson-4-prompting-101#assigning-personas) ("expert code reviewer") biases vocabulary toward critical analysis instead of descriptive summarization—"violates single responsibility" vs "this function does multiple things." Explicit change description (`$DESCRIBE_CHANGES`) anchors [grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) by framing intent, enabling detection of misalignment between goals and execution (intended to add caching, actually introduced side effects). Sequential numbered structure implements [Chain-of-Thought](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path) reasoning across review dimensions, preventing premature conclusions—can't evaluate maintainability without first understanding architecture. Grounding directive ("Use ChunkHound") forces actual codebase investigation instead of hallucinating patterns from training data. "DO NOT EDIT" [constraint](/docs/methodology/lesson-4-prompting-101#constraints-as-guardrails) maintains separation between review and implementation phases, preventing premature fixes before comprehensive analysis. Four categories ensure systematic coverage: Architecture (structural correctness, pattern conformance, module boundaries), Code Quality (readability, style consistency, KISS adherence), Maintainability (future LLM comprehension, documentation sync, intent clarity), UX (meaningful enhancements, simplicity-value balance). +**Why four-category framework works:** [Persona directive](/methodology/lesson-4-prompting-101#assigning-personas) ("expert code reviewer") biases vocabulary toward critical analysis instead of descriptive summarization—"violates single responsibility" vs "this function does multiple things." Explicit change description (`$DESCRIBE_CHANGES`) anchors [grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) by framing intent, enabling detection of misalignment between goals and execution (intended to add caching, actually introduced side effects). Sequential numbered structure implements [Chain-of-Thought](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path) reasoning across review dimensions, preventing premature conclusions—can't evaluate maintainability without first understanding architecture. Grounding directive ("Use ChunkHound") forces actual codebase investigation instead of hallucinating patterns from training data. "DO NOT EDIT" [constraint](/methodology/lesson-4-prompting-101#constraints-as-guardrails) maintains separation between review and implementation phases, preventing premature fixes before comprehensive analysis. Four categories ensure systematic coverage: Architecture (structural correctness, pattern conformance, module boundaries), Code Quality (readability, style consistency, KISS adherence), Maintainability (future LLM comprehension, documentation sync, intent clarity), UX (meaningful enhancements, simplicity-value balance). -**When to use—critical fresh-context requirement:** Always run in [fresh context](/docs/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) separate from where code was written—agents reviewing their own work in the same conversation defend prior decisions rather than providing objective analysis (confirmation bias from accumulated context). Use after implementation completion (Execute phase done), post-refactoring (architecture changes), pre-PR submission ([Validate phase](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)). Critical: be specific with `$DESCRIBE_CHANGES`—vague descriptions ("fix bugs", "update code") prevent alignment analysis between intent and implementation; effective descriptions specify the architectural goal ("add Redis caching layer to user service", "refactor authentication to use JWT tokens"). Review is iterative: review in fresh context → fix issues → run tests → re-review in new conversation → repeat until green light or diminishing returns. Stop iterating when tests pass and remaining feedback is minor (3-4 cycles max)—excessive iteration introduces review-induced regressions where fixes address critique without improving functionality. +**When to use—critical fresh-context requirement:** Always run in [fresh context](/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) separate from where code was written—agents reviewing their own work in the same conversation defend prior decisions rather than providing objective analysis (confirmation bias from accumulated context). Use after implementation completion (Execute phase done), post-refactoring (architecture changes), pre-PR submission ([Validate phase](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)). Critical: be specific with `$DESCRIBE_CHANGES`—vague descriptions ("fix bugs", "update code") prevent alignment analysis between intent and implementation; effective descriptions specify the architectural goal ("add Redis caching layer to user service", "refactor authentication to use JWT tokens"). Review is iterative: review in fresh context → fix issues → run tests → re-review in new conversation → repeat until green light or diminishing returns. Stop iterating when tests pass and remaining feedback is minor (3-4 cycles max)—excessive iteration introduces review-induced regressions where fixes address critique without improving functionality. -**Prerequisites:** [Code research capabilities](https://chunkhound.github.io/) (semantic search across codebase, architectural pattern discovery, implementation reading), access to git working tree changes (`git diff`, `git status`), project architecture documentation (CLAUDE.md, AGENTS.md, README). Requires explicit description of intended changes (`$DESCRIBE_CHANGES`), access to both changed files and surrounding context for pattern conformance. Agent provides structured feedback by category with file paths, line numbers, specific issues, and actionable recommendations ([evidence requirements](/docs/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)). [Adapt pattern for specialized reviews](/docs/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work): security (attack surface mapping/input validation boundaries/authentication flows/credential handling/OWASP Top 10), performance (algorithmic complexity/database query efficiency/memory allocation/I/O operations/caching strategy), accessibility (semantic HTML structure/keyboard navigation/ARIA labels/screen reader compatibility/color contrast ratios), API design (REST conventions/error responses/versioning/backwards compatibility). +**Prerequisites:** [Code research capabilities](https://chunkhound.github.io/) (semantic search across codebase, architectural pattern discovery, implementation reading), access to git working tree changes (`git diff`, `git status`), project architecture documentation (CLAUDE.md, AGENTS.md, README). Requires explicit description of intended changes (`$DESCRIBE_CHANGES`), access to both changed files and surrounding context for pattern conformance. Agent provides structured feedback by category with file paths, line numbers, specific issues, and actionable recommendations ([evidence requirements](/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)). [Adapt pattern for specialized reviews](/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work): security (attack surface mapping/input validation boundaries/authentication flows/credential handling/OWASP Top 10), performance (algorithmic complexity/database query efficiency/memory allocation/I/O operations/caching strategy), accessibility (semantic HTML structure/keyboard navigation/ARIA labels/screen reader compatibility/color contrast ratios), API design (REST conventions/error responses/versioning/backwards compatibility). ### Related Lessons -- **[Lesson 3: High-Level Methodology](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate) — review is the Validate phase -- **[Lesson 4: Prompting 101](/docs/methodology/lesson-4-prompting-101)** - [Persona directives](/docs/methodology/lesson-4-prompting-101#assigning-personas), [Chain-of-Thought](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), [constraints](/docs/methodology/lesson-4-prompting-101#constraints-as-guardrails), structured prompting -- **[Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - ChunkHound for codebase research, preventing hallucination -- **[Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements, grounding techniques -- **[Lesson 8: Tests as Guardrails](/docs/practical-techniques/lesson-8-tests-as-guardrails#the-three-context-workflow)** - Fresh-context validation, preventing confirmation bias through three-context workflow -- **[Lesson 9: Reviewing Code](/docs/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work)** - Iterative review cycles, diminishing returns, mixed vs agent-only codebases +- **[Lesson 3: High-Level Methodology](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate) — review is the Validate phase +- **[Lesson 4: Prompting 101](/methodology/lesson-4-prompting-101)** - [Persona directives](/methodology/lesson-4-prompting-101#assigning-personas), [Chain-of-Thought](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), [constraints](/methodology/lesson-4-prompting-101#constraints-as-guardrails), structured prompting +- **[Lesson 5: Grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - ChunkHound for codebase research, preventing hallucination +- **[Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements, grounding techniques +- **[Lesson 8: Tests as Guardrails](/practical-techniques/lesson-8-tests-as-guardrails#the-three-context-workflow)** - Fresh-context validation, preventing confirmation bias through three-context workflow +- **[Lesson 9: Reviewing Code](/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work)** - Iterative review cycles, diminishing returns, mixed vs agent-only codebases diff --git a/website/prompts/debugging/evidence-based-debug.md b/website/prompts/debugging/evidence-based-debug.md index a175d88..d453194 100644 --- a/website/prompts/debugging/evidence-based-debug.md +++ b/website/prompts/debugging/evidence-based-debug.md @@ -9,15 +9,15 @@ import EvidenceBasedDebug from '@site/shared-prompts/\_evidence-based-debug.mdx' ### Overview -**Why evidence requirements prevent hallucination:** "Provide evidence (file paths, line numbers, actual values)" is an explicit [grounding directive](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)—agents cannot provide that evidence without retrieving it from the codebase. Without evidence requirements, agents produce pattern completion from training data ("probably a database timeout"), not analysis. Evidence forces codebase reading, execution tracing, and concrete citations. INVESTIGATE/ANALYZE/EXPLAIN numbered steps implement [Chain-of-Thought](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), forcing sequential analysis (can't jump to "root cause" without examining error context first). "Use the code research" is explicit retrieval directive—prevents relying on training patterns. Fenced code block preserves error formatting and prevents LLM from interpreting failure messages as instructions. Good evidence includes file paths with line numbers, actual variable/config values, specific function names, and complete stack traces—not vague assertions. +**Why evidence requirements prevent hallucination:** "Provide evidence (file paths, line numbers, actual values)" is an explicit [grounding directive](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)—agents cannot provide that evidence without retrieving it from the codebase. Without evidence requirements, agents produce pattern completion from training data ("probably a database timeout"), not analysis. Evidence forces codebase reading, execution tracing, and concrete citations. INVESTIGATE/ANALYZE/EXPLAIN numbered steps implement [Chain-of-Thought](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), forcing sequential analysis (can't jump to "root cause" without examining error context first). "Use the code research" is explicit retrieval directive—prevents relying on training patterns. Fenced code block preserves error formatting and prevents LLM from interpreting failure messages as instructions. Good evidence includes file paths with line numbers, actual variable/config values, specific function names, and complete stack traces—not vague assertions. -**When to use—fresh context requirement:** Production errors with stack traces/logs, unexpected behavior in specific scenarios, silent failures requiring code path tracing, performance bottlenecks needing profiling analysis, architectural issues spanning multiple files. Critical: use in separate conversation from implementation for unbiased analysis. This diagnostic pattern prevents "cycle of self-deception" where agents defend their own implementation. Running in [fresh context](/docs/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) provides objective analysis without prior assumptions. Always provide complete error output—truncated logs prevent accurate diagnosis. Challenge agent explanations when they don't fit observed behavior: "You said X causes timeout, but logs show connection established. Explain this discrepancy with evidence." Reject guesses without citations: "Show me the file and line number where this occurs." +**When to use—fresh context requirement:** Production errors with stack traces/logs, unexpected behavior in specific scenarios, silent failures requiring code path tracing, performance bottlenecks needing profiling analysis, architectural issues spanning multiple files. Critical: use in separate conversation from implementation for unbiased analysis. This diagnostic pattern prevents "cycle of self-deception" where agents defend their own implementation. Running in [fresh context](/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) provides objective analysis without prior assumptions. Always provide complete error output—truncated logs prevent accurate diagnosis. Challenge agent explanations when they don't fit observed behavior: "You said X causes timeout, but logs show connection established. Explain this discrepancy with evidence." Reject guesses without citations: "Show me the file and line number where this occurs." -**Prerequisites:** [Code research capabilities](https://chunkhound.github.io/) (deep codebase exploration via multi-hop semantic search, query expansion, and iterative follow-ups), file system access for reading implementation and configuration, complete error messages/stack traces/logs (not truncated output), optionally: file paths or function names if known. Verify all cited file paths and line numbers—agents can hallucinate locations. Use engineering judgment to validate reasoning—LLMs complete patterns, not logic. [Adapt pattern for other diagnostics](/docs/practical-techniques/lesson-10-debugging#the-closed-loop-debugging-workflow): performance issues (metrics/thresholds/profiling data), security vulnerabilities (attack vectors/boundaries/configuration gaps), deployment failures (infrastructure logs/expected vs actual state), integration issues (API contracts/data flow/boundary errors). +**Prerequisites:** [Code research capabilities](https://chunkhound.github.io/) (deep codebase exploration via multi-hop semantic search, query expansion, and iterative follow-ups), file system access for reading implementation and configuration, complete error messages/stack traces/logs (not truncated output), optionally: file paths or function names if known. Verify all cited file paths and line numbers—agents can hallucinate locations. Use engineering judgment to validate reasoning—LLMs complete patterns, not logic. [Adapt pattern for other diagnostics](/practical-techniques/lesson-10-debugging#the-closed-loop-debugging-workflow): performance issues (metrics/thresholds/profiling data), security vulnerabilities (attack vectors/boundaries/configuration gaps), deployment failures (infrastructure logs/expected vs actual state), integration issues (API contracts/data flow/boundary errors). ### Related Lessons -- **[Lesson 4: Prompting 101](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path)** - Chain-of-Thought, constraints, structured reasoning -- **[Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - Grounding directives, RAG, forcing retrieval -- **[Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements, challenging agent logic -- **[Lesson 10: Debugging](/docs/practical-techniques/lesson-10-debugging#the-closed-loop-debugging-workflow)** - Closed-loop workflow, reproduction scripts, evidence-based approach +- **[Lesson 4: Prompting 101](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path)** - Chain-of-Thought, constraints, structured reasoning +- **[Lesson 5: Grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - Grounding directives, RAG, forcing retrieval +- **[Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements, challenging agent logic +- **[Lesson 10: Debugging](/practical-techniques/lesson-10-debugging#the-closed-loop-debugging-workflow)** - Closed-loop workflow, reproduction scripts, evidence-based approach diff --git a/website/prompts/index.md b/website/prompts/index.md index 44c8e40..d06b800 100644 --- a/website/prompts/index.md +++ b/website/prompts/index.md @@ -14,6 +14,7 @@ Production-ready prompt templates for common SDLC workflows. | **Onboarding** | [Generate AGENTS.md](/prompts/onboarding/generate-agents-md) | Bootstrap project context files automatically | | **Specifications** | [Generate System Spec](/prompts/specifications/generate-spec) | Before modifying complex systems or planning features | | **Specifications** | [Spec Template](/prompts/specifications/spec-template) | Writing specs from scratch for new features or design docs | +| **Specifications** | [Experience Spec Template](/prompts/specifications/experience-spec-template) | Writing frontend/UI specs for component architecture and interaction flows | | **Planning** | [Edge Case Discovery](/prompts/testing/edge-case-discovery) | Before implementing features or writing tests | | **Implementation** | [Evidence-Based Debug](/prompts/debugging/evidence-based-debug) | When debugging issues or unexpected behavior | | **Testing** | [Test Failure Diagnosis](/prompts/testing/test-failure-diagnosis) | When tests fail during development or CI/CD | diff --git a/website/prompts/onboarding/generate-agents-md.md b/website/prompts/onboarding/generate-agents-md.md index 29d511e..6117670 100644 --- a/website/prompts/onboarding/generate-agents-md.md +++ b/website/prompts/onboarding/generate-agents-md.md @@ -9,15 +9,15 @@ import GenerateAgentsMD from '@site/shared-prompts/\_generate-agents-md.mdx'; ### Overview -**Why multi-source grounding works:** [ChunkHound](/docs/methodology/lesson-5-grounding#code-grounding-choosing-tools-by-scale) provides codebase-specific context (patterns, conventions, architecture) while [ArguSeek](/docs/methodology/lesson-5-grounding#arguseek-isolated-context--state) provides current ecosystem knowledge (framework best practices, security guidelines)—this implements [multi-source grounding](/docs/methodology/lesson-5-grounding#production-pattern-multi-source-grounding) to combine empirical project reality with ecosystem best practices. The [structured output format](/docs/methodology/lesson-4-prompting-101#applying-structure-to-prompts) with explicit sections ensures comprehensive coverage by forcing systematic enumeration instead of free-form narrative. The ≤200 line [conciseness constraint](/docs/methodology/lesson-4-prompting-101#constraints-as-guardrails) forces prioritization—without it, agents generate verbose documentation that gets ignored during actual use. The non-duplication directive keeps focus on AI-specific operational details agents can't easily infer from code alone (environment setup, non-interactive command modifications, deployment gotchas). This implements the [Research phase](/docs/methodology/lesson-3-high-level-methodology#phase-1-research-grounding) of the [four-phase workflow](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow), letting agents build their own foundation before tackling implementation tasks. +**Why multi-source grounding works:** [ChunkHound](/methodology/lesson-5-grounding#code-grounding-choosing-tools-by-scale) provides codebase-specific context (patterns, conventions, architecture) while [ArguSeek](/methodology/lesson-5-grounding#arguseek-isolated-context--state) provides current ecosystem knowledge (framework best practices, security guidelines)—this implements [multi-source grounding](/methodology/lesson-5-grounding#production-pattern-multi-source-grounding) to combine empirical project reality with ecosystem best practices. The [structured output format](/methodology/lesson-4-prompting-101#applying-structure-to-prompts) with explicit sections ensures comprehensive coverage by forcing systematic enumeration instead of free-form narrative. The ≤200 line [conciseness constraint](/methodology/lesson-4-prompting-101#constraints-as-guardrails) forces prioritization—without it, agents generate verbose documentation that gets ignored during actual use. The non-duplication directive keeps focus on AI-specific operational details agents can't easily infer from code alone (environment setup, non-interactive command modifications, deployment gotchas). This implements the [Research phase](/methodology/lesson-3-high-level-methodology#phase-1-research-grounding) of the [four-phase workflow](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow), letting agents build their own foundation before tackling implementation tasks. -**When to use this pattern:** New project onboarding (establish baseline context before first implementation task), documenting legacy projects (capture tribal knowledge systematically), refreshing context after architectural changes (re-run after migrations, framework upgrades, major refactors). Run early in project adoption to establish baseline [context files](/docs/practical-techniques/lesson-6-project-onboarding#the-context-file-ecosystem), re-run after major changes, then manually add tribal knowledge (production incidents, team conventions, non-obvious gotchas) that AI can't discover from code. Without initial context grounding, agents hallucinate conventions based on training patterns instead of reading your actual codebase—this manifests as style violations, incorrect assumptions about architecture, and ignored project-specific constraints. +**When to use this pattern:** New project onboarding (establish baseline context before first implementation task), documenting legacy projects (capture tribal knowledge systematically), refreshing context after architectural changes (re-run after migrations, framework upgrades, major refactors). Run early in project adoption to establish baseline [context files](/practical-techniques/lesson-6-project-onboarding#the-context-file-ecosystem), re-run after major changes, then manually add tribal knowledge (production incidents, team conventions, non-obvious gotchas) that AI can't discover from code. Without initial context grounding, agents hallucinate conventions based on training patterns instead of reading your actual codebase—this manifests as style violations, incorrect assumptions about architecture, and ignored project-specific constraints. -**Prerequisites:** [ChunkHound code research](https://chunkhound.github.io/) (deep codebase exploration via multi-hop semantic search, query expansion, and iterative follow-ups), [ArguSeek web research](https://github.com/ArguSeek/arguseek) (ecosystem documentation and current best practices), write access to project root. Requires existing codebase with source files and README/basic documentation to avoid duplication. After generation, [validate by testing with a typical task](/docs/methodology/lesson-3-high-level-methodology#phase-4-validate-the-iteration-decision)—if the agent doesn't follow documented conventions, the context file needs iteration. Without validation, you risk cementing incorrect assumptions into project context that compound across future tasks. +**Prerequisites:** [ChunkHound code research](https://chunkhound.github.io/) (deep codebase exploration via multi-hop semantic search, query expansion, and iterative follow-ups), [ArguSeek web research](https://github.com/ArguSeek/arguseek) (ecosystem documentation and current best practices), write access to project root. Requires existing codebase with source files and README/basic documentation to avoid duplication. After generation, [validate by testing with a typical task](/methodology/lesson-3-high-level-methodology#phase-4-validate-the-iteration-decision)—if the agent doesn't follow documented conventions, the context file needs iteration. Without validation, you risk cementing incorrect assumptions into project context that compound across future tasks. ### Related Lessons -- **[Lesson 3: High-Level Methodology](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate), iteration decisions -- **[Lesson 4: Prompting 101](/docs/methodology/lesson-4-prompting-101#applying-structure-to-prompts)** - Structured prompting, constraints as guardrails, information density -- **[Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#production-pattern-multi-source-grounding)** - Multi-source grounding (ChunkHound + ArguSeek), semantic search, sub-agents -- **[Lesson 6: Project Onboarding](/docs/practical-techniques/lesson-6-project-onboarding#the-context-file-ecosystem)** - Context files (AGENTS.md, CLAUDE.md), hierarchical context, tribal knowledge +- **[Lesson 3: High-Level Methodology](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate), iteration decisions +- **[Lesson 4: Prompting 101](/methodology/lesson-4-prompting-101#applying-structure-to-prompts)** - Structured prompting, constraints as guardrails, information density +- **[Lesson 5: Grounding](/methodology/lesson-5-grounding#production-pattern-multi-source-grounding)** - Multi-source grounding (ChunkHound + ArguSeek), semantic search, sub-agents +- **[Lesson 6: Project Onboarding](/practical-techniques/lesson-6-project-onboarding#the-context-file-ecosystem)** - Context files (AGENTS.md, CLAUDE.md), hierarchical context, tribal knowledge diff --git a/website/prompts/pull-requests/ai-assisted-review.md b/website/prompts/pull-requests/ai-assisted-review.md index ec4d70b..8bf4498 100644 --- a/website/prompts/pull-requests/ai-assisted-review.md +++ b/website/prompts/pull-requests/ai-assisted-review.md @@ -9,16 +9,16 @@ import AIAssistedReview from '@site/shared-prompts/\_ai-assisted-review.mdx'; ### Overview -**Why two-step workflow with human validation:** Step 1 generates structured analysis using [Chain of Draft (CoD)](/docs/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work) reasoning—5 words max per thinking step, reducing token consumption 60-80% while preserving reasoning quality. GitHub CLI integration (`gh pr view --comments`) provides [multi-source grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) beyond code diff: PR metadata, discussion threads, linked issues, author intent. The [persona directive](/docs/methodology/lesson-4-prompting-101#assigning-personas) ("maintainer") biases toward critical analysis rather than generic approval. Evidence requirement ("never speculate...investigate files") forces code research before claims, preventing hallucinated issues. **Between steps, you validate findings**—LLMs can be confidently wrong about architectural violations. Cross-check file:line references, challenge vague criticisms. Step 2 then transforms validated analysis into dual-audience output: HUMAN_REVIEW.md (concise, actionable) and AGENT_REVIEW.md (efficient technical context). This mirrors the [PR Description Generator](/prompts/pull-requests/dual-optimized-pr) pattern—same context continuation, not fresh analysis. +**Why two-step workflow with human validation:** Step 1 generates structured analysis using [Chain of Draft (CoD)](/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work) reasoning—5 words max per thinking step, reducing token consumption 60-80% while preserving reasoning quality. GitHub CLI integration (`gh pr view --comments`) provides [multi-source grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) beyond code diff: PR metadata, discussion threads, linked issues, author intent. The [persona directive](/methodology/lesson-4-prompting-101#assigning-personas) ("maintainer") biases toward critical analysis rather than generic approval. Evidence requirement ("never speculate...investigate files") forces code research before claims, preventing hallucinated issues. **Between steps, you validate findings**—LLMs can be confidently wrong about architectural violations. Cross-check file:line references, challenge vague criticisms. Step 2 then transforms validated analysis into dual-audience output: HUMAN_REVIEW.md (concise, actionable) and AGENT_REVIEW.md (efficient technical context). This mirrors the [PR Description Generator](/prompts/pull-requests/dual-optimized-pr) pattern—same context continuation, not fresh analysis. -**When to use—primary use cases:** Systematic PR review for architectural changes touching core modules, introducing new patterns, or significant refactoring. Most effective with AI-optimized description from [PR Description Generator](/prompts/pull-requests/dual-optimized-pr) (explicit file paths, module boundaries, breaking changes). Best used pre-merge as final validation in [Validate phase](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow), not during active development (use [Comprehensive Review](/prompts/code-review/comprehensive-review) for worktree changes). The dual output files enable: HUMAN_REVIEW.md for maintainer discussion (1-3 paragraphs, praise + actionable focus), AGENT_REVIEW.md for downstream AI tools that may process the review. Less effective for trivial PRs (documentation-only, dependency updates, simple bug fixes) where review overhead exceeds value. +**When to use—primary use cases:** Systematic PR review for architectural changes touching core modules, introducing new patterns, or significant refactoring. Most effective with AI-optimized description from [PR Description Generator](/prompts/pull-requests/dual-optimized-pr) (explicit file paths, module boundaries, breaking changes). Best used pre-merge as final validation in [Validate phase](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow), not during active development (use [Comprehensive Review](/prompts/code-review/comprehensive-review) for worktree changes). The dual output files enable: HUMAN_REVIEW.md for maintainer discussion (1-3 paragraphs, praise + actionable focus), AGENT_REVIEW.md for downstream AI tools that may process the review. Less effective for trivial PRs (documentation-only, dependency updates, simple bug fixes) where review overhead exceeds value. -**Prerequisites:** [GitHub CLI](https://cli.github.com/) (`gh`) installed and authenticated (`gh auth status`), [ChunkHound](https://chunkhound.github.io/) for codebase semantic search, [ArguSeek](https://github.com/ofrivera/ArguSeek) for learning human/LLM optimization patterns in Step 2. Requires PR link (URL or number), AI-optimized description from [PR Description Generator](/prompts/pull-requests/dual-optimized-pr) workflow. Outputs: Step 1 produces structured verdict (Summary/Strengths/Issues/Decision), Step 2 produces HUMAN_REVIEW.md and AGENT_REVIEW.md files. [Adapt Critical Checks for specialized focus](/docs/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work): security (input validation, auth checks, credential handling, injection vectors), performance (complexity, N+1 queries, memory patterns, caching), accessibility (semantic HTML, keyboard nav, ARIA, contrast). +**Prerequisites:** [GitHub CLI](https://cli.github.com/) (`gh`) installed and authenticated (`gh auth status`), [ChunkHound](https://chunkhound.github.io/) for codebase semantic search, [ArguSeek](https://github.com/ofrivera/ArguSeek) for learning human/LLM optimization patterns in Step 2. Requires PR link (URL or number), AI-optimized description from [PR Description Generator](/prompts/pull-requests/dual-optimized-pr) workflow. Outputs: Step 1 produces structured verdict (Summary/Strengths/Issues/Decision), Step 2 produces HUMAN_REVIEW.md and AGENT_REVIEW.md files. [Adapt Critical Checks for specialized focus](/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work): security (input validation, auth checks, credential handling, injection vectors), performance (complexity, N+1 queries, memory patterns, caching), accessibility (semantic HTML, keyboard nav, ARIA, contrast). ### Related Lessons -- **[Lesson 3: High-Level Methodology](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate)—PR review is Validate phase -- **[Lesson 4: Prompting 101](/docs/methodology/lesson-4-prompting-101#assigning-personas)** - [Persona directives](/docs/methodology/lesson-4-prompting-101#assigning-personas), [Chain-of-Thought](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), [constraints](/docs/methodology/lesson-4-prompting-101#constraints-as-guardrails), structured output -- **[Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - Multi-source grounding (GitHub CLI + code research + AI-optimized descriptions), explicit grounding directives -- **[Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements force file:line citations, challenging vague claims -- **[Lesson 9: Reviewing Code](/docs/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work)** - Chain of Draft (CoD) efficiency, dual-optimized PR workflow, specialized review adaptations +- **[Lesson 3: High-Level Methodology](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate)—PR review is Validate phase +- **[Lesson 4: Prompting 101](/methodology/lesson-4-prompting-101#assigning-personas)** - [Persona directives](/methodology/lesson-4-prompting-101#assigning-personas), [Chain-of-Thought](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), [constraints](/methodology/lesson-4-prompting-101#constraints-as-guardrails), structured output +- **[Lesson 5: Grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - Multi-source grounding (GitHub CLI + code research + AI-optimized descriptions), explicit grounding directives +- **[Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements force file:line citations, challenging vague claims +- **[Lesson 9: Reviewing Code](/practical-techniques/lesson-9-reviewing-code#mechanisms-at-work)** - Chain of Draft (CoD) efficiency, dual-optimized PR workflow, specialized review adaptations diff --git a/website/prompts/pull-requests/dual-optimized-pr.md b/website/prompts/pull-requests/dual-optimized-pr.md index 16cae72..2166432 100644 --- a/website/prompts/pull-requests/dual-optimized-pr.md +++ b/website/prompts/pull-requests/dual-optimized-pr.md @@ -9,11 +9,11 @@ import DualOptimizedPR from '@site/shared-prompts/\_dual-optimized-pr.mdx'; ### Overview -**Why dual-audience optimization works:** [Sub-agents](/docs/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) conserve context by spawning separate agents to explore git history—without this delegation, 20-30 file changes consume 40K+ tokens, pushing critical constraints into the [attention curve's ignored middle](/docs/methodology/lesson-5-grounding#the-scale-problem-context-window-limits). [Multi-source grounding](/docs/methodology/lesson-5-grounding#production-pattern-multi-source-grounding) combines ArguSeek (PR best practices from GitHub docs and engineering blogs) with ChunkHound (project-specific architecture patterns, module responsibilities), preventing generic advice divorced from your codebase realities. The "co-worker" [persona framing](/docs/methodology/lesson-4-prompting-101#assigning-personas) with explicit style [constraints](/docs/methodology/lesson-4-prompting-101#constraints-as-guardrails) (direct, concise, assume competence, skip obvious) prevents verbose explanations that waste reviewer attention. Dual constraints balance audiences: "1-3 paragraphs max" for humans prevents overwhelming maintainers with walls of text, while "explain efficiently" keeps AI context comprehensive but structured—critical because [AI reviewers](/prompts/pull-requests/ai-assisted-review) need architectural context (file relationships, module boundaries) that humans infer from experience. +**Why dual-audience optimization works:** [Sub-agents](/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) conserve context by spawning separate agents to explore git history—without this delegation, 20-30 file changes consume 40K+ tokens, pushing critical constraints into the [attention curve's ignored middle](/methodology/lesson-5-grounding#the-scale-problem-context-window-limits). [Multi-source grounding](/methodology/lesson-5-grounding#production-pattern-multi-source-grounding) combines ArguSeek (PR best practices from GitHub docs and engineering blogs) with ChunkHound (project-specific architecture patterns, module responsibilities), preventing generic advice divorced from your codebase realities. The "co-worker" [persona framing](/methodology/lesson-4-prompting-101#assigning-personas) with explicit style [constraints](/methodology/lesson-4-prompting-101#constraints-as-guardrails) (direct, concise, assume competence, skip obvious) prevents verbose explanations that waste reviewer attention. Dual constraints balance audiences: "1-3 paragraphs max" for humans prevents overwhelming maintainers with walls of text, while "explain efficiently" keeps AI context comprehensive but structured—critical because [AI reviewers](/prompts/pull-requests/ai-assisted-review) need architectural context (file relationships, module boundaries) that humans infer from experience. -**When to use—workflow integration:** Before submitting PRs with complex changesets (10+ files, multiple modules touched, cross-cutting concerns) or cross-team collaboration where reviewers lack deep module familiarity. Integrate into [four-phase workflow](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow): complete implementation → validate with tests → self-review for issues → fix discovered problems → generate dual descriptions → submit PR with both files. Be specific with `$CHANGES_DESC`—vague descriptions ("fix bugs", "update logic") produce generic output because [grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) requires concrete intent. Without specific change description, agent has no anchor to evaluate "what matters" in the git diff. Critical: if you manually edit generated descriptions post-generation, regenerate BOTH files—stale context in AI-optimized description causes [hallucinations during review](/docs/practical-techniques/lesson-9-reviewing-code) when architectural explanations contradict actual changes. For teams without AI reviewers yet, human-optimized output alone provides concise summaries that respect reviewer time. +**When to use—workflow integration:** Before submitting PRs with complex changesets (10+ files, multiple modules touched, cross-cutting concerns) or cross-team collaboration where reviewers lack deep module familiarity. Integrate into [four-phase workflow](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow): complete implementation → validate with tests → self-review for issues → fix discovered problems → generate dual descriptions → submit PR with both files. Be specific with `$CHANGES_DESC`—vague descriptions ("fix bugs", "update logic") produce generic output because [grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) requires concrete intent. Without specific change description, agent has no anchor to evaluate "what matters" in the git diff. Critical: if you manually edit generated descriptions post-generation, regenerate BOTH files—stale context in AI-optimized description causes [hallucinations during review](/practical-techniques/lesson-9-reviewing-code) when architectural explanations contradict actual changes. For teams without AI reviewers yet, human-optimized output alone provides concise summaries that respect reviewer time. -**Prerequisites:** [Sub-agent/task tool](/docs/methodology/lesson-5-grounding#solution-2-sub-agents-for-context-isolation) ([Claude Code CLI](/developer-tools/cli-coding-agents#claude-code) provides built-in Task tool—other platforms require manual context management via sequential prompts), [ArguSeek](https://github.com/ofrivera/ArguSeek) (web research for PR best practices), [ChunkHound](https://chunkhound.github.io/) (codebase research via multi-hop semantic search and iterative exploration), git history access with committed changes on feature branch. Requires base branch for comparison (typically `main` or `develop`), architecture documentation ([CLAUDE.md project context](/docs/practical-techniques/lesson-6-project-onboarding#the-context-file-ecosystem), AGENTS.md for agentic workflows). Agent generates two markdown files: **human-optimized** (1-3 paragraphs covering what changed, why it matters, breaking changes if any, value delivered) and **AI-optimized** (file paths with line numbers, module responsibilities, architectural patterns followed, boundary changes, testing coverage, edge cases addressed). [Adapt this pattern](/docs/practical-techniques/lesson-7-planning-execution) for other documentation needs: release notes (user-facing features vs technical changelog), incident postmortems (executive summary vs technical root cause analysis), design docs (stakeholder overview vs implementation deep-dive). +**Prerequisites:** [Sub-agent/task tool](/methodology/lesson-5-grounding#solution-2-sub-agents-for-context-isolation) ([Claude Code CLI](/developer-tools/cli-coding-agents#claude-code) provides built-in Task tool—other platforms require manual context management via sequential prompts), [ArguSeek](https://github.com/ofrivera/ArguSeek) (web research for PR best practices), [ChunkHound](https://chunkhound.github.io/) (codebase research via multi-hop semantic search and iterative exploration), git history access with committed changes on feature branch. Requires base branch for comparison (typically `main` or `develop`), architecture documentation ([CLAUDE.md project context](/practical-techniques/lesson-6-project-onboarding#the-context-file-ecosystem), AGENTS.md for agentic workflows). Agent generates two markdown files: **human-optimized** (1-3 paragraphs covering what changed, why it matters, breaking changes if any, value delivered) and **AI-optimized** (file paths with line numbers, module responsibilities, architectural patterns followed, boundary changes, testing coverage, edge cases addressed). [Adapt this pattern](/practical-techniques/lesson-7-planning-execution) for other documentation needs: release notes (user-facing features vs technical changelog), incident postmortems (executive summary vs technical root cause analysis), design docs (stakeholder overview vs implementation deep-dive). **For actual review**, use these prompts with the generated artifacts: @@ -22,7 +22,7 @@ import DualOptimizedPR from '@site/shared-prompts/\_dual-optimized-pr.mdx'; ### Related Lessons -- **[Lesson 2: Agents Demystified](/docs/fundamentals/lesson-2-how-agents-work#the-stateless-advantage)** - Sub-agents, task delegation, context conservation -- **[Lesson 4: Prompting 101](/docs/methodology/lesson-4-prompting-101#assigning-personas)** - Persona, constraints, attention curves -- **[Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#production-pattern-multi-source-grounding)** - Multi-source grounding, preventing hallucination -- **[Lesson 9: Reviewing Code](/docs/practical-techniques/lesson-9-reviewing-code)** - Dual-audience optimization, PR workflows, AI-assisted review +- **[Lesson 2: Agents Demystified](/fundamentals/lesson-2-how-agents-work#the-stateless-advantage)** - Sub-agents, task delegation, context conservation +- **[Lesson 4: Prompting 101](/methodology/lesson-4-prompting-101#assigning-personas)** - Persona, constraints, attention curves +- **[Lesson 5: Grounding](/methodology/lesson-5-grounding#production-pattern-multi-source-grounding)** - Multi-source grounding, preventing hallucination +- **[Lesson 9: Reviewing Code](/practical-techniques/lesson-9-reviewing-code)** - Dual-audience optimization, PR workflows, AI-assisted review diff --git a/website/prompts/specifications/experience-spec-template.md b/website/prompts/specifications/experience-spec-template.md new file mode 100644 index 0000000..6355624 --- /dev/null +++ b/website/prompts/specifications/experience-spec-template.md @@ -0,0 +1,35 @@ +--- +title: Experience Specification Template +sidebar_position: 3 +--- + +import ExperienceSpecTemplate from '@site/shared-prompts/_experience-spec-template.mdx'; + +## Experience Specification Template + +A structured template for writing experience (frontend/UI) specifications. Use this when +designing user-facing features, planning component architecture, or documenting interaction +flows for AI agent implementation. + +For **system/backend** specs, use the +[System Spec Template](/prompts/specifications/spec-template) instead. + +### The Template + + + +### How to Use + +1. Start with **Tier 1** (Components + Flows + State) — the minimum viable spec. Add sections from + Tier 2–4 as the code pulls depth from you + (see [Iterate, Then Delete](/experience-engineering/lesson-17-verification#iterate-then-delete)) +2. Fill in with your domain specifics — skip sections that don't apply +3. Use browser automation to verify implementation matches spec +4. Delete after implementation — code + mock contracts + accessibility tree are the source of truth + ([Lesson 12](/practical-techniques/lesson-12-spec-driven-development)) + +### Related + +- [Experience Engineering](/experience-engineering/lesson-14-design-tokens) — teaches the thinking behind UI spec-driven development with AI agents (Lessons 14–17) +- [Lesson 13: Thinking in Systems](/practical-techniques/lesson-13-systems-thinking-specs) — the system spec counterpart +- [System Spec Template](/prompts/specifications/spec-template) — for backend/system specifications diff --git a/website/prompts/specifications/generate-spec.md b/website/prompts/specifications/generate-spec.md index 448a280..c5c480b 100644 --- a/website/prompts/specifications/generate-spec.md +++ b/website/prompts/specifications/generate-spec.md @@ -9,14 +9,14 @@ import GenerateSpec from '@site/shared-prompts/_generate-spec.mdx'; ### Overview -**Why systematic extraction works:** Code research tools ([ChunkHound](/docs/methodology/lesson-5-grounding#code-grounding-choosing-tools-by-scale)) extract architectural knowledge from source code dynamically. The spec template transforms this raw information into structured thinking—modules, boundaries, contracts, state, failures—that surfaces constraints before implementation begins. This implements [multi-source grounding](/docs/methodology/lesson-5-grounding#production-pattern-multi-source-grounding) for architectural understanding: code research provides the facts, the template provides the structure for reasoning about them. +**Why systematic extraction works:** Code research tools ([ChunkHound](/methodology/lesson-5-grounding#code-grounding-choosing-tools-by-scale)) extract architectural knowledge from source code dynamically. The spec template transforms this raw information into structured thinking—modules, boundaries, contracts, state, failures—that surfaces constraints before implementation begins. This implements [multi-source grounding](/methodology/lesson-5-grounding#production-pattern-multi-source-grounding) for architectural understanding: code research provides the facts, the template provides the structure for reasoning about them. **When to use this pattern:** Brownfield modifications (understand before changing), architectural investigation (how does this system work?), pre-planning for complex features (decompose before prompting), onboarding to unfamiliar codebases (extract the mental model). Most valuable when changes span multiple modules or touch unfamiliar code. Skip for isolated bug fixes where the context window serves as sufficient spec. -**Prerequisites:** [ChunkHound code research](https://chunkhound.github.io/) for codebase exploration, existing code to analyze. For new systems (greenfield), use the template directly as a design tool rather than extraction target. After extraction, validate claims with file:line evidence—agents hallucinate structure that doesn't exist. Delete specs after implementation; the code becomes the source of truth ([Lesson 12](/docs/practical-techniques/lesson-12-spec-driven-development)). +**Prerequisites:** [ChunkHound code research](https://chunkhound.github.io/) for codebase exploration, existing code to analyze. For new systems (greenfield), use the template directly as a design tool rather than extraction target. After extraction, validate claims with file:line evidence—agents hallucinate structure that doesn't exist. Delete specs after implementation; the code becomes the source of truth ([Lesson 12](/practical-techniques/lesson-12-spec-driven-development)). ### Related Lessons -- **[Lesson 5: Grounding](/docs/methodology/lesson-5-grounding)** - Code research tools, semantic search, evidence requirements -- **[Lesson 12: Spec Driven Development](/docs/practical-techniques/lesson-12-spec-driven-development)** - Spec lifecycle, code as source of truth, delete after implementation -- **[Lesson 13: Systems Thinking for Specs](/docs/practical-techniques/lesson-13-systems-thinking-specs)** - Using the template as a thinking skeleton, matching formality to complexity +- **[Lesson 5: Grounding](/methodology/lesson-5-grounding)** - Code research tools, semantic search, evidence requirements +- **[Lesson 12: Spec Driven Development](/practical-techniques/lesson-12-spec-driven-development)** - Spec lifecycle, code as source of truth, delete after implementation +- **[Lesson 13: Systems Thinking for Specs](/practical-techniques/lesson-13-systems-thinking-specs)** - Using the template as a thinking skeleton, matching formality to complexity diff --git a/website/prompts/specifications/spec-template.md b/website/prompts/specifications/spec-template.md index 2e0a35c..3288935 100644 --- a/website/prompts/specifications/spec-template.md +++ b/website/prompts/specifications/spec-template.md @@ -21,14 +21,14 @@ For **extracting** specs from an existing codebase automatically, use the ### How to Use 1. Start with Architecture + Interfaces + State, then add sections as the code pulls them - (see [Converge, Don't Count Passes](/docs/practical-techniques/lesson-13-systems-thinking-specs#converge-dont-count-passes)) + (see [Converge, Don't Count Passes](/practical-techniques/lesson-13-systems-thinking-specs#converge-dont-count-passes)) 2. Fill in with your domain specifics — skip sections that don't apply 3. Use as context for agent implementation or as a design document for team review 4. Delete after implementation — code is the source of truth - ([Lesson 12](/docs/practical-techniques/lesson-12-spec-driven-development)) + ([Lesson 12](/practical-techniques/lesson-12-spec-driven-development)) ### Related -- [Lesson 13: Thinking in Systems](/docs/practical-techniques/lesson-13-systems-thinking-specs) — explains the reasoning behind each section -- [Lesson 12: Spec-Driven Development](/docs/practical-techniques/lesson-12-spec-driven-development) — the spec lifecycle +- [Lesson 13: Thinking in Systems](/practical-techniques/lesson-13-systems-thinking-specs) — explains the reasoning behind each section +- [Lesson 12: Spec-Driven Development](/practical-techniques/lesson-12-spec-driven-development) — the spec lifecycle - [Generate System Spec](/prompts/specifications/generate-spec) — auto-extract specs from code diff --git a/website/prompts/testing/edge-case-discovery.md b/website/prompts/testing/edge-case-discovery.md index 5a48c74..8987973 100644 --- a/website/prompts/testing/edge-case-discovery.md +++ b/website/prompts/testing/edge-case-discovery.md @@ -9,15 +9,15 @@ import EdgeCaseDiscovery from '@site/shared-prompts/\_edge-case-discovery.mdx'; ### Overview -**Why two-step pattern prevents generic advice:** Step 1 loads concrete constraints—agent searches for function, reads implementation, finds existing tests. This populates context with actual edge cases from your codebase ("OAuth users skip email verification," "admin users bypass rate limits"). Step 2 identifies gaps—with implementation in context, agent analyzes what's NOT tested rather than listing generic test categories. [Grounding directives](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) force codebase search before suggesting tests. Existing tests reveal coverage patterns and domain-specific edge cases. Implementation details expose actual failure modes, not hypothetical ones. Prevents [specification gaming](/docs/practical-techniques/lesson-8-tests-as-guardrails#the-three-context-workflow) by discovering edge cases separately from implementation—similar to [fresh context requirement](/docs/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) for objective analysis. +**Why two-step pattern prevents generic advice:** Step 1 loads concrete constraints—agent searches for function, reads implementation, finds existing tests. This populates context with actual edge cases from your codebase ("OAuth users skip email verification," "admin users bypass rate limits"). Step 2 identifies gaps—with implementation in context, agent analyzes what's NOT tested rather than listing generic test categories. [Grounding directives](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality) force codebase search before suggesting tests. Existing tests reveal coverage patterns and domain-specific edge cases. Implementation details expose actual failure modes, not hypothetical ones. Prevents [specification gaming](/practical-techniques/lesson-8-tests-as-guardrails#the-three-context-workflow) by discovering edge cases separately from implementation—similar to [fresh context requirement](/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) for objective analysis. -**When to use—research-first requirement:** Before implementing new features (discover existing patterns), test-driven development (identify edge cases before implementation), increasing coverage (find gaps in existing suites), refactoring legacy code (understand implicit edge case handling), code review (validate PRs include relevant tests). Critical: Don't skip Step 1—asking directly "what edge cases should I test?" produces generic advice without codebase grounding. Be specific in Step 2 with domain-relevant categories (see examples in prompt). If you generate edge cases and implementation in same conversation, tests will match implementation assumptions—use this pattern in [fresh context](/docs/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) or before implementation. Without [grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality), agents hallucinate based on training patterns instead of analyzing your actual code. +**When to use—research-first requirement:** Before implementing new features (discover existing patterns), test-driven development (identify edge cases before implementation), increasing coverage (find gaps in existing suites), refactoring legacy code (understand implicit edge case handling), code review (validate PRs include relevant tests). Critical: Don't skip Step 1—asking directly "what edge cases should I test?" produces generic advice without codebase grounding. Be specific in Step 2 with domain-relevant categories (see examples in prompt). If you generate edge cases and implementation in same conversation, tests will match implementation assumptions—use this pattern in [fresh context](/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) or before implementation. Without [grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality), agents hallucinate based on training patterns instead of analyzing your actual code. **Prerequisites:** [Code research capabilities](https://chunkhound.github.io/) (deep codebase exploration via multi-hop semantic search, query expansion, and iterative follow-ups), access to implementation files and existing test suites, function/module name to analyze. After Step 1, agent provides implementation summary with file paths, currently tested edge cases with evidence from test files, special handling logic and conditional branches. After Step 2, agent identifies untested code paths with line numbers, missing edge case coverage with concrete examples from your domain, potential failure modes based on implementation analysis. ### Related Lessons -- **[Lesson 4: Prompting 101](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path)** - Chain-of-Thought, structured prompts with sequential steps -- **[Lesson 5: Grounding](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - Loading codebase context before generation, preventing hallucination -- **[Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Research-first methodology, evidence requirements -- **[Lesson 8: Tests as Guardrails](/docs/practical-techniques/lesson-8-tests-as-guardrails#the-three-context-workflow)** - Three-context workflow, preventing specification gaming +- **[Lesson 4: Prompting 101](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path)** - Chain-of-Thought, structured prompts with sequential steps +- **[Lesson 5: Grounding](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)** - Loading codebase context before generation, preventing hallucination +- **[Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Research-first methodology, evidence requirements +- **[Lesson 8: Tests as Guardrails](/practical-techniques/lesson-8-tests-as-guardrails#the-three-context-workflow)** - Three-context workflow, preventing specification gaming diff --git a/website/prompts/testing/test-failure-diagnosis.md b/website/prompts/testing/test-failure-diagnosis.md index 86efe88..a006493 100644 --- a/website/prompts/testing/test-failure-diagnosis.md +++ b/website/prompts/testing/test-failure-diagnosis.md @@ -9,15 +9,15 @@ import TestFailureDiagnosis from '@site/shared-prompts/\_test-failure-diagnosis. ### Overview -**Why systematic diagnosis prevents hallucination:** Fenced code block preserves error formatting and prevents LLM from interpreting failure messages as instructions. "Use the code research" is explicit [grounding directive](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)—forces codebase search instead of hallucination from training patterns. DIAGNOSE numbered steps implement [Chain-of-Thought](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), forcing sequential analysis (can't jump to "root cause" without examining test intent first). "Understand the intention" (step 2) ensures agent articulates WHY the test exists, not just WHAT it does—critical for distinguishing bugs from outdated requirements. DETERMINE binary decision [constrains output](/docs/methodology/lesson-4-prompting-101#constraints-as-guardrails) to "bug vs outdated test" instead of open-ended conclusions. "Provide evidence" requires file paths and line numbers—concrete proof, not vague assertions. +**Why systematic diagnosis prevents hallucination:** Fenced code block preserves error formatting and prevents LLM from interpreting failure messages as instructions. "Use the code research" is explicit [grounding directive](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality)—forces codebase search instead of hallucination from training patterns. DIAGNOSE numbered steps implement [Chain-of-Thought](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path), forcing sequential analysis (can't jump to "root cause" without examining test intent first). "Understand the intention" (step 2) ensures agent articulates WHY the test exists, not just WHAT it does—critical for distinguishing bugs from outdated requirements. DETERMINE binary decision [constrains output](/methodology/lesson-4-prompting-101#constraints-as-guardrails) to "bug vs outdated test" instead of open-ended conclusions. "Provide evidence" requires file paths and line numbers—concrete proof, not vague assertions. -**When to use—fresh context requirement:** Test failures during refactoring (determine if tests need updating or code has bugs), CI/CD pipeline failures (systematic root cause analysis), after implementing new features (analyze failures in existing suites). Critical: use in separate conversation from implementation for unbiased analysis. This diagnostic pattern prevents "cycle of self-deception" where agents defend their own implementation. Running in [fresh context](/docs/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) provides objective analysis without prior assumptions. Include full stack traces and error messages—truncated output prevents accurate diagnosis. Without [grounding directive](/docs/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality), agents hallucinate based on training patterns instead of reading your actual codebase. +**When to use—fresh context requirement:** Test failures during refactoring (determine if tests need updating or code has bugs), CI/CD pipeline failures (systematic root cause analysis), after implementing new features (analyze failures in existing suites). Critical: use in separate conversation from implementation for unbiased analysis. This diagnostic pattern prevents "cycle of self-deception" where agents defend their own implementation. Running in [fresh context](/fundamentals/lesson-2-how-agents-work#the-stateless-advantage) provides objective analysis without prior assumptions. Include full stack traces and error messages—truncated output prevents accurate diagnosis. Without [grounding directive](/methodology/lesson-5-grounding#grounding-anchoring-agents-in-reality), agents hallucinate based on training patterns instead of reading your actual codebase. -**Prerequisites:** [Code research capabilities](https://chunkhound.github.io/) (deep codebase exploration via multi-hop semantic search, query expansion, and iterative follow-ups), access to both test files and implementation code, test failure output (stack traces, assertion errors, logs), file paths to relevant files. [Adapt pattern for other diagnostics](/docs/practical-techniques/lesson-10-debugging#the-closed-loop-debugging-workflow): performance issues (metrics/thresholds/bottlenecks), security vulnerabilities (attack vectors/boundaries/gaps), deployment failures (logs/expected flow/configuration mismatches). +**Prerequisites:** [Code research capabilities](https://chunkhound.github.io/) (deep codebase exploration via multi-hop semantic search, query expansion, and iterative follow-ups), access to both test files and implementation code, test failure output (stack traces, assertion errors, logs), file paths to relevant files. [Adapt pattern for other diagnostics](/practical-techniques/lesson-10-debugging#the-closed-loop-debugging-workflow): performance issues (metrics/thresholds/bottlenecks), security vulnerabilities (attack vectors/boundaries/gaps), deployment failures (logs/expected flow/configuration mismatches). ### Related Lessons -- **[Lesson 3: High-Level Methodology](/docs/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate) -- **[Lesson 4: Prompting 101](/docs/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path)** - Chain-of-Thought, constraints, structured format -- **[Lesson 7: Planning & Execution](/docs/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements, grounding techniques -- **[Lesson 8: Tests as Guardrails](/docs/practical-techniques/lesson-8-tests-as-guardrails#test-failure-diagnosis-agent-driven-debug-cycle)** - Three-context workflow, test failure diagnosis +- **[Lesson 3: High-Level Methodology](/methodology/lesson-3-high-level-methodology#the-four-phase-workflow)** - Four-phase workflow (Research > Plan > Execute > Validate) +- **[Lesson 4: Prompting 101](/methodology/lesson-4-prompting-101#chain-of-thought-paving-a-clear-path)** - Chain-of-Thought, constraints, structured format +- **[Lesson 7: Planning & Execution](/practical-techniques/lesson-7-planning-execution#require-evidence-to-force-grounding)** - Evidence requirements, grounding techniques +- **[Lesson 8: Tests as Guardrails](/practical-techniques/lesson-8-tests-as-guardrails#test-failure-diagnosis-agent-driven-debug-cycle)** - Three-context workflow, test failure diagnosis diff --git a/website/shared-prompts/_experience-spec-template.mdx b/website/shared-prompts/_experience-spec-template.mdx new file mode 100644 index 0000000..c27f968 --- /dev/null +++ b/website/shared-prompts/_experience-spec-template.mdx @@ -0,0 +1,371 @@ +{/* Standalone experience specification template — pure structure, no agent instructions */} + +````markdown +# {Feature Name} + +> **Spec Version:** 1.0 | **Code:** `git:HEAD` | **Status:** draft + +## User Story & Metrics + +**Problem:** [1-2 sentences — what user pain does this solve?] + +**Impact:** [who affected, how many, severity] + +| Metric | Category | Current | Target | Failure | +|--------|----------|---------|--------|---------| +| [name] | efficiency / effectiveness / satisfaction / accessibility / internationalization | [baseline] | [goal] | [unacceptable] | + +--- + +## Components + +> Props are preconditions. Rendered output is postconditions. Accessibility attributes are non-negotiable. + +### Component Table + +| Component | Responsibility | Owns / Never Does | Interface | +|-----------|---------------|----------|----------| +| [name] | [single responsibility] | [what it never does] | Props: `[type]`, emits: `[callbacks]` | + +All text-bearing components accept translation keys, never hardcoded strings (L-001). + +### Interfaces + +| Parent | Child | Interface | +|--------|-------|-----------| +| [parent] | [child] | `[prop]: [Type]` — precondition: [what must be true] | +| [child] | [parent] | `[callback]` — postcondition: [when called] | +| Any component | Screen reader | `role`, `aria-label` — non-negotiable: always present on interactive elements (A-001) | + +### Responsibilities + +- **[component]** — NEVER imports from [other components] — [reason] +- **[component]** — NEVER calls APIs directly — receives data via props +- **Shared kernel:** `src/components/primitives/` — [what lives here] + +--- + +## Flows + +### [Flow Name] + +``` +1. User [action] on [Component] + → [immediate UI response] + → Focus moves to [target element] (A-006) + → On cancel: [cancel behavior], return focus to [trigger] (A-007) + +2. User [action] + → [validation / loading state] + → Call [API endpoint] + → On success: [success behavior], announce via aria-live="polite" (A-008) + → On error (4xx): [error behavior], announce via aria-live="assertive" (A-009) + → On network error: [network error behavior] +``` + +All user-visible strings use translation keys. Pluralization follows ICU MessageFormat (L-003). + +--- + +## State Model + +> Choose one per entity: Declarative, Event-Driven, or State Machine. Delete unused. + +### [Entity Name] + +#### Option A: Declarative (Desired State) + +- **Target state:** [what the view converges to] +- **Reconciliation:** [how drift is detected — e.g., React reconciliation] +- **Trigger:** [what causes re-render] + +#### Option B: Event-Driven + +| Event | Payload | Source | Handler | +|-------|---------|--------|---------| +| [name] | [schema] | [WebSocket / user action / timer] | [component/hook] | + +#### Option C: State Machine + +| State | Transitions To | Trigger | +|-------|----------------|---------| +| [state] | [next states] | [user action / API response] | + +- **Initial:** [start state] +- **Terminal:** [end states] + +### Five View States + +> Every data-bound view has exactly five states: happy, loading, error, empty, partial. Omitting any is a bug. + +| State | What User Sees | Screen Reader | Constraint | +|-------|---------------|---------------|------------| +| Happy | [rendered data] | Content via landmarks and labels | [design spec match] | +| Loading | [skeleton/spinner] | `aria-busy="true"` (A-002) | Within 100ms of trigger | +| Error | [error + recovery CTA] | `aria-live="assertive"` (A-003) | Never show raw error | +| Empty | [guidance to action] | Perceivable guidance (A-004) | Guide user to next step | +| Partial | [partial data + failed indicator] | Failed section announced (A-005) | Never block entire view | + +### View State Constraints + +| ID | Rule | Verified By | +|----|------|-------------| +| V-001 | NEVER [forbidden UI state] | Browser automation: [verification method] | + +--- + +## Design Token Assumptions + +> Architectural constants that constrain every component, layout, and flow. When a token assumption changes, its "Drives" column shows which spec elements to revisit. + +| Token Assumption | Source | Drives | +|------------------|--------|--------| +| [spacing/type/color/contrast constraint] | [design system / accessibility standard / brand guide] | V-[id], A-[id], [component], [layout] | + +### Primitive Tokens + +| Token | Value | Color Space | +|-------|-------|-------------| +| [name] | [value] | OKLCH / sRGB fallback | + +### Semantic Tokens + +| Token | Light | Dark | Purpose | +|-------|-------|------|---------| +| [name] | {primitive} | {primitive} | [intent] | + +--- + +## Layouts + +| Layout | Structure | Components | +|--------|-----------|------------| +| [name] | [grid/flex description] | [child components] | + +RTL layouts mirror the entire grid. Use logical properties for all spatial tokens (L-002). + +### Breakpoints + +| Breakpoint | Viewport | Layout Adaptation | Token Scale | +|------------|----------|-------------------|-------------| +| Mobile | < 768px | [adaptation] | [scale] | +| Tablet | 768-1024px | [adaptation] | [scale] | +| Desktop | > 1024px | [adaptation] | [scale] | + +Touch targets ≥ 44px on mobile (A-012). Tab order follows visual order per breakpoint (A-013). Zoom to 200% — no horizontal scroll (A-014). + +--- + +## Accessibility Architecture + +> Structural decisions affecting every component, flow, and state transition. Specify up front, not after implementation. + +### Semantic HTML + +| Need | Use | Not | +|------|-----|-----| +| [interaction type] | [native HTML element] | [ARIA workaround] | + +### Landmark Structure + +| Landmark | Element | Purpose | +|----------|---------|---------| +| `banner` | `
` | [purpose] | +| `navigation` | `