diff --git a/.gitignore b/.gitignore index 49d921109..d895f5001 100644 --- a/.gitignore +++ b/.gitignore @@ -58,13 +58,21 @@ extension/detector/ evals/ tests/evals-v2/ -# Generated sub-pages (built from source/skills + content/site at build time) +# Generated sub-pages (legacy, now replaced by Astro content collections) public/docs/ public/anti-patterns/ public/tutorials/ public/visual-mode/ public/slop/ +# Build artifacts written to public/ so Astro copies them to build/ +public/_data/ +public/_headers +public/_redirects +public/_routes.json +public/js/detect-antipatterns-browser.js +public/js/generated/ + # Note: harness skill directories (.claude/skills/, .cursor/skills/, etc.) # are intentionally tracked. npx skills reads them from this repo at install # time, and they enable clean submodule use. Run `bun run build` to refresh @@ -73,3 +81,4 @@ public/slop/ # Codex CLI consumes `.agents/skills/`; `.codex/` is not used. Ignore it so # local artifacts or old trees are never committed. .codex/ +.astro/ diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 000000000..59f958655 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,15 @@ +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + srcDir: './site', + output: 'static', + build: { + format: 'directory', + }, + outDir: './build', + vite: { + build: { + assetsInlineLimit: 0, + }, + }, +}); diff --git a/demos/landing-demo/DESIGN.json b/demos/landing-demo/DESIGN.json new file mode 100644 index 000000000..d7767547c --- /dev/null +++ b/demos/landing-demo/DESIGN.json @@ -0,0 +1,281 @@ +{ + "schemaVersion": 2, + "generatedAt": "2026-04-29T00:00:00Z", + "title": "Design System: Lumina", + "extensions": { + "colorMeta": { + "cream": { + "role": "neutral", + "displayName": "Cream", + "canonical": "oklch(96.5% 0.012 80)", + "tonalRamp": [ + "oklch(15% 0.012 80)", + "oklch(25% 0.012 80)", + "oklch(35% 0.012 80)", + "oklch(50% 0.012 80)", + "oklch(65% 0.012 80)", + "oklch(80% 0.012 80)", + "oklch(90% 0.012 80)", + "oklch(96.5% 0.012 80)" + ] + }, + "cream-warm": { + "role": "neutral", + "displayName": "Cream Warm", + "canonical": "oklch(92% 0.020 75)", + "tonalRamp": [ + "oklch(15% 0.020 75)", + "oklch(25% 0.020 75)", + "oklch(35% 0.020 75)", + "oklch(50% 0.020 75)", + "oklch(65% 0.020 75)", + "oklch(78% 0.020 75)", + "oklch(85% 0.020 75)", + "oklch(92% 0.020 75)" + ] + }, + "peach": { + "role": "neutral", + "displayName": "Peach", + "canonical": "oklch(91% 0.040 60)", + "tonalRamp": [ + "oklch(20% 0.040 60)", + "oklch(30% 0.060 60)", + "oklch(45% 0.080 60)", + "oklch(60% 0.080 60)", + "oklch(72% 0.060 60)", + "oklch(82% 0.050 60)", + "oklch(88% 0.045 60)", + "oklch(91% 0.040 60)" + ] + }, + "line": { + "role": "neutral", + "displayName": "Line (Hairline Border)", + "canonical": "oklch(89% 0.018 75)", + "tonalRamp": [ + "oklch(15% 0.018 75)", + "oklch(25% 0.018 75)", + "oklch(40% 0.018 75)", + "oklch(55% 0.018 75)", + "oklch(70% 0.018 75)", + "oklch(80% 0.018 75)", + "oklch(85% 0.018 75)", + "oklch(89% 0.018 75)" + ] + }, + "ink": { + "role": "neutral", + "displayName": "Ink", + "canonical": "oklch(15% 0.010 60)", + "tonalRamp": [ + "oklch(15% 0.010 60)", + "oklch(22% 0.010 60)", + "oklch(30% 0.010 60)", + "oklch(40% 0.010 60)", + "oklch(55% 0.010 60)", + "oklch(70% 0.010 60)", + "oklch(85% 0.010 60)", + "oklch(94% 0.010 60)" + ] + }, + "soft": { + "role": "neutral", + "displayName": "Soft", + "canonical": "oklch(40% 0.012 60)", + "tonalRamp": [ + "oklch(15% 0.012 60)", + "oklch(25% 0.012 60)", + "oklch(35% 0.012 60)", + "oklch(40% 0.012 60)", + "oklch(55% 0.012 60)", + "oklch(70% 0.012 60)", + "oklch(82% 0.012 60)", + "oklch(92% 0.012 60)" + ] + }, + "accent": { + "role": "primary", + "displayName": "Burnt Orange Accent", + "canonical": "oklch(60% 0.150 40)", + "tonalRamp": [ + "oklch(15% 0.060 40)", + "oklch(25% 0.090 40)", + "oklch(35% 0.120 40)", + "oklch(48% 0.150 40)", + "oklch(60% 0.150 40)", + "oklch(72% 0.130 40)", + "oklch(85% 0.080 40)", + "oklch(95% 0.040 40)" + ] + }, + "accent-deep": { + "role": "primary", + "displayName": "Accent Deep", + "canonical": "oklch(50% 0.150 38)", + "tonalRamp": [ + "oklch(15% 0.060 38)", + "oklch(25% 0.090 38)", + "oklch(35% 0.120 38)", + "oklch(50% 0.150 38)", + "oklch(62% 0.140 38)", + "oklch(75% 0.110 38)", + "oklch(86% 0.070 38)", + "oklch(95% 0.035 38)" + ] + } + }, + "typographyMeta": { + "display": { + "displayName": "Display", + "purpose": "Hero headlines only. Fraunces, weight 400, optical-sized for large display." + }, + "headline": { + "displayName": "Headline", + "purpose": "Section headlines (the features-head h2, cta-section h2)." + }, + "title": { + "displayName": "Title", + "purpose": "Card headings inside the feature grid." + }, + "lede": { + "displayName": "Lede", + "purpose": "The supporting paragraph that sits below a hero headline." + }, + "body": { + "displayName": "Body", + "purpose": "Default paragraph copy. Cap line length at 65–75ch." + }, + "label": { + "displayName": "Label", + "purpose": "The eyebrow chip and any small uppercase labels." + } + }, + "shadows": [], + "motion": [ + { + "name": "ease-button", + "value": "ease", + "duration": "150ms", + "purpose": "Default easing for button hover transforms." + }, + { + "name": "ease-card", + "value": "ease", + "duration": "300ms", + "purpose": "Card hover transition (currently unused but reserved)." + } + ], + "breakpoints": [ + { "name": "container", "value": "1180px" }, + { "name": "logo-strip", "value": "1100px" } + ] + }, + "components": [ + { + "name": "Primary Button", + "kind": "button", + "refersTo": "button-primary", + "description": "The default CTA. Ink background, cream text, fully rounded.", + "html": "Start free trial", + "css": ".ds-btn-primary { display: inline-flex; align-items: center; gap: 8px; padding: 14px 28px; border-radius: 999px; background: #1f1a15; color: #faf6ef; font-family: 'Inter', system-ui, sans-serif; font-weight: 500; font-size: 15px; text-decoration: none; transition: transform 150ms ease; } .ds-btn-primary:hover { transform: translateY(-1px); }" + }, + { + "name": "Ghost Button", + "kind": "button", + "refersTo": "button-ghost", + "description": "Secondary CTA, always paired with the primary.", + "html": "Watch demo", + "css": ".ds-btn-ghost { display: inline-flex; align-items: center; gap: 8px; padding: 14px 28px; border-radius: 999px; background: transparent; color: #1f1a15; border: 1px solid #1f1a15; font-family: 'Inter', system-ui, sans-serif; font-weight: 500; font-size: 15px; text-decoration: none; transition: transform 150ms ease; } .ds-btn-ghost:hover { transform: translateY(-1px); }" + }, + { + "name": "Nav Pill", + "kind": "button", + "refersTo": "nav-pill", + "description": "Compact primary CTA used in the nav. Smaller padding than the full button.", + "html": "Get started", + "css": ".ds-nav-pill { display: inline-flex; align-items: center; padding: 9px 18px; border-radius: 999px; background: #1f1a15; color: #faf6ef; font-family: 'Inter', system-ui, sans-serif; font-weight: 500; font-size: 14px; text-decoration: none; }" + }, + { + "name": "Eyebrow Chip", + "kind": "chip", + "description": "The small uppercase label sitting above the hero headline. Pure typography, no background.", + "html": "
AI-native workflows
", + "css": ".ds-eyebrow { display: inline-block; font-family: 'Inter', system-ui, sans-serif; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.16em; color: #a8431f; }" + }, + { + "name": "Feature Card", + "kind": "card", + "refersTo": "card", + "description": "Tonal-layered card on cream-warm with hairline border. Flat, no shadow.", + "html": "
\n
\n

Lightning Fast

\n

Move from idea to production in minutes.

\n
", + "css": ".ds-card { background: #f4ebdc; border: 1px solid #e6dccb; border-radius: 20px; padding: 40px 32px; text-align: center; max-width: 280px; } .ds-card-icon { width: 56px; height: 56px; border-radius: 14px; background: #faf6ef; border: 1px solid #e6dccb; display: inline-flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 24px; } .ds-card-title { font-family: 'Fraunces', Georgia, serif; font-weight: 500; font-size: 22px; letter-spacing: -0.01em; margin: 0 0 12px; color: #1f1a15; } .ds-card-body { font-family: 'Inter', system-ui, sans-serif; font-size: 15px; color: #5b4f44; margin: 0; line-height: 1.55; }" + }, + { + "name": "Hero Headline", + "kind": "custom", + "description": "The display-scale headline with the One-Italic Rule applied: italic accent on a single word.", + "html": "

Build faster with intelligent workflows

", + "css": ".ds-hero-h1 { font-family: 'Fraunces', Georgia, serif; font-weight: 400; font-size: clamp(48px, 7vw, 88px); line-height: 1.05; letter-spacing: -0.02em; color: #1f1a15; max-width: 720px; margin: 0; } .ds-hero-h1 em { font-style: italic; color: #a8431f; }" + }, + { + "name": "Logo Strip Wordmark", + "kind": "custom", + "description": "The italic Fraunces wordmarks in the logo row — the exception to the One-Italic Rule.", + "html": "Northwind", + "css": ".ds-logo-mark { font-family: 'Fraunces', Georgia, serif; font-style: italic; font-size: 22px; color: #5b4f44; }" + } + ], + "narrative": { + "northStar": "Editorial confidence in warm light.", + "overview": "The system reads as a printed magazine spread, transposed to a screen. Cream paper as the page surface, ink-dark headlines in Fraunces, restrained pacing carried by whitespace. Density is mid. The accent (a burnt orange warming toward the lower stop of the cream-to-peach hero gradient) appears sparingly: on the eyebrow chip, the logo dot, and the italic emphasis inside the hero headline. Nowhere else.\n\nWhat the system explicitly rejects, per PRODUCT.md anti-references: glassmorphism, dark mode with neon glow, gradient text, side-stripe accents on cards, and the broader \"Fraunces-cream-peach SaaS template\" that this very page exemplifies. The DESIGN.md documents the current visual reality so the live-mode design panel can render it accurately. The brand intent is to diffuse away from it.", + "keyCharacteristics": [ + "Warm-paper palette anchored on cream (#faf6ef)", + "Display in Fraunces, body in Inter", + "Editorial-leaning layout vocabulary", + "Rounded throughout (14px, 20px, 999px)", + "Flat: no shadows, depth via tonal layering" + ], + "rules": [ + { + "name": "The Cream-Family Rule", + "body": "Every neutral surface tints toward the brand hue. No pure white anywhere, no pure black, no untinted gray. The eye should never read this page as 'default browser.'", + "section": "colors" + }, + { + "name": "The 10% Accent Rule", + "body": "The burnt orange covers no more than 10% of any rendered surface. Its rarity is the point.", + "section": "colors" + }, + { + "name": "The One-Italic Rule", + "body": "Italic appears exactly once per page: on a single emphasized word inside the hero headline. Nowhere else. The logo strip's italic Fraunces wordmarks are the exception that proves it (wordmark, not running italic).", + "section": "typography" + }, + { + "name": "The No-Gradient-Text Rule", + "body": "Type is solid color, always. The hero's cream-to-peach gradient is a section background, never a typographic effect.", + "section": "typography" + }, + { + "name": "The Flat-By-Default Rule", + "body": "Surfaces are flat at rest. Hover lift uses transform: translateY(-1px), never a shadow. Glassmorphism, neon glow, and elevation halos are absent by design.", + "section": "elevation" + } + ], + "dos": [ + "Do keep the burnt-orange accent under 10% of any visible surface; it's a typographic accent and a logo dot, not a button.", + "Do use Fraunces for display and Inter for body; respect the One-Italic Rule.", + "Do carry depth via tonal layering and hairline borders, not shadows.", + "Do tint every neutral toward the cream hue. Reject pure white and pure gray.", + "Do keep buttons fully rounded (999px) and cards moderately rounded (20px); the contrast is intentional." + ], + "donts": [ + "Don't add box-shadows to surfaces. The system is flat by default; hover lift uses transform, not shadow.", + "Don't introduce gradient text or background-clip: text. The hero gradient is a section background, never a typographic effect.", + "Don't add glassmorphism, neon glow, dark mode by default, or side-stripe colored borders. All banned in PRODUCT.md anti-references.", + "Don't introduce a fourth color outside the cream / ink / orange family without an explicit reason recorded in PRODUCT.md.", + "Don't drift into the broader 'Fraunces-cream-peach SaaS template' the page already exemplifies. The PRODUCT.md anti-references this aesthetic; departure-mode variants should diffuse away from it." + ] + } +} diff --git a/demos/landing-demo/DESIGN.md b/demos/landing-demo/DESIGN.md new file mode 100644 index 000000000..ca5ff39e7 --- /dev/null +++ b/demos/landing-demo/DESIGN.md @@ -0,0 +1,207 @@ +--- +name: Lumina +description: Editorial-warm landing page for an AI-native workflow tool. +colors: + cream: "#faf6ef" + cream-warm: "#f4ebdc" + peach: "#f6dfcb" + line: "#e6dccb" + ink: "#1f1a15" + soft: "#5b4f44" + accent: "#c8552b" + accent-deep: "#a8431f" +typography: + display: + fontFamily: "Fraunces, Georgia, serif" + fontSize: "clamp(3rem, 7vw, 5.5rem)" + fontWeight: 400 + lineHeight: 1.05 + letterSpacing: "-0.02em" + headline: + fontFamily: "Fraunces, Georgia, serif" + fontSize: "clamp(2.25rem, 4.5vw, 3.25rem)" + fontWeight: 400 + lineHeight: 1.1 + letterSpacing: "-0.02em" + title: + fontFamily: "Fraunces, Georgia, serif" + fontSize: "1.375rem" + fontWeight: 500 + lineHeight: 1.3 + letterSpacing: "-0.01em" + body: + fontFamily: "Inter, system-ui, sans-serif" + fontSize: "1rem" + fontWeight: 400 + lineHeight: 1.55 + letterSpacing: "normal" + lede: + fontFamily: "Inter, system-ui, sans-serif" + fontSize: "1.25rem" + fontWeight: 400 + lineHeight: 1.55 + letterSpacing: "normal" + label: + fontFamily: "Inter, system-ui, sans-serif" + fontSize: "0.75rem" + fontWeight: 600 + lineHeight: 1.4 + letterSpacing: "0.16em" +rounded: + card: "20px" + icon: "14px" + pill: "999px" +spacing: + xs: "8px" + sm: "16px" + md: "24px" + lg: "32px" + xl: "56px" + 2xl: "80px" + 3xl: "120px" +components: + button-primary: + backgroundColor: "{colors.ink}" + textColor: "{colors.cream}" + rounded: "{rounded.pill}" + padding: "14px 28px" + button-ghost: + backgroundColor: "transparent" + textColor: "{colors.ink}" + rounded: "{rounded.pill}" + padding: "14px 28px" + nav-pill: + backgroundColor: "{colors.ink}" + textColor: "{colors.cream}" + rounded: "{rounded.pill}" + padding: "9px 18px" + card: + backgroundColor: "{colors.cream-warm}" + textColor: "{colors.soft}" + rounded: "{rounded.card}" + padding: "40px 32px" + icon-tile: + backgroundColor: "{colors.cream}" + rounded: "{rounded.icon}" + size: "56px" +--- + +# Design System: Lumina + +## 1. Overview + +**Creative North Star: "Editorial confidence in warm light."** + +The system reads as a printed magazine spread, transposed to a screen. Cream paper as the page surface, ink-dark headlines in Fraunces, restrained pacing carried by whitespace. Density is mid. The accent (a burnt orange warming toward the lower stop of the cream-to-peach hero gradient) appears sparingly: on the eyebrow chip, the logo dot, and the italic emphasis inside the hero headline. Nowhere else. + +What the system explicitly rejects, per PRODUCT.md anti-references: glassmorphism, dark mode with neon glow, gradient text, side-stripe accents on cards, and the broader "Fraunces-cream-peach SaaS template" that this very page exemplifies. The DESIGN.md documents the current visual reality so the live-mode design panel can render it accurately. The brand intent is to diffuse away from it. + +**Key Characteristics:** +- Warm-paper palette anchored on cream (#faf6ef) +- Display in Fraunces, body in Inter +- Editorial-leaning layout vocabulary +- Rounded throughout (14px, 20px, 999px) +- Flat: no shadows, depth via tonal layering + +## 2. Colors + +A single warm-cream family carrying both surface and neutral text, with one muted-orange accent that earns its rare appearances. + +### Primary +- **Burnt Orange Accent** (#c8552b): the eyebrow chip's color, the logo dot, the italic emphasis inside the hero headline. Decorative; never a surface, never a button background. +- **Accent Deep** (#a8431f): the darker variant for hover and emphasis states; same hue, a step deeper. + +### Neutral +- **Cream** (#faf6ef): the page surface, the nav background (translucent), and the bottom of the hero gradient. +- **Cream Warm** (#f4ebdc): the feature card surface; one shade darker than cream, anchors the card group as a contained set. +- **Peach** (#f6dfcb): the warmer stop of the hero gradient. Atmospheric, not structural. +- **Ink** (#1f1a15): primary body text; primary-button background; the dark CTA section's surface. +- **Soft** (#5b4f44): secondary text; captions; footer copy; nav link rest state. +- **Line** (#e6dccb): hairline borders on cards, the nav, the footer, and the icon tiles. + +### Named Rules +**The Cream-Family Rule.** Every neutral surface tints toward the brand hue. No pure white anywhere, no pure black, no untinted gray. The eye should never read this page as "default browser." + +**The 10% Accent Rule.** The burnt orange covers no more than 10% of any rendered surface. Its rarity is the point. + +## 3. Typography + +**Display Font:** Fraunces (Georgia fallback, serif) +**Body Font:** Inter (system-ui fallback, sans-serif) + +**Character:** A magazine-cover serif for the headlines and brand mark, paired with a refined sans for everything else. The italic Fraunces inside `` is the system's only italic, used exactly once per page on a single word inside the hero headline. + +### Hierarchy +- **Display** (Fraunces, clamp(48px, 7vw, 88px), weight 400, line-height 1.05, letter-spacing -0.02em): hero headlines only. +- **Headline** (Fraunces, clamp(36px, 4.5vw, 52px), weight 400, line-height 1.1): section headlines. +- **Title** (Fraunces, 22px, weight 500, letter-spacing -0.01em): card headings. +- **Lede** (Inter, 20px, weight 400, line-height 1.55): the supporting paragraph below a hero headline. +- **Body** (Inter, 15–18px, weight 400, line-height 1.55): default paragraph copy. Cap line length at 65–75ch. +- **Label** (Inter, 12px, weight 600, letter-spacing 0.16em, uppercase): the eyebrow chip and any small uppercase labels. + +### Named Rules +**The One-Italic Rule.** Italic appears exactly once per page: on a single emphasized word inside the hero headline. Nowhere else. The logo strip's italic Fraunces wordmarks are the exception that proves it (wordmark, not running italic). + +**The No-Gradient-Text Rule.** Type is solid color, always. The hero's cream-to-peach gradient is a section background, never a typographic effect. + +## 4. Elevation + +Flat. No shadows on cards, buttons, surfaces, or any rendered element. Depth is conveyed by three things and only those: + +- **Tonal layering**: cream (page) sits below cream-warm (cards), which sit below ink (the dark CTA section). +- **Hairline borders**: 1px line color (`--line`) on every contained surface. +- **Sticky-nav backdrop blur**: the only blur in the system, marking the nav as living above content. + +### Named Rules +**The Flat-By-Default Rule.** Surfaces are flat at rest. Hover lift uses `transform: translateY(-1px)`, never a shadow. Glassmorphism, neon glow, and elevation halos are absent by design. + +## 5. Components + +### Buttons +- **Shape:** fully rounded (border-radius: 999px). The full-size variant is 14px 28px padding; the smaller nav pill is 9px 18px. +- **Primary** (`.btn-primary`, `.pill`): background ink, text cream. The primary CTA on every section. +- **Ghost** (`.btn-ghost`): transparent background, ink 1px border, ink text. Secondary CTA, always paired with primary. +- **Hover:** translateY(-1px), 150ms ease. +- **Inverted Primary** (`.cta-section .btn-primary`): background cream, text ink. Used because the dark CTA section reverses the surface contrast. + +### Cards (Feature Tiles) +- **Corner Style:** 20px rounded (less than pill, more than container). +- **Background:** cream-warm (`--cream-warm`), one shade darker than the surrounding cream surface. +- **Border:** 1px line color. +- **Internal Padding:** 40px 32px (generous; cards breathe). +- **Shadow:** none, see Elevation. + +### Icon Tile (inside Cards) +- **Size:** 56×56px. +- **Background:** cream (`--cream`). +- **Border:** 1px line color. +- **Corner Style:** 14px rounded. +- **Position:** centered above the card heading, 24px bottom margin. + +### Eyebrow Chip +- **Style:** uppercase Inter, 12px, weight 600, letter-spacing 0.16em, color accent-deep (#a8431f). No background or border — pure typographic label. +- **Position:** standalone above the hero `

`, with 32px bottom margin. + +### Navigation +- **Background:** translucent cream (`rgba(250, 246, 239, 0.85)`) with `backdrop-filter: blur(10px)`. Sticky to the top. +- **Border:** 1px line color at the bottom edge. +- **Links:** 14px Inter, weight 400, color soft (rest), color ink (hover). The trailing pill CTA uses the nav-pill component. + +### Logo Strip +- **Items:** Fraunces italic, 22px, color soft. Six wordmarks, justified across a single row, separated by hairline borders top and bottom. + +## 6. Do's and Don'ts + +### Do: +- **Do** keep the burnt-orange accent under 10% of any visible surface; it's a typographic accent and a logo dot, not a button. +- **Do** use Fraunces for display and Inter for body; respect the One-Italic Rule. +- **Do** carry depth via tonal layering and hairline borders, not shadows. +- **Do** tint every neutral toward the cream hue. Reject pure white and pure gray. +- **Do** keep buttons fully rounded (999px) and cards moderately rounded (20px); the contrast is intentional. + +### Don't: +- **Don't** add box-shadows to surfaces. The system is flat by default; hover lift uses transform, not shadow. +- **Don't** introduce gradient text or `background-clip: text`. The hero gradient is a section background, never a typographic effect. +- **Don't** add glassmorphism, neon glow, dark mode by default, or side-stripe colored borders — all banned in PRODUCT.md anti-references. +- **Don't** introduce a fourth color outside the cream / ink / orange family without an explicit reason recorded in PRODUCT.md. +- **Don't** drift into the broader "Fraunces-cream-peach SaaS template" the page already exemplifies. The PRODUCT.md anti-references this aesthetic; departure-mode variants should diffuse away from it. diff --git a/demos/landing-demo/PRODUCT.md b/demos/landing-demo/PRODUCT.md new file mode 100644 index 000000000..ca8b945bc --- /dev/null +++ b/demos/landing-demo/PRODUCT.md @@ -0,0 +1,39 @@ +# Lumina + +## Register + +brand + +## Users + +Senior product builders at Series A through C software companies: founding engineers, design-leaning PMs, and tech-fluent founders. They evaluate tools fast, distrust marketing-speak, and have spent the last twelve months watching every SaaS landing page converge on the same warm-cream-and-Fraunces template. They will close the tab on anything that pattern-matches to "AI startup default" within two seconds. + +## Product Purpose + +Lumina is an AI-native workflow tool for product teams. The marketing site exists to make taste-aware buyers stop, read, and remember. Success is not "high conversion." Success is "shared in a group chat with the message: have you seen this yet." + +## Brand Personality + +Three-word personality: **specific, earned, unmistakable**. The voice is closer to a well-written engineering blog post than a pitch deck. Claims are concrete and verifiable. Adjectives are rationed. The brand has a point of view and trusts the reader's intelligence. + +## Anti-references + +Explicitly avoid the patterns that have become the new monoculture: + +- **The Fraunces-cream-peach SaaS template.** Warm cream backgrounds, large italic Fraunces headlines, soft burnt-orange accent, gentle peach gradients. This was novel in early 2025 and is now everywhere. The current `index.html` is itself an example. Diffuse away from it. +- **Three-icon feature tile rows.** Icon-above-h3-above-paragraph, repeated three or four times across equal-width cards, generic single-word feature names ("Lightning Fast", "Enterprise Secure", "Built to Scale"). The flattest possible expression of "we have features." +- **Hero superlatives.** "All-in-one platform", "in record time", "powered by AI, designed for humans", "trusted by 10,000+ teams worldwide". B2B SaaS mad-libs. +- **Soft-everything aesthetic.** Rounded corners on every element, soft shadows on every surface, polite center-aligned spacing, no edges, no opinions. +- **Decorative gradient text and glass-blur backdrops.** Both already banned by the shared design laws; restated here because they're still the first thing models reach for on brand surfaces. + +## Design Principles + +1. **Distinctiveness over polish.** Polish is table stakes; standing out from the SaaS-default sea is the job. If the page could plausibly be running on twenty other companies' domains, it has failed regardless of how clean it looks. +2. **Specific over generic.** Real numbers, real product screenshots, real customer names, real claims with evidence. No abstract feature taxonomies. +3. **Earned attention, not decorative attention.** Type weight, scale, and rhythm carry hierarchy. Animation, gradients, and effects are reserved for moments that genuinely warrant them. +4. **Commitment over compromise.** Confidence shows up as restraint OR as full commitment, never as a hedged middle. A timid centered-stack with polite shadows is the failure mode this brand exists to avoid. +5. **Confidence without volume.** The strongest brands speak quietly when quiet is the right register, and loudly when loud is. Insecurity is what splits the difference. + +## Accessibility & Inclusion + +WCAG 2.2 AA minimum across body text, headings, and interactive controls. Honor `prefers-reduced-motion` for any animation. Color is never the sole carrier of meaning. Tap targets ≥44px on mobile. Tested with screen readers; reading order matches visual order. diff --git a/demos/landing-demo/classic/index.html b/demos/landing-demo/classic/index.html new file mode 100644 index 000000000..b17a18b43 --- /dev/null +++ b/demos/landing-demo/classic/index.html @@ -0,0 +1,350 @@ + + + + + +Lumina — The future of intelligent workflows + + + + + + + + + +
+
🚀 Powered by AI
+

Build faster with intelligent workflows

+

The all-in-one platform that helps modern teams ship better products in record time. Powered by AI, designed for humans.

+ +
Backed by top-tier investors
+
+ +
+
+ Northwind + Halcyon + Meridian + Fieldnote + Atlas & Co. + Verity +
+
+ +
+
+

Everything you need to ship faster

+

One platform. Endless possibilities. Built for the future.

+
+
+
+
+

Lightning Fast

+

Move from idea to production in minutes, not months. Our AI handles the heavy lifting so you can focus on what matters.

+
+
+
🔒
+

Enterprise Secure

+

SOC 2 compliant infrastructure with end-to-end encryption. Your data is safe, always.

+
+
+
📈
+

Built to Scale

+

From your first prototype to millions of users, Lumina grows with your team without breaking a sweat.

+
+
+
+ +
+

Ready to transform how your team builds?

+

Join thousands of teams already shipping faster with Lumina.

+ Get early access → +
+ +
+ © 2022 Lumina, Inc. Powered by AI in San Francisco. +
+ + diff --git a/demos/landing-demo/index.html b/demos/landing-demo/index.html new file mode 100644 index 000000000..ff7e53432 --- /dev/null +++ b/demos/landing-demo/index.html @@ -0,0 +1,309 @@ + + + + + +Lumina — Intelligent workflows for modern product teams + + + + + + + + + + + + + + +
+
AI-native workflows
+

Build faster with intelligent workflows for modern teams

+

The all-in-one platform that helps product teams ship better software in record time. Powered by AI, designed for humans.

+ +
Trusted by 10,000+ forward-thinking teams worldwide
+
+ + + + + +
+
+ Northwind + Halcyon + Meridian + Fieldnote + Atlas & Co. + Verity +
+
+ +
+
+

Everything you need to ship great products

+

One platform. Endless possibilities. Built for teams who care about craft.

+
+ +
+
+
+

Lightning Fast

+

Move from idea to production in minutes, not months. Our AI handles the heavy lifting so you can focus on what matters.

+
+
+
🔒
+

Enterprise Secure

+

SOC 2 compliant infrastructure with end-to-end encryption. Your data is safe, always.

+
+
+
📈
+

Built to Scale

+

From your first prototype to millions of users — Lumina grows with your team without breaking a sweat.

+
+
+ +
+ + +
+

Ready to transform how your team builds?

+

Join thousands of teams already shipping faster with Lumina.

+ Start your free trial → +
+ + +
+ © 2026 Lumina, Inc. — Crafted with care in San Francisco. +
+ + + diff --git a/demos/landing-demo/package.json b/demos/landing-demo/package.json new file mode 100644 index 000000000..374ff06e8 --- /dev/null +++ b/demos/landing-demo/package.json @@ -0,0 +1,14 @@ +{ + "name": "landing-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^8.0.0" + } +} diff --git a/package.json b/package.json index 69248586b..83c6f06bc 100644 --- a/package.json +++ b/package.json @@ -41,13 +41,15 @@ "LICENSE" ], "scripts": { - "build": "bun run scripts/build.js", + "build:skills": "bun run scripts/build.js", + "build:site": "npx astro build", + "build": "bun run build:skills && bun run build:site && cp -R dist build/_data/dist", "build:browser": "node scripts/build-browser-detector.js", "build:extension": "node scripts/build-extension.js", "clean": "rm -rf dist build", "rebuild": "bun run clean && bun run build", - "dev": "bun run server/index.js", - "preview": "bun run build && wrangler pages dev", + "dev": "npx astro dev", + "preview": "bun run build && npx astro preview", "deploy": "bun run build && wrangler pages deploy build/", "test": "bun test tests/build.test.js tests/detect-antipatterns.test.js tests/windows-path-fix.test.js && node --test tests/detect-antipatterns-fixtures.test.mjs && node --test tests/detect-antipatterns-browser.test.mjs && node --test tests/cleanup-deprecated.test.mjs && node --test tests/live-wrap.test.mjs && node --test tests/live-accept.test.mjs && node --test tests/live-inject.test.mjs && node --test tests/live-server.test.mjs && node --test tests/live-browser-regression.test.mjs && node --test tests/framework-fixtures.test.mjs", "test:live-e2e": "node --test --test-timeout=600000 tests/live-e2e.test.mjs", @@ -67,6 +69,8 @@ "puppeteer": "^24.42.0" }, "devDependencies": { + "astro": "^6.2.1", + "@paper-design/shaders": "^0.0.76", "@ai-sdk/anthropic": "^3.0.71", "@ai-sdk/openai": "^3.0.53", "@anthropic-ai/claude-agent-sdk": "^0.2.119", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 719d67f04..15c769339 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,12 +30,18 @@ importers: '@google/genai': specifier: ^1.50.1 version: 1.50.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6)) + '@paper-design/shaders': + specifier: ^0.0.76 + version: 0.0.76 ai: specifier: ^6.0.168 version: 6.0.168(zod@4.3.6) archiver: specifier: ^7.0.1 version: 7.0.1 + astro: + specifier: ^6.2.1 + version: 6.2.1(@types/node@25.6.0)(rollup@4.60.2) modern-screenshot: specifier: ^4.7.0 version: 4.7.0 @@ -100,21 +106,25 @@ packages: resolution: {integrity: sha512-7EhskzBkeH5LOzuXNPoyTjNb7TwR9+mZcZ0s/e3AVEbTJTN9fddx3dcqZ6h+PivyF+JAGYjf7/OqUWTrawIgJw==} cpu: [arm64] os: [linux] + libc: [musl] '@anthropic-ai/claude-agent-sdk-linux-arm64@0.2.122': resolution: {integrity: sha512-tSOy0BrxQfPNG4mqrz7KC7T+7ysrcuzof1m2Cw+DRVqp1O4CZl1VvYu2gQJxnVYoVKO2B6KzC05oOqpV9EWn7A==} cpu: [arm64] os: [linux] + libc: [glibc] '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.2.122': resolution: {integrity: sha512-SLPgQcz8tEsYA4YqHb8SrVfssTMPIXJ4C6NTrullOd3+IlutuJnyRw9wX0tLi0sozDwv6TIwAf78RV84RMUd/w==} cpu: [x64] os: [linux] + libc: [musl] '@anthropic-ai/claude-agent-sdk-linux-x64@0.2.122': resolution: {integrity: sha512-ClpyiD79YzjJ5o1w/yHJ/FugaZTCeS0IypQJg/SrAKKfn2oTyKFDg7P+Fu4+WSezj81qgdO/vT1TW2IPALue2w==} cpu: [x64] os: [linux] + libc: [glibc] '@anthropic-ai/claude-agent-sdk-win32-arm64@0.2.122': resolution: {integrity: sha512-h7aUAlm9PTuWyf2RNncpdFwweYD5yMsMqB1Y3DioCP8jZSPNRuiBPxJdtRVPimrAkPre7gY1hLiicKOaEDwFpw==} @@ -161,22 +171,64 @@ packages: '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@astrojs/compiler@4.0.0': + resolution: {integrity: sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==} + + '@astrojs/internal-helpers@0.9.0': + resolution: {integrity: sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg==} + + '@astrojs/markdown-remark@7.1.1': + resolution: {integrity: sha512-C6e9BnLGlbdv6bV8MYGeHpHxsUHrCrB4OuRLqi5LI7oiBVcBcqfUN06zpwFQdHgV48QCCrMmLpyqBr7VqC+swA==} + + '@astrojs/prism@4.0.1': + resolution: {integrity: sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ==} + engines: {node: '>=22.12.0'} + + '@astrojs/telemetry@3.3.1': + resolution: {integrity: sha512-7fcIxXS9J4ls5tr8b3ww9rbAIz2+HrhNJYZdkAhhB4za/I5IZ/60g+Bs8q7zwG0tOIZfNB4JWhVJ1Qkl/OrNCw==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.29.2': resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true + '@capsizecss/unpack@4.0.0': + resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} + engines: {node: '>=18'} + + '@clack/core@1.3.0': + resolution: {integrity: sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.3.0': + resolution: {integrity: sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw==} + engines: {node: '>= 20.12.0'} + '@cloudflare/kv-asset-handler@0.4.2': resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} engines: {node: '>=18.0.0'} @@ -473,89 +525,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -608,6 +676,12 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@paper-design/shaders@0.0.76': + resolution: {integrity: sha512-AcNDY4J66YQHUfQYFInkCP7M9VOje0od7wLpOR7LtCmc532opJy6ll+h1W9zBovz8tt9U7OADUmJ/qKEXyOX/A==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -656,6 +730,184 @@ packages: engines: {node: '>=18'} hasBin: true + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.60.2': + resolution: {integrity: sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.2': + resolution: {integrity: sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.2': + resolution: {integrity: sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.2': + resolution: {integrity: sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.2': + resolution: {integrity: sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.2': + resolution: {integrity: sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.2': + resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.2': + resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.2': + resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.2': + resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.2': + resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.2': + resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.2': + resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.2': + resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.2': + resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.2': + resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.2': + resolution: {integrity: sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.2': + resolution: {integrity: sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.2': + resolution: {integrity: sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.2': + resolution: {integrity: sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.2': + resolution: {integrity: sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==} + cpu: [x64] + os: [win32] + + '@shikijs/core@4.0.2': + resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} + engines: {node: '>=20'} + + '@shikijs/engine-javascript@4.0.2': + resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.0.2': + resolution: {integrity: sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==} + engines: {node: '>=20'} + + '@shikijs/langs@4.0.2': + resolution: {integrity: sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.0.2': + resolution: {integrity: sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==} + engines: {node: '>=20'} + + '@shikijs/themes@4.0.2': + resolution: {integrity: sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==} + engines: {node: '>=20'} + + '@shikijs/types@4.0.2': + resolution: {integrity: sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sindresorhus/is@7.2.0': resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} @@ -669,15 +921,39 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node@25.6.0': resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vercel/oidc@3.2.0': resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} engines: {node: '>= 20'} @@ -727,6 +1003,10 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + archiver-utils@5.0.2: resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} engines: {node: '>= 14'} @@ -738,13 +1018,29 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + ast-types@0.13.4: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} + astro@6.2.1: + resolution: {integrity: sha512-3g1sYNly+QAkuO5ErNEQBYvsxorNDSCUNIeStBs+kcXGchvKQl1Q9EuDNOvSg010XLlHJFLVFZs9LV18Jjp4Hg==} + engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + b4a@1.8.0: resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==} peerDependencies: @@ -753,6 +1049,9 @@ packages: react-native-b4a: optional: true + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -817,6 +1116,9 @@ packages: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} @@ -849,15 +1151,39 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chromium-bidi@14.0.0: resolution: {integrity: sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==} peerDependencies: devtools-protocol: '*' + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -865,6 +1191,17 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + common-ancestor-path@2.0.0: + resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} + engines: {node: '>= 18'} + compress-commons@6.0.2: resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} engines: {node: '>= 14'} @@ -877,6 +1214,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -918,10 +1258,28 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + css-tree@3.2.1: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -946,6 +1304,12 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + degenerator@5.0.1: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} @@ -954,13 +1318,50 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devalue@5.8.0: + resolution: {integrity: sha512-2zA9pFEsnp7vWBZbXF5JAgAq0fsUIt/1XPbRiAmRV3lp/2C3upzH+sADiyy66aFCihoLEsrQHxNM5w1gIDfsBg==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + devtools-protocol@0.0.1595872: resolution: {integrity: sha512-kRfgp8vWVjBu/fbYCiVFiOqsCk3CrMKEo3WbgGT2NXK2dG7vawWPBljixajVgGK9II8rDO9G0oD0zLt3I1daRg==} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -987,6 +1388,10 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + entities@6.0.1: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} @@ -1009,6 +1414,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -1025,6 +1433,10 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + escodegen@2.1.0: resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} @@ -1039,6 +1451,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1051,6 +1466,9 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + events-universal@1.0.1: resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} @@ -1090,12 +1508,30 @@ packages: fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -1104,6 +1540,17 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fontace@0.4.1: + resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==} + + fontkitten@1.0.3: + resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} + engines: {node: '>=20'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -1175,6 +1622,9 @@ packages: resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} engines: {node: '>= 14'} + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -1195,6 +1645,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -1203,6 +1656,36 @@ packages: resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + hono@4.12.15: resolution: {integrity: sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg==} engines: {node: '>=16.9.0'} @@ -1211,6 +1694,15 @@ packages: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -1245,13 +1737,35 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-docker@4.0.0: + resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==} + engines: {node: '>=20'} + hasBin: true + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -1262,6 +1776,10 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -1332,6 +1850,9 @@ packages: long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -1343,6 +1864,15 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.2: + resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@16.4.2: resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} engines: {node: '>= 20'} @@ -1352,6 +1882,48 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} @@ -1363,6 +1935,90 @@ packages: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} @@ -1414,30 +2070,55 @@ packages: react-dom: optional: true + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + netmask@2.1.1: resolution: {integrity: sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==} engines: {node: '>= 0.4.0'} + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} deprecated: Use your platform's native DOMException instead + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1446,6 +2127,15 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -1453,10 +2143,28 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + p-limit@7.3.0: + resolution: {integrity: sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==} + engines: {node: '>=20'} + + p-queue@9.2.0: + resolution: {integrity: sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g==} + engines: {node: '>=20'} + p-retry@4.6.2: resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} engines: {node: '>=8'} + p-timeout@7.0.1: + resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} + engines: {node: '>=20'} + pac-proxy-agent@7.2.0: resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} engines: {node: '>= 14'} @@ -1468,6 +2176,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1476,6 +2187,12 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parse5@8.0.0: resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} @@ -1503,9 +2220,20 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + piccolore@0.1.3: + resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pkce-challenge@5.0.1: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} @@ -1520,6 +2248,14 @@ packages: engines: {node: '>=18'} hasBin: true + postcss@8.5.13: + resolution: {integrity: sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==} + engines: {node: ^10 || ^12 || >=14} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -1531,6 +2267,9 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + protobufjs@7.5.6: resolution: {integrity: sha512-M71sTMB146U3u0di3yup8iM+zv8yPRNQVr1KK4tyBitl3qFvEGucq/rGDRShD2rsJhtN02RJaJ7j5X5hmy8SJg==} engines: {node: '>=12.0.0'} @@ -1566,6 +2305,9 @@ packages: resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} engines: {node: '>=0.6'} + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -1584,6 +2326,47 @@ packages: readdir-glob@1.1.3: resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1596,10 +2379,27 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} + rollup@4.60.2: + resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -1613,6 +2413,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -1645,6 +2449,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shiki@4.0.2: + resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==} + engines: {node: '>=20'} + side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -1665,10 +2473,17 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + engines: {node: '>= 18'} + socks-proxy-agent@8.0.5: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} @@ -1685,6 +2500,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -1706,6 +2524,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1718,6 +2539,11 @@ packages: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} + svgo@4.0.1: + resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} + engines: {node: '>=16'} + hasBin: true + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -1733,6 +2559,21 @@ packages: text-decoder@1.2.7: resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinyclip@0.1.12: + resolution: {integrity: sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA==} + engines: {node: ^16.14.0 || >= 17.3.0} + + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + engines: {node: '>=18'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + tldts-core@7.0.28: resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} @@ -1752,9 +2593,25 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-algebra@2.0.0: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -1765,6 +2622,15 @@ packages: typed-query-selector@2.12.1: resolution: {integrity: sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@7.19.2: resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} @@ -1779,10 +2645,105 @@ packages: unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unifont@0.7.4: + resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -1790,10 +2751,70 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -1813,6 +2834,10 @@ packages: resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1875,6 +2900,9 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -1883,6 +2911,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -1890,6 +2922,10 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + youch-core@0.3.3: resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} @@ -1911,6 +2947,9 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@ai-sdk/anthropic@3.0.71(zod@4.3.6)': @@ -2013,6 +3052,51 @@ snapshots: '@asamuzakjp/nwsapi@2.3.9': {} + '@astrojs/compiler@4.0.0': {} + + '@astrojs/internal-helpers@0.9.0': + dependencies: + picomatch: 4.0.4 + + '@astrojs/markdown-remark@7.1.1': + dependencies: + '@astrojs/internal-helpers': 0.9.0 + '@astrojs/prism': 4.0.1 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + js-yaml: 4.1.1 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + retext-smartypants: 6.2.0 + shiki: 4.0.2 + smol-toml: 1.6.1 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.1.0 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@4.0.1': + dependencies: + prismjs: 1.30.0 + + '@astrojs/telemetry@3.3.1': + dependencies: + ci-info: 4.4.0 + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 4.0.0 + is-wsl: 3.1.1 + which-pm-runs: 1.1.0 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -2020,15 +3104,41 @@ snapshots: picocolors: 1.1.1 optional: true - '@babel/helper-validator-identifier@7.28.5': - optional: true + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 '@babel/runtime@7.29.2': {} + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 + '@capsizecss/unpack@4.0.0': + dependencies: + fontkitten: 1.0.3 + + '@clack/core@1.3.0': + dependencies: + fast-wrap-ansi: 0.2.0 + sisteransi: 1.0.5 + + '@clack/prompts@1.3.0': + dependencies: + '@clack/core': 1.3.0 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.0 + sisteransi: 1.0.5 + '@cloudflare/kv-asset-handler@0.4.2': {} '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260426.1)': @@ -2320,6 +3430,10 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@oslojs/encoding@1.1.0': {} + + '@paper-design/shaders@0.0.76': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -2374,6 +3488,129 @@ snapshots: - supports-color optional: true + '@rollup/pluginutils@5.3.0(rollup@4.60.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.60.2 + + '@rollup/rollup-android-arm-eabi@4.60.2': + optional: true + + '@rollup/rollup-android-arm64@4.60.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.2': + optional: true + + '@rollup/rollup-darwin-x64@4.60.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.2': + optional: true + + '@shikijs/core@4.0.2': + dependencies: + '@shikijs/primitive': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + + '@shikijs/primitive@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/themes@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + + '@shikijs/types@4.0.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@sindresorhus/is@7.2.0': {} '@speed-highlight/core@1.2.15': {} @@ -2383,17 +3620,41 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': optional: true + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + '@types/node@25.6.0': dependencies: undici-types: 7.19.2 '@types/retry@0.12.0': {} + '@types/unist@3.0.3': {} + '@types/yauzl@2.10.3': dependencies: '@types/node': 25.6.0 optional: true + '@ungap/structured-clone@1.3.0': {} + '@vercel/oidc@3.2.0': {} abort-controller@3.0.0: @@ -2436,6 +3697,11 @@ snapshots: ansi-styles@6.2.3: {} + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + archiver-utils@5.0.2: dependencies: glob: 10.4.5 @@ -2460,18 +3726,118 @@ snapshots: - bare-buffer - react-native-b4a - argparse@2.0.1: - optional: true + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-iterate@2.0.1: {} ast-types@0.13.4: dependencies: tslib: 2.8.1 optional: true + astro@6.2.1(@types/node@25.6.0)(rollup@4.60.2): + dependencies: + '@astrojs/compiler': 4.0.0 + '@astrojs/internal-helpers': 0.9.0 + '@astrojs/markdown-remark': 7.1.1 + '@astrojs/telemetry': 3.3.1 + '@capsizecss/unpack': 4.0.0 + '@clack/prompts': 1.3.0 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.3.0(rollup@4.60.2) + aria-query: 5.3.2 + axobject-query: 4.1.0 + ci-info: 4.4.0 + clsx: 2.1.1 + common-ancestor-path: 2.0.0 + cookie: 1.1.1 + devalue: 5.8.0 + diff: 8.0.4 + dset: 3.1.4 + es-module-lexer: 2.1.0 + esbuild: 0.27.3 + flattie: 1.1.1 + fontace: 0.4.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + js-yaml: 4.1.1 + magic-string: 0.30.21 + magicast: 0.5.2 + mrmime: 2.0.1 + neotraverse: 0.6.18 + obug: 2.1.1 + p-limit: 7.3.0 + p-queue: 9.2.0 + package-manager-detector: 1.6.0 + piccolore: 0.1.3 + picomatch: 4.0.4 + rehype: 13.0.2 + semver: 7.7.4 + shiki: 4.0.2 + smol-toml: 1.6.1 + svgo: 4.0.1 + tinyclip: 0.1.12 + tinyexec: 1.1.2 + tinyglobby: 0.2.16 + tsconfck: 3.1.6 + ultrahtml: 1.6.0 + unifont: 0.7.4 + unist-util-visit: 5.1.0 + unstorage: 1.17.5 + vfile: 6.0.3 + vite: 7.3.2(@types/node@25.6.0) + vitefu: 1.1.3(vite@7.3.2(@types/node@25.6.0)) + xxhash-wasm: 1.1.0 + yargs-parser: 22.0.0 + zod: 4.3.6 + optionalDependencies: + sharp: 0.34.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + async@3.2.6: {} + axobject-query@4.1.0: {} + b4a@1.8.0: {} + bail@2.0.2: {} + balanced-match@1.0.2: {} bare-events@2.8.2: {} @@ -2533,6 +3899,8 @@ snapshots: transitivePeerDependencies: - supports-color + boolbase@1.0.0: {} + brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 @@ -2564,6 +3932,18 @@ snapshots: callsites@3.1.0: optional: true + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + chromium-bidi@14.0.0(devtools-protocol@0.0.1595872): dependencies: devtools-protocol: 0.0.1595872 @@ -2571,6 +3951,8 @@ snapshots: zod: 3.25.76 optional: true + ci-info@4.4.0: {} + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -2578,12 +3960,20 @@ snapshots: wrap-ansi: 7.0.0 optional: true + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + + commander@11.1.0: {} + + common-ancestor-path@2.0.0: {} + compress-commons@6.0.2: dependencies: crc-32: 1.2.2 @@ -2596,6 +3986,8 @@ snapshots: content-type@1.0.5: {} + cookie-es@1.2.3: {} + cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -2630,11 +4022,34 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + css-tree@3.2.1: dependencies: mdn-data: 2.27.1 source-map-js: 1.2.1 + css-what@6.2.2: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + data-uri-to-buffer@4.0.1: {} data-uri-to-buffer@6.0.2: @@ -2653,6 +4068,12 @@ snapshots: decimal.js@10.6.0: {} + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + defu@6.1.7: {} + degenerator@5.0.1: dependencies: ast-types: 0.13.4 @@ -2662,11 +4083,45 @@ snapshots: depd@2.0.0: {} + dequal@2.0.3: {} + + destr@2.0.5: {} + detect-libc@2.1.2: {} + devalue@5.8.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + devtools-protocol@0.0.1595872: optional: true + diff@8.0.4: {} + + dlv@1.1.3: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dset@3.1.4: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2692,6 +4147,8 @@ snapshots: once: 1.4.0 optional: true + entities@4.5.0: {} + entities@6.0.1: {} env-paths@2.2.1: @@ -2708,6 +4165,8 @@ snapshots: es-errors@1.3.0: {} + es-module-lexer@2.1.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -2746,6 +4205,8 @@ snapshots: escape-html@1.0.3: {} + escape-string-regexp@5.0.0: {} + escodegen@2.1.0: dependencies: esprima: 4.0.1 @@ -2761,6 +4222,8 @@ snapshots: estraverse@5.3.0: optional: true + estree-walker@2.0.2: {} + esutils@2.0.3: optional: true @@ -2768,6 +4231,8 @@ snapshots: event-target-shim@5.0.1: {} + eventemitter3@5.0.4: {} + events-universal@1.0.1: dependencies: bare-events: 2.8.2 @@ -2837,13 +4302,27 @@ snapshots: fast-fifo@1.3.2: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.0: {} + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + fd-slicer@1.1.0: dependencies: pend: 1.2.0 optional: true + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -2860,6 +4339,16 @@ snapshots: transitivePeerDependencies: - supports-color + flattie@1.1.1: {} + + fontace@0.4.1: + dependencies: + fontkitten: 1.0.3 + + fontkitten@1.0.3: + dependencies: + tiny-inflate: 1.0.3 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -2938,6 +4427,8 @@ snapshots: - supports-color optional: true + github-slugger@2.0.0: {} + glob@10.4.5: dependencies: foreground-child: 3.3.1 @@ -2964,12 +4455,111 @@ snapshots: graceful-fs@4.2.11: {} + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + has-symbols@1.1.0: {} hasown@2.0.3: dependencies: function-bind: 1.1.2 + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + hono@4.12.15: {} html-encoding-sniffer@6.0.0: @@ -2978,6 +4568,12 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + http-cache-semantics@4.2.0: {} + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -3019,17 +4615,33 @@ snapshots: ipaddr.js@1.9.1: {} + iron-webcrypto@1.2.1: {} + is-arrayish@0.2.1: optional: true + is-docker@3.0.0: {} + + is-docker@4.0.0: {} + is-fullwidth-code-point@3.0.0: {} + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} is-promise@4.0.0: {} is-stream@2.0.1: {} + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + isarray@1.0.0: {} isexe@2.0.0: {} @@ -3048,7 +4660,6 @@ snapshots: js-yaml@4.1.1: dependencies: argparse: 2.0.1 - optional: true jsdom@29.0.0: dependencies: @@ -3118,6 +4729,8 @@ snapshots: long@5.3.2: {} + longest-streak@3.1.0: {} + lru-cache@10.4.3: {} lru-cache@11.3.3: {} @@ -3125,16 +4738,341 @@ snapshots: lru-cache@7.18.3: optional: true + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.2: + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + + markdown-table@3.0.4: {} + marked@16.4.2: {} math-intrinsics@1.1.0: {} + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + mdn-data@2.27.1: {} media-typer@1.1.0: {} merge-descriptors@2.0.0: {} + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + mime-db@1.54.0: {} mime-types@3.0.2: @@ -3179,27 +5117,55 @@ snapshots: framer-motion: 12.38.0 tslib: 2.8.1 + mrmime@2.0.1: {} + ms@2.1.3: {} + nanoid@3.3.12: {} + negotiator@1.0.0: {} + neotraverse@0.6.18: {} + netmask@2.1.1: optional: true + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + node-domexception@1.0.0: {} + node-fetch-native@1.6.7: {} + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + node-mock-http@1.0.4: {} + normalize-path@3.0.0: {} + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + object-assign@4.1.1: {} object-inspect@1.13.4: {} + obug@2.1.1: {} + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + ohash@2.0.11: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -3208,11 +5174,30 @@ snapshots: dependencies: wrappy: 1.0.2 + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + p-limit@7.3.0: + dependencies: + yocto-queue: 1.2.2 + + p-queue@9.2.0: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 7.0.1 + p-retry@4.6.2: dependencies: '@types/retry': 0.12.0 retry: 0.13.1 + p-timeout@7.0.1: {} + pac-proxy-agent@7.2.0: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 @@ -3235,6 +5220,8 @@ snapshots: package-json-from-dist@1.0.1: {} + package-manager-detector@1.6.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -3248,6 +5235,19 @@ snapshots: lines-and-columns: 1.2.4 optional: true + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + parse5@8.0.0: dependencies: entities: 6.0.1 @@ -3270,8 +5270,13 @@ snapshots: pend@1.2.0: optional: true - picocolors@1.1.1: - optional: true + piccolore@0.1.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} pkce-challenge@5.0.1: {} @@ -3283,6 +5288,14 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + postcss@8.5.13: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prismjs@1.30.0: {} + process-nextick-args@2.0.1: {} process@0.11.10: {} @@ -3290,6 +5303,8 @@ snapshots: progress@2.0.3: optional: true + property-information@7.1.0: {} + protobufjs@7.5.6: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -3375,6 +5390,8 @@ snapshots: dependencies: side-channel: 1.1.0 + radix3@1.1.2: {} + range-parser@1.2.1: {} raw-body@3.0.2: @@ -3406,6 +5423,84 @@ snapshots: dependencies: minimatch: 5.1.9 + readdirp@5.0.0: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + require-directory@2.1.1: optional: true @@ -3414,8 +5509,64 @@ snapshots: resolve-from@4.0.0: optional: true + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.1.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + retry@0.13.1: {} + rollup@4.60.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.2 + '@rollup/rollup-android-arm64': 4.60.2 + '@rollup/rollup-darwin-arm64': 4.60.2 + '@rollup/rollup-darwin-x64': 4.60.2 + '@rollup/rollup-freebsd-arm64': 4.60.2 + '@rollup/rollup-freebsd-x64': 4.60.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.2 + '@rollup/rollup-linux-arm-musleabihf': 4.60.2 + '@rollup/rollup-linux-arm64-gnu': 4.60.2 + '@rollup/rollup-linux-arm64-musl': 4.60.2 + '@rollup/rollup-linux-loong64-gnu': 4.60.2 + '@rollup/rollup-linux-loong64-musl': 4.60.2 + '@rollup/rollup-linux-ppc64-gnu': 4.60.2 + '@rollup/rollup-linux-ppc64-musl': 4.60.2 + '@rollup/rollup-linux-riscv64-gnu': 4.60.2 + '@rollup/rollup-linux-riscv64-musl': 4.60.2 + '@rollup/rollup-linux-s390x-gnu': 4.60.2 + '@rollup/rollup-linux-x64-gnu': 4.60.2 + '@rollup/rollup-linux-x64-musl': 4.60.2 + '@rollup/rollup-openbsd-x64': 4.60.2 + '@rollup/rollup-openharmony-arm64': 4.60.2 + '@rollup/rollup-win32-arm64-msvc': 4.60.2 + '@rollup/rollup-win32-ia32-msvc': 4.60.2 + '@rollup/rollup-win32-x64-gnu': 4.60.2 + '@rollup/rollup-win32-x64-msvc': 4.60.2 + fsevents: 2.3.3 + router@2.2.0: dependencies: debug: 4.4.3 @@ -3432,6 +5583,8 @@ snapshots: safer-buffer@2.1.2: {} + sax@1.6.0: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -3502,6 +5655,17 @@ snapshots: shebang-regex@3.0.0: {} + shiki@4.0.2: + dependencies: + '@shikijs/core': 4.0.2 + '@shikijs/engine-javascript': 4.0.2 + '@shikijs/engine-oniguruma': 4.0.2 + '@shikijs/langs': 4.0.2 + '@shikijs/themes': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -3532,9 +5696,13 @@ snapshots: signal-exit@4.1.0: {} + sisteransi@1.0.5: {} + smart-buffer@4.2.0: optional: true + smol-toml@1.6.1: {} + socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.4 @@ -3555,6 +5723,8 @@ snapshots: source-map@0.6.1: optional: true + space-separated-tokens@2.0.2: {} + statuses@2.0.2: {} streamx@2.25.0: @@ -3586,6 +5756,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -3596,6 +5771,16 @@ snapshots: supports-color@10.2.2: {} + svgo@4.0.1: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.2.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + symbol-tree@3.2.4: {} tar-fs@3.1.2: @@ -3635,6 +5820,17 @@ snapshots: transitivePeerDependencies: - react-native-b4a + tiny-inflate@1.0.3: {} + + tinyclip@0.1.12: {} + + tinyexec@1.1.2: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + tldts-core@7.0.28: {} tldts@7.0.28: @@ -3651,8 +5847,14 @@ snapshots: dependencies: punycode: 2.3.1 + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-algebra@2.0.0: {} + tsconfck@3.1.6: {} + tslib@2.8.1: {} type-is@2.0.1: @@ -3664,6 +5866,12 @@ snapshots: typed-query-selector@2.12.1: optional: true + ufo@1.6.4: {} + + ultrahtml@1.6.0: {} + + uncrypto@0.1.3: {} + undici-types@7.19.2: {} undici@7.24.7: {} @@ -3674,16 +5882,118 @@ snapshots: dependencies: pathe: 2.0.3 + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unifont@0.7.4: + dependencies: + css-tree: 3.2.1 + ofetch: 1.5.1 + ohash: 2.0.11 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + unpipe@1.0.0: {} + unstorage@1.17.5: + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.3.3 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + util-deprecate@1.0.2: {} vary@1.1.2: {} + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@7.3.2(@types/node@25.6.0): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.13 + rollup: 4.60.2 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 25.6.0 + fsevents: 2.3.3 + + vitefu@1.1.3(vite@7.3.2(@types/node@25.6.0)): + optionalDependencies: + vite: 7.3.2(@types/node@25.6.0) + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 + web-namespaces@2.0.1: {} + web-streams-polyfill@3.3.3: {} webdriver-bidi-protocol@0.4.1: @@ -3701,6 +6011,8 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + which-pm-runs@1.1.0: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -3751,12 +6063,16 @@ snapshots: xmlchars@2.2.0: {} + xxhash-wasm@1.1.0: {} + y18n@5.0.8: optional: true yargs-parser@21.1.1: optional: true + yargs-parser@22.0.0: {} + yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -3774,6 +6090,8 @@ snapshots: fd-slicer: 1.1.0 optional: true + yocto-queue@1.2.2: {} + youch-core@0.3.3: dependencies: '@poppinss/exception': 1.2.3 @@ -3801,3 +6119,5 @@ snapshots: optional: true zod@4.3.6: {} + + zwitch@2.0.4: {} diff --git a/public/antipattern-examples/new-slop-2026.html b/public/antipattern-examples/new-slop-2026.html new file mode 100644 index 000000000..8896374fa --- /dev/null +++ b/public/antipattern-examples/new-slop-2026.html @@ -0,0 +1,310 @@ + + + + + +Lumina — Intelligent workflows for modern product teams + + + + + + + + + + + + + + +
+
AI-native workflows
+

Build faster with intelligent workflows for modern teams

+

The all-in-one platform that helps product teams ship better software in record time. Powered by AI, designed for humans.

+ +
Trusted by 10,000+ forward-thinking teams worldwide
+
+ + + + + +
+
+ Northwind + Halcyon + Meridian + Fieldnote + Atlas & Co. + Verity +
+
+ +
+
+

Everything you need to ship great products

+

One platform. Endless possibilities. Built for teams who care about craft.

+
+ +
+
+
+

Lightning Fast

+

Move from idea to production in minutes, not months. Our AI handles the heavy lifting so you can focus on what matters.

+
+
+
🔒
+

Enterprise Secure

+

SOC 2 compliant infrastructure with end-to-end encryption. Your data is safe, always.

+
+
+
📈
+

Built to Scale

+

From your first prototype to millions of users — Lumina grows with your team without breaking a sweat.

+
+
+ +
+ + +
+

Ready to transform how your team builds?

+

Join thousands of teams already shipping faster with Lumina.

+ Start your free trial → +
+ + +
+ © 2026 Lumina, Inc. — Crafted with care in San Francisco. +
+ + + + diff --git a/public/antipattern-examples/old-slop-2022.html b/public/antipattern-examples/old-slop-2022.html new file mode 100644 index 000000000..4b730346a --- /dev/null +++ b/public/antipattern-examples/old-slop-2022.html @@ -0,0 +1,351 @@ + + + + + +Lumina — The future of intelligent workflows + + + + + + + + + +
+
🚀 Powered by AI
+

Build faster with intelligent workflows

+

The all-in-one platform that helps modern teams ship better products in record time. Powered by AI, designed for humans.

+ +
Backed by top-tier investors
+
+ +
+
+ Northwind + Halcyon + Meridian + Fieldnote + Atlas & Co. + Verity +
+
+ +
+
+

Everything you need to ship faster

+

One platform. Endless possibilities. Built for the future.

+
+
+
+
+

Lightning Fast

+

Move from idea to production in minutes, not months. Our AI handles the heavy lifting so you can focus on what matters.

+
+
+
🔒
+

Enterprise Secure

+

SOC 2 compliant infrastructure with end-to-end encryption. Your data is safe, always.

+
+
+
📈
+

Built to Scale

+

From your first prototype to millions of users, Lumina grows with your team without breaking a sweat.

+
+
+
+ +
+

Ready to transform how your team builds?

+

Join thousands of teams already shipping faster with Lumina.

+ Get early access → +
+ +
+ © 2022 Lumina, Inc. Powered by AI in San Francisco. +
+ + + diff --git a/public/cases/neo-mirai/index.html b/public/cases/neo-mirai/index.html deleted file mode 100644 index 55d24bf40..000000000 --- a/public/cases/neo-mirai/index.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - Neo Mirai case study | Impeccable - - - - - - - - - - - - - -
-
-
- Designing with Impeccable - Case study -

Neo Mirai: generated mock to shipped page.

-

A retro-futurist AI design conference became a real static site through the full Impeccable loop: visual reference, brand direction, implementation, asset regeneration, responsive fixes, animation polish, and browser verification.

- -
- - The shipped Neo Mirai conference website hero and agenda, showing a warm retro-futurist Tokyo skyline and large NEO MIRAI headline. - -
- -
-
- 01 · Visualize - Generated hi-fi website mock for the Neo Mirai conference page. -
Hi-fi north star. A concrete composition to build toward, not a paragraph to interpret.
-
-
- 02 · Shape - Generated Neo Mirai brand toolkit plate with palette, typography, symbols, and application mockups. -
Brand toolkit. Identity, palette, type, image language, and motion direction before code.
-
-
- 03 · Ship - - Full-page screenshot of the implemented Neo Mirai website. - -
Implemented page. Semantic HTML, responsive layout, regenerated assets, real states, and polish.
-
-
- -
-
- -

The mock did not become a screenshot. It became a system.

-
-
-
-

Composition matching

-

The build preserved the mock's asymmetric rhythm: full-bleed hero artwork, dark agenda block, orange manifesto band, drifting installation grid, and structured ticket field.

-
-
-

Asset regeneration

-

Image-native pieces stayed image-native. The manifesto city, speaker portraits, pine overlay, and supporting illustrations were regenerated or isolated where raster detail mattered.

-
-
-

Browser iteration

-

The page was tested in the browser after each pass. Overlaps, bad crops, active nav state, speaker carousel behavior, mobile heights, and large-viewport balance were fixed visually.

-
-
-
- -
-
- Generated hi-fi Neo Mirai page mock. -
North-star mock The reference image preserved the intended rhythm: full-bleed hero, dark agenda, speaker carousel, installation field, manifesto, and tickets.
-
-
- - Full-page screenshot of the implemented Neo Mirai website. - -
Live build The shipped page keeps the same visual ambition while becoming responsive markup, real links, carousel controls, hover states, and browser-tested layout.
-
-
- -
-
- -

Use craft when the output has to feel designed.

-

/impeccable craft is the right command when a feature needs shaping, visual direction, implementation, and browser iteration in one run.

-
-
/impeccable craft retro-futurist AI design conference website
-
-
- - - - diff --git a/public/live-mode/index.html b/public/live-mode/index.html deleted file mode 100644 index 1082b269b..000000000 --- a/public/live-mode/index.html +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - Live Mode | Impeccable - - - - - - - - - - - - - - - - -
- -
-
-

New in v3.0 Alpha

-

Live Mode

-

Pick any element in the browser. Drop a comment or a stroke. Three production-quality variants swap in via your framework's HMR. Accept the one you want and it writes back to source.

-

Why alpha: Live Mode works end-to-end and is ready to try, but it still needs more testing against real-world repos and framework configs. Expect rough edges on uncommon setups, and please report what breaks.

-
- $ - /impeccable live - -
-
- -
- -
-
-
- - - - localhost:3000 -
- -
- - -
-
-
- Newsletter -

Subscribe for updates

-

Monthly-ish design notes.

- -
-
-
-
- No. 04 -

Letters, occasionally.

-

A postcard from the editor, about once a month. No tracking pixels, no "just checking in."

- -
-
-
-
-
- Dispatch -

Design notes,
every other
Thursday.

- -
-
-
-
-
- Field Notes -

A monthly letter, for people who still read email for pleasure.

- -
-
-
- - - - - -
-
- - - - - - -
-
- - Generating variants… -
-
- - 1 / 3 - - - - -
-
- - Variant 3 written to source -
-
- - -
- -
- / - - - - - -
-
-
-
-

Click the frame or scroll it into view to start the loop. Respects prefers-reduced-motion.

-
- -
-

What happens, in three moves

-
-
- 01 · Pick -

Point at what bugs you

-

Click any element on your running dev server. Add a comment pin where the issue lives. Draw a stroke through the bit you want to change. Or just type "more playful".

-
-
-
- Newsletter card - 1 -
-
-
-
-
- 02 · Generate -

Three genuinely different takes

-

Variants anchor to different archetypes, not three riffs on color. Each one explores a different primary axis: hierarchy, typography, density, layout, or palette strategy.

-
-
-
No.04
-
Dispatch
-
Field
-
-
-
-
- 03 · Accept -

Lands in real source

-

The accepted variant replaces the picked element in your source file. CSS consolidates into your real stylesheet, not inline. Discard all three and the original stays.

-
- Variant 2 written to source -
-
-
-
- -
-

Where next

- -
- -
- Supported dev servers -
    -
  • Vite
  • -
  • Next.js (incl. monorepos)
  • -
  • SvelteKit
  • -
  • Astro
  • -
  • Nuxt
  • -
  • Bun
  • -
  • Plain static HTML
  • -
-
-
-
- - - diff --git a/public/privacy.html b/public/privacy.html deleted file mode 100644 index 44b2853aa..000000000 --- a/public/privacy.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - Privacy Policy - Impeccable - - - - - - - - - - - - -
-

Privacy Policy

-

Last updated: April 6, 2026

- -

What Impeccable is

-

Impeccable is an open-source collection of agent skills (text files) that run locally in your AI coding tool. The skills themselves collect no data, make no network requests, and have no analytics.

- -

Website analytics

-

The Impeccable website (impeccable.style) uses Google Analytics to understand traffic patterns (page views, referrers, country). No personal information is collected beyond what Google Analytics provides by default. No cookies are used for advertising.

- -

Downloads

-

When you download a skill bundle from the website, we log the download event (which bundle, timestamp) for usage statistics. No personal information is attached to these logs.

- -

Claude Code Plugin

-

When installed as a Claude Code plugin, Impeccable runs entirely within your local Claude Code session. No data is sent to Impeccable's servers. Anthropic's own privacy policy governs the Claude Code application itself.

- -

Chrome Extension

-

The Impeccable Chrome DevTools extension runs entirely in your browser. All anti-pattern detection happens locally on the page you are inspecting. No page content, URLs, or detection results are ever sent to any external server.

-

The extension stores your rule preferences (which detections are enabled or disabled) using Chrome's built-in sync storage (chrome.storage.sync), which syncs settings across your Chrome instances via your Google account. No other data is stored or transmitted.

-

The extension requests the following permissions:

-
    -
  • activeTab / scripting - to inject the detector script into the page you are inspecting
  • -
  • storage - to save your rule preferences
  • -
  • webNavigation - to re-scan automatically when you navigate to a new page
  • -
  • Host permissions (all URLs) - so the detector can run on any website you choose to inspect
  • -
- -

GitHub

-

The source code is hosted on GitHub. Interactions with the repository (issues, pull requests, stars) are governed by GitHub's privacy policy.

- -

Contact

-

Questions about this policy? Open an issue on GitHub or reach out to @pbakaus.

-
- - diff --git a/scripts/build-sub-pages.js b/scripts/build-sub-pages.js deleted file mode 100644 index 671792434..000000000 --- a/scripts/build-sub-pages.js +++ /dev/null @@ -1,1532 +0,0 @@ -/** - * Generate static HTML files for /docs, /slop, /tutorials, /live-mode, - * /designing. - * - * Called from both scripts/build.js (before buildStaticSite) and - * server/index.js (at module load), so dev and prod share the same - * code path and output shape. - * - * Output lives under public/docs/, public/slop/, public/tutorials/, all - * gitignored. Bun's HTML loader picks them up the same way it picks up - * the hand-authored pages. - */ - -import fs from 'node:fs'; -import path from 'node:path'; -import { - buildSubPageData, - CATEGORY_ORDER, - CATEGORY_LABELS, - CATEGORY_DESCRIPTIONS, - COMMAND_RELATIONSHIPS, - LAYER_LABELS, - LAYER_DESCRIPTIONS, - GALLERY_ITEMS, -} from './lib/sub-pages-data.js'; -import { renderMarkdown, slugify } from './lib/render-markdown.js'; -import { renderPage } from './lib/render-page.js'; - -function escapeHtml(str) { - return String(str || '') - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -/** - * Render the before/after split-compare demo block for a skill. - * Returns '' when the skill has no demo data (e.g. /shape). - */ -function renderSkillDemo(skill) { - if (!skill.demo) return ''; - const { before, after, caption } = skill.demo; - return ` -
-
-

Drag or hover to compare

-
-
-
${before}
-
-
-
${after || before}
-
-
-
-
- Before - ${caption ? `

${escapeHtml(caption)}

` : ''} - After -
-
-
`; -} - -function renderCraftCaseCallout() { - return ` -`; -} - -/** - * Render one skill detail page HTML body (without the site shell). - */ -function renderSkillDetail(skill, knownSkillIds) { - const bodyHtml = renderMarkdown(skill.body, { - knownSkillIds, - currentSkillId: skill.id, - }); - - const editorialHtml = skill.editorial - ? renderMarkdown(skill.editorial.body, { knownSkillIds, currentSkillId: skill.id }) - : ''; - - const demoHtml = renderSkillDemo(skill); - const caseCalloutHtml = skill.id === 'craft' ? renderCraftCaseCallout() : ''; - - const tagline = skill.editorial?.frontmatter?.tagline || skill.description; - const categoryLabel = CATEGORY_LABELS[skill.category] || skill.category; - - // Reference files as collapsible
blocks - let referencesHtml = ''; - if (skill.references && skill.references.length > 0) { - const refs = skill.references - .map((ref) => { - const slug = slugify(ref.name); - const refBody = renderMarkdown(ref.content, { - knownSkillIds, - currentSkillId: skill.id, - }); - const title = ref.name - .split('-') - .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) - .join(' '); - return ` -
- Reference${escapeHtml(title)} -
-${refBody} -
-
`; - }) - .join('\n'); - referencesHtml = ` -
-

Deeper reference

- ${refs} -
`; - } - - const metaStrip = ` -
- ${escapeHtml(categoryLabel)} - User-invocable - ${skill.argumentHint ? `${escapeHtml(skill.argumentHint)}` : ''} -
`; - - const hasDemo = demoHtml.trim().length > 0; - - // Sub-commands are accessed via /impeccable . Show "/impeccable" as - // a smaller namespace label above the command name, matching the magazine - // spread treatment so the command name stays at full display size. - const titleHtml = skill.isSubCommand - ? `/impeccable${escapeHtml(skill.id)}` - : `/${escapeHtml(skill.id)}`; - - return ` -
-
-
-

Docs / ${escapeHtml(categoryLabel)}

-

${titleHtml}

-

${escapeHtml(tagline)}

- ${metaStrip} -
- ${demoHtml} -
- - ${editorialHtml ? `
\n${editorialHtml}\n
` : ''} - - ${caseCalloutHtml} - -
-
- ${skill.isSubCommand ? 'reference/' + escapeHtml(skill.id) + '.md' : 'SKILL.md'} - ${skill.isSubCommand ? 'Loaded when the impeccable skill routes to this command.' : 'The canonical skill definition your AI harness loads.'} -
-
-${bodyHtml} -
-
- - ${referencesHtml} -
-`; -} - -/** - * Render the unified Docs sidebar used across /skills and /tutorials. - * Shows every skill grouped by category, then tutorials as a final - * group. Pass the current page identifier so we can mark it: - * - * { kind: 'skill', id: 'polish' } - * { kind: 'tutorial', slug: 'getting-started' } - * null (no current page) - */ -function renderDocsSidebar(skillsByCategory, tutorials, current = null) { - // Label the toggle button with the current page so mobile users know - // where they are at a glance, then open the menu to switch. - let currentLabel = 'Docs menu'; - if (current?.kind === 'skill') { - currentLabel = `/${current.id}`; - } else if (current?.kind === 'tutorial') { - const t = tutorials.find((x) => x.slug === current.slug); - if (t) currentLabel = t.title; - } - - let html = ` -`; - return html; -} - -/** - * Render the /skills overview main column content (not the sidebar). - * This is the orientation piece: what skills are, how to pick one, - * the six categories explained with inline cross-links to detail pages. - */ -function renderSkillsOverviewMain(skillsByCategory, allSkills) { - // Build a lookup by id so we can pull taglines/descriptions for the cards. - const skillsById = Object.fromEntries(allSkills.map((s) => [s.id, s])); - const commandCount = allSkills.filter((s) => s.id !== 'impeccable').length; - - // Short, clean tagline for the home command card. Fall back to the editorial - // tagline if set, otherwise use a default. - const impeccable = skillsById['impeccable']; - const homeTagline = impeccable?.editorial?.frontmatter?.tagline - || 'The design intelligence behind every command.'; - - // Render a single command as a compact row: name on the left, description - // and relationship on the right. Matches the original cheatsheet density. - const renderCommandRow = (skill) => { - const tagline = skill.editorial?.frontmatter?.tagline || skill.description; - const shortTagline = tagline.length > 140 ? tagline.slice(0, 137) + '...' : tagline; - const rel = COMMAND_RELATIONSHIPS[skill.id] || {}; - const isAlpha = skill.id === 'live'; - - let metaHtml = ''; - if (rel.pairs) { - metaHtml = `
Pairs with ${rel.pairs}
`; - } else if (rel.leadsTo?.length) { - const links = rel.leadsTo.map((c) => `${c}`).join(', '); - metaHtml = `
Leads to ${links}
`; - } else if (rel.combinesWith?.length) { - const links = rel.combinesWith.map((c) => `${c}`).join(', '); - metaHtml = `
Combines with ${links}
`; - } - - // Row is a
(not an ) so the inner relationship links are valid. - // The name on the left is the primary link target. - return ` -
-
- /impeccable ${escapeHtml(skill.id)}${isAlpha ? ' ALPHA' : ''} -
-
-

${escapeHtml(shortTagline)}

- ${metaHtml} -
-
`; - }; - - // Render category sections, skipping the Create category's /impeccable root - // (shown as a hero card above) but keeping everything else in place. - let categoriesHtml = ''; - for (const category of CATEGORY_ORDER) { - const list = (skillsByCategory[category] || []).filter((s) => s.id !== 'impeccable'); - if (list.length === 0) continue; - - const rowsHtml = list.map(renderCommandRow).join(''); - - categoriesHtml += ` -
-
-
-

${escapeHtml(CATEGORY_LABELS[category])}

-

${escapeHtml(CATEGORY_DESCRIPTIONS[category])}

-
- ${list.length} ${list.length === 1 ? 'command' : 'commands'} -
-
-${rowsHtml} -
-
-`; - } - - return ` -
-
-

1 skill · ${commandCount} commands

-

Commands

-

Impeccable gives you a shared design vocabulary with your AI. ${commandCount} commands that each encode a specific design discipline, so you can steer with precision. Pick one for the job or let the skill route you automatically.

-
- -
-
- The home command -

/impeccable

-

${escapeHtml(homeTagline)}

-

Call /impeccable directly for freeform design work with the full guidebook loaded. Or reach for one of its specialized modes:

-
- -
- -
-${categoriesHtml} -
-
`; -} - -/** - * Wrap sidebar + main content in the docs-browser layout shell. - */ -function wrapInDocsLayout(sidebarHtml, mainHtml) { - return ` -
- ${sidebarHtml} -
-${mainHtml} -
-
`; -} - -/** - * Group anti-pattern rules by skill section. - * Rules without a skillSection fall into a 'General quality' bucket. - */ -function groupRulesBySection(rules) { - // Canonical ordering. Additional sections referenced by rules (e.g. - // 'Interaction', 'Responsive' from LLM-only entries) are appended to - // the end, before 'General quality', so every rule renders. - const primaryOrder = [ - 'Visual Details', - 'Typography', - 'Color & Contrast', - 'Layout & Space', - 'Motion', - 'Interaction', - 'Responsive', - ]; - const bySection = {}; - for (const name of primaryOrder) bySection[name] = []; - bySection['General quality'] = []; - - for (const rule of rules) { - const section = rule.skillSection || 'General quality'; - if (!bySection[section]) bySection[section] = []; - bySection[section].push(rule); - } - - // Sort each bucket: slop first (they're the named tells), then quality. - for (const name of Object.keys(bySection)) { - bySection[name].sort((a, b) => { - if (a.category !== b.category) return a.category === 'slop' ? -1 : 1; - return a.name.localeCompare(b.name); - }); - } - - // Final render order: primary sections first, then any extras that - // rules introduced, then General quality last. - const order = [...primaryOrder]; - for (const name of Object.keys(bySection)) { - if (!order.includes(name) && name !== 'General quality') { - order.push(name); - } - } - order.push('General quality'); - - return { order, bySection }; -} - -/** - * Render the /slop sidebar: a table of contents for the four top-level - * sections (See it / Try it live / The catalog / Run it yourself), with - * the catalog's per-section anchors nested under "The catalog". - */ -function renderSlopSidebar(grouped, gallerySize) { - const catalogEntries = grouped.order - .filter((section) => grouped.bySection[section]?.length > 0) - .map((section) => { - const slug = slugify(section); - const count = grouped.bySection[section].length; - return `
  • ${escapeHtml(section)}${count}
  • `; - }) - .join('\n'); - - const catalogTotal = grouped.order - .reduce((sum, s) => sum + (grouped.bySection[s]?.length || 0), 0); - - return ` -`; -} - -/** - * Render one rule card inside the anti-patterns main column. - */ -function renderRuleCard(rule) { - const categoryLabel = rule.category === 'slop' ? 'AI slop' : 'Quality'; - const layer = rule.layer || 'cli'; - const layerLabel = LAYER_LABELS[layer] || layer; - const layerTitle = LAYER_DESCRIPTIONS[layer] || ''; - const skillLink = rule.skillSection - ? `See in /impeccable` - : ''; - const visual = rule.visual - ? `` - : ''; - return ` -
    - ${visual} -
    -
    - ${categoryLabel} - ${escapeHtml(layerLabel)} -
    -

    ${escapeHtml(rule.name)}

    -

    ${escapeHtml(rule.description)}

    - ${skillLink} -
    -
    `; -} - -function escapeAttr(str) { - return String(str || '').replace(/"/g, '"'); -} - -/** - * Render the /tutorials index main content. - */ -function renderTutorialsIndexMain(tutorials) { - const cards = tutorials - .map( - (t) => ` - - ${String(t.order).padStart(2, '0')} -
    -

    ${escapeHtml(t.title)}

    -

    ${escapeHtml(t.tagline || t.description)}

    -
    - -
    `, - ) - .join('\n'); - - return ` -
    -
    -

    ${tutorials.length} walk-throughs

    -

    Tutorials

    -

    Short, opinionated walk-throughs of the highest-leverage workflows. Each one takes around ten minutes and ends with something working in your project.

    -
    - -
    -${cards} -
    -
    `; -} - -/** - * Render the /slop page main content. - * - * Four numbered sections in one scroll: See it (iframe overlay demo), - * Try it live (specimen gallery), The catalog (the full rule list), and - * Run it yourself (three invocation methods). Lives inside the docs - * layout shell so the slop sidebar (renderSlopSidebar) navigates both - * the top-level anchors and the per-catalog-section anchors. - */ -function renderSlopMain(grouped, totalRules) { - // Catalog: rule-card sections, grouped by skillSection. - let catalogSectionsHtml = ''; - for (const section of grouped.order) { - const rules = grouped.bySection[section] || []; - if (rules.length === 0) continue; - const slug = slugify(section); - catalogSectionsHtml += ` -
    -
    -

    ${escapeHtml(section)}

    -

    ${rules.length} ${rules.length === 1 ? 'rule' : 'rules'}

    -
    -
    -${rules.map(renderRuleCard).join('\n')} -
    -
    `; - } - - const detectedCount = grouped.order - .flatMap((s) => grouped.bySection[s] || []) - .filter((r) => r.layer !== 'llm').length; - const llmCount = totalRules - detectedCount; - - const specimenCards = GALLERY_ITEMS.map( - (item) => ` - - - - `, - ).join('\n'); - - return ` -
    -
    -

    The visible tells of AI design

    -

    Slop

    -

    ${totalRules} patterns that mark an interface as AI-generated, and the detection overlay that catches them in place. Watch it flag them live, try it on ${GALLERY_ITEMS.length} synthetic specimens, or browse the full catalog. ${detectedCount} rules run deterministically (npx impeccable detect or the browser extension); ${llmCount} need /impeccable critique's LLM review pass.

    -
    - -
    -

    01 See it

    -
    -
    - - - - Live on a synthetic slop page -
    - -
    -

    Hover or tap any outlined element to see which rule fired.

    -
    - - - -
    -
    -

    03 The catalog

    -

    Every pattern /impeccable teaches against. AI slop rules flag the tells of AI-generated UIs; Quality rules flag general design mistakes that hurt regardless of who wrote them.

    -
    - -
    - - How to read this - - -
    -

    Each rule shows how it is detected:

    -
    -
    CLI
    Deterministic. Runs from npx impeccable detect on files, no browser required.
    -
    Browser
    Deterministic, but needs real browser layout. Runs via the browser extension or Puppeteer, not the plain CLI.
    -
    LLM only
    No deterministic detector. Caught by /impeccable critique during its LLM design review.
    -
    -
    -
    - -
    -${catalogSectionsHtml} -
    -
    - -
    -

    04 Run it yourself

    -
    -
    -

    Inside /impeccable critique

    -

    /impeccable critique

    -

    The design review command opens the overlay automatically during its browser assessment pass. Deterministic findings highlighted in place while the LLM runs its separate heuristic review.

    -
    -
    -

    Standalone CLI

    -

    npx impeccable live

    -

    Starts a local server that serves the detector script. Inject it into any page via a <script> tag to see the overlay. Works on your own dev server, a staging URL, or anyone's live page.

    -
    - -
    -
    -
    `; -} - -/** - * Render the animated Live Mode demo block. HTML structure matches the - * homepage #live-demo exactly so public/js/components/live-demo.js can - * drive it without modification. The CSS (.live-demo-*) lives in - * public/css/live-mode.css, imported by main.css and also loaded on this - * page via extraHead. - */ -function renderLiveModeDemo() { - return ` -
    -
    -
    - - - - localhost:3000 -
    - -
    - - -
    -
    -
    - Newsletter -

    Subscribe for updates

    -

    Monthly-ish design notes.

    - -
    -
    -
    -
    - No. 04 -

    Letters, occasionally.

    -

    A postcard from the editor, about once a month. No tracking pixels, no "just checking in."

    - -
    -
    -
    -
    -
    - Dispatch -

    Design notes,
    every other
    Thursday.

    - -
    -
    -
    -
    -
    - Field Notes -

    A monthly letter, for people who still read email for pleasure.

    - -
    -
    -
    - - - - - -
    -
    - - - - - - -
    -
    - - Generating variants… -
    -
    - - 1 / 3 - - - - -
    -
    - - Variant 3 written to source -
    -
    - - -
    - -
    - / - - - - - -
    -
    -
    -
    `; -} - -/** - * Render the /live-mode page main content. - * - * Marketing-style single-column layout mirroring /visual-mode's structure. - * Surfaces the animated homepage live-demo, a three-stage narrative, - * pathway cards (tutorial / reference / install), and the supported - * framework strip. - */ -function renderLiveModeMain() { - return ` -
    -
    -

    New in v3.0 Alpha

    -

    Live Mode

    -

    Pick any element in the browser. Drop a comment or a stroke. Three production-quality variants swap in via your framework's HMR. Accept the one you want and it writes back to source.

    -

    Why alpha: Live Mode works end-to-end and is ready to try, but it still needs more testing against real-world repos and framework configs. Expect rough edges on uncommon setups, and please report what breaks.

    -
    - $ - /impeccable live - -
    -
    - -
    - ${renderLiveModeDemo()} -

    Click the frame or scroll it into view to start the loop. Respects prefers-reduced-motion.

    -
    - -
    -

    What happens, in three moves

    -
    -
    - 01 · Pick -

    Point at what bugs you

    -

    Click any element on your running dev server. Add a comment pin where the issue lives. Draw a stroke through the bit you want to change. Or just type "more playful".

    -
    -
    -
    - Newsletter card - 1 -
    -
    -
    -
    -
    - 02 · Generate -

    Three genuinely different takes

    -

    Variants anchor to different archetypes, not three riffs on color. Each one explores a different primary axis: hierarchy, typography, density, layout, or palette strategy.

    -
    -
    -
    No.04
    -
    Dispatch
    -
    Field
    -
    -
    -
    -
    - 03 · Accept -

    Lands in real source

    -

    The accepted variant replaces the picked element in your source file. CSS consolidates into your real stylesheet, not inline. Discard all three and the original stays.

    -
    - Variant 2 written to source -
    -
    -
    -
    - -
    -

    Where next

    - -
    - -
    - Supported dev servers -
      -
    • Vite
    • -
    • Next.js (incl. monorepos)
    • -
    • SvelteKit
    • -
    • Astro
    • -
    • Nuxt
    • -
    • Bun
    • -
    • Plain static HTML
    • -
    -
    -
    `; -} - -/** -/** - * Render the /designing page. - * - * Editorial orientation: four-phase loop (start → iterate → polish → - * maintain) as the spine, plus three appendix sections (register, - * interop, avoid) and a CTA climax. Cards are rare: most sections - * rely on typography, hairline rules, and whitespace for structure. - */ -function renderDesigningMain() { - const loopNodes = [ - { id: 'start', num: '01', name: 'Start', hint: 'From a blank file, through a brief, to a designed feature.' }, - { id: 'iterate', num: '02', name: 'Iterate', hint: 'Refine in place. Command line or in the browser.' }, - { id: 'polish', num: '03', name: 'Polish', hint: 'The pre-ship gauntlet. Audit, clarify, harden.' }, - { id: 'maintain', num: '04', name: 'Maintain', hint: 'Pay down design debt before it solidifies.' }, - ]; - const nodeHtml = loopNodes.map((n) => ` - - ${n.num} - ${n.name} - ${escapeHtml(n.hint)} - `).join(''); - - return ` -
    -
    -
    - The core loop -

    Designing with Impeccable

    -

    Shipping a real interface is a loop. Four phases, each with one place to begin.

    -
    - -
    - The core loop -
    - ${nodeHtml} - -
    -
    -
    - -
    -
    - 01 · Start -

    From a blank file to a designed feature.

    -

    Three commands, one arc. /impeccable teach writes the brief, once per project. /impeccable shape drafts a reference you can look at. /impeccable craft codes toward what you can see. Words, then pictures, then code.

    - -
    - -
    -
    - teach · in words -
    -
    -
    - PRODUCT.md - Written by teach -
    -
    -
    - Register - Product. Design serves the task. -
    -
    - Users - SREs on call, reading fast, often in the dark. -
    -
    - Voice - Calm, clinical, no hype. -
    -
    - Anti-references - Purple gradients. Glassmorphism. Hype. -
    -
    -
    -
    -

    Teach runs a short discovery interview about audience, register, voice, and anti-references. It writes PRODUCT.md and, if there's code to scan, a DESIGN.md. Every later command reads both files before generating.

    -
    -
    -
    - -
    - shape + craft · in pictures -

    Since image generation crossed the reference-quality threshold, shape can draft a brand toolkit you review at a glance, and craft can code toward a hi-fi mock instead of a paragraph. Neo Mirai is the full loop: generated direction, implemented page, browser iteration.

    - -
    -
    - - Auto-generated brand toolkit plate: identity lockups, colour palette, type specimens, icon system, and application mocks for a fictional AI design conference, rendered in warm earth tones. - -
    - Shape -

    Brand toolkit. Identity, palette, type, icon language, applications, social tiles, UI direction. One plate, reviewable at a glance. Approved decisions get written into DESIGN.md.

    -
    -
    - -
    - - Auto-generated hi-fi landing-page mock: a long vertical editorial comp for a fictional Tokyo AI design conference, in warm earth tones with committed serif display type. - -
    - Visualize -

    Hi-fi reference. The destination, before the first line of CSS. Craft codes toward a concrete image, not an abstract brief. That is the step change.

    -
    -
    - -
    - - Full-page screenshot of the implemented Neo Mirai website. - -
    - Ship -

    Live build. The mock became semantic markup, regenerated assets, responsive fixes, nav state, speaker carousel behavior, and browser-verified polish. Open the live site.

    -
    -
    -
    - -

    The first two plates were generated by OpenAI GPT Image 2. The third is the implemented Neo Mirai page. Gemini Nano Banana Pro, Imagen 4 Ultra, and Grok Imagen work the same way, via Codex, Gemini CLI, and compatible harnesses.

    -
    -
    -
    - -
    -
    - 02 · Iterate -

    Refine what's there.

    -

    Once something exists, you're iterating. There are two paths: specific commands for named dimensions, or Live Mode for visual exploration.

    -
    - -
    -
    -
    - Command line -

    When the edit has a name.

    -

    Type a command and let the skill encode a specific discipline. Best when you know the word: typography, layout, color, motion.

    - -
    - -
    - Live Mode -

    When the edit is easier to point at.

    -

    Pick any element in the browser, draw, type, hit Go. Three production-quality variants. Accept one and it writes to source.

    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    When to reach for which
    Fix something "off" that you can't name/impeccable live
    Apply a specific discipline: type, layout, color, motion/typeset · /layout · /colorize · /animate
    Explore three directions side by side/impeccable live
    Ask "is this any good?"/impeccable critique
    Bring a safe design to life, or tone a shouting one down/bolder · /quieter
    -
    -
    - -
    -
    - 03 · Polish -

    The pre-ship gauntlet.

    -

    Three commands in sequence before anything ships. They don't redesign; they find what still needs to change.

    -
    - -
    -
    -
    - Pre-ship -
    - audit - clarify - harden -
    - 03 · 04 -
    -
    -
    -

    Score it.

    -

    Five dimensions scored 0 to 4: accessibility, performance, theming, responsive, anti-patterns. Findings tagged P0 to P3.

    -
    -
    -

    Rewrite the copy.

    -

    Labels, error messages, empty-state prose, microcopy. Tuned to the audience from PRODUCT.md.

    -
    -
    -

    Stress-test reality.

    -

    60-character names, German product titles, prices in the billions, 500s, offline. Production data is messy.

    -
    -
    -
    -
    -
    - -
    -
    - 04 · Maintain -

    Design debt is real. Pay it down.

    -

    Features ship, drift happens. Two commands close the gap before it solidifies.

    -
    - -
    -
    -
    -
    - -
    -
    - /impeccable extract -

    Consolidate drift.

    -

    Find patterns used three or more times with the same intent. Propose tokens and primitives.

    -
    -
    - -
    -
    - -
    -
    - /impeccable document -

    Re-capture the system.

    -

    Scans tokens, components, and rendered output. Writes a spec-compliant DESIGN.md.

    -
    -
    -
    -
    -
    - -
    -
    - Two lanes -

    Brand, or product.

    -

    Two defaults with different vocabularies. Impeccable picks the lane from your task cue and PRODUCT.md before every command, so typeset, animate, colorize, and friends adjust their output to match. You rarely need to set it by hand.

    -
    - -
    -
    -
    - Brand -

    Design IS the product. Marketing, landing, editorial, long-form, portfolio.

    - -
    -
    - Product -

    Design serves the task. App UI, admin, dashboards, tools.

    - -
    -
    - Read the brand-vs-product tutorial → -
    -
    - -
    -
    - Common mistakes -

    What to avoid.

    -

    An anti-patterns list, for using the anti-patterns tool.

    -
    - -
    -
      -
    • - -
      - Running both Impeccable and Anthropic's frontend-design skill -

      Anthropic still promotes their skill in Claude Code, but it's been unmaintained and is now behind on recommended patterns. Run both and they collide on vocabulary, cancelling each other out. Pick one.

      -
      -
    • -
    • - -
      - Pinning every command -

      Pinning brings back /audit, /polish, /critique as shortcuts. Pin everything and you've re-exploded the / menu the v3.0 consolidation cleaned up. Pin the two or three you reach for daily.

      -
      -
    • -
    • - -
      - Skipping teach -

      Commands still run without PRODUCT.md and DESIGN.md. They default to generic SaaS patterns. The floor is meaningfully higher with context. Run teach once; every later command benefits.

      -
      -
    • -
    • - -
      - Treating it like a linter -

      Impeccable is an opinionated design partner, not a validator. It has a point of view. Push back with a reason and it'll work with you. Ignore the opinion without a reason and output gets worse, not better.

      -
      -
    • -
    -
    -
    - - -
    `; -} - -/** - * Render a tutorial detail page main content. - */ -function renderTutorialDetail(tutorial, knownSkillIds) { - const bodyHtml = renderMarkdown(tutorial.body, { knownSkillIds }); - return ` -
    -
    -

    Tutorials / ${String(tutorial.order).padStart(2, '0')}

    -

    ${escapeHtml(tutorial.title)}

    - ${tutorial.tagline ? `

    ${escapeHtml(tutorial.tagline)}

    ` : ''} -
    - -
    -${bodyHtml} -
    -
    `; -} - -/** - * Entry point. Generates all sub-page HTML files. - * - * @param {string} rootDir - * @returns {Promise<{ files: string[] }>} list of generated file paths (absolute) - */ -export async function generateSubPages(rootDir) { - const data = await buildSubPageData(rootDir); - const outDirs = { - docs: path.join(rootDir, 'public/docs'), - slop: path.join(rootDir, 'public/slop'), - tutorials: path.join(rootDir, 'public/tutorials'), - liveMode: path.join(rootDir, 'public/live-mode'), - designing: path.join(rootDir, 'public/designing'), - }; - // Clean up legacy output dirs from /anti-patterns and /visual-mode, - // which have been merged into /slop. A stray file in either would - // otherwise keep getting served by Bun's static handler. - for (const legacy of ['public/anti-patterns', 'public/visual-mode']) { - const dir = path.join(rootDir, legacy); - if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true }); - } - - // Fresh output dirs each time so stale files don't linger. - for (const dir of Object.values(outDirs)) { - if (fs.existsSync(dir)) fs.rmSync(dir, { recursive: true, force: true }); - fs.mkdirSync(dir, { recursive: true }); - } - - const generated = []; - - // Docs index: the full command reference with rich cards. - { - const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, null); - const main = renderSkillsOverviewMain(data.skillsByCategory, data.skills); - const html = renderPage({ - title: 'Docs | Impeccable', - description: - '22 commands that teach your AI harness how to design. Browse by category: create, evaluate, refine, simplify, harden.', - bodyHtml: wrapInDocsLayout(sidebar, main), - activeNav: 'docs', - canonicalPath: '/docs', - bodyClass: 'sub-page skills-layout-page', - }); - const out = path.join(outDirs.docs, 'index.html'); - fs.writeFileSync(out, html, 'utf-8'); - generated.push(out); - } - - // Per-command detail pages: same docs-browser shell as the overview. - for (const skill of data.skills) { - const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, { kind: 'skill', id: skill.id }); - const main = renderSkillDetail(skill, data.knownSkillIds); - const title = skill.isSubCommand - ? `/impeccable ${skill.id} | Impeccable` - : `/${skill.id} | Impeccable`; - const description = skill.editorial?.frontmatter?.tagline || skill.description; - const html = renderPage({ - title, - description, - bodyHtml: wrapInDocsLayout(sidebar, main), - activeNav: 'docs', - canonicalPath: `/docs/${skill.id}`, - bodyClass: 'sub-page skills-layout-page', - }); - const out = path.join(outDirs.docs, `${skill.id}.html`); - fs.writeFileSync(out, html, 'utf-8'); - generated.push(out); - } - - // Slop: merged anti-patterns catalog + visual-mode overlay demo + gallery. - // Single page, docs-browser shell with a nested TOC sidebar. - { - const grouped = groupRulesBySection(data.rules); - const sidebar = renderSlopSidebar(grouped, GALLERY_ITEMS.length); - const main = renderSlopMain(grouped, data.rules.length); - const html = renderPage({ - title: 'Slop | Impeccable', - description: `${data.rules.length} patterns that mark an interface as AI-generated, plus the live detection overlay that catches them in place. The rule catalog behind npx impeccable detect, the browser extension, and /impeccable critique.`, - bodyHtml: wrapInDocsLayout(sidebar, main), - activeNav: 'slop', - canonicalPath: '/slop', - bodyClass: 'sub-page skills-layout-page slop-page', - }); - const out = path.join(outDirs.slop, 'index.html'); - fs.writeFileSync(out, html, 'utf-8'); - generated.push(out); - } - - // Tutorials index (under the unified Docs umbrella). - if (data.tutorials.length > 0) { - const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, null); - const main = renderTutorialsIndexMain(data.tutorials); - const html = renderPage({ - title: 'Tutorials | Impeccable', - description: `${data.tutorials.length} short, opinionated walk-throughs of the highest-leverage Impeccable workflows.`, - bodyHtml: wrapInDocsLayout(sidebar, main), - activeNav: 'docs', - canonicalPath: '/tutorials', - bodyClass: 'sub-page skills-layout-page tutorials-page', - }); - const out = path.join(outDirs.tutorials, 'index.html'); - fs.writeFileSync(out, html, 'utf-8'); - generated.push(out); - } - - // Live Mode: marketing landing mirroring the other single-column pages. - // Needs live-mode.css (not imported by sub-pages.css to keep the base - // bundle small) and the live-demo JS module to animate the demo. - { - const extraHead = ` - - `; - const html = renderPage({ - title: 'Live Mode | Impeccable', - description: - 'Iterate on UI in the browser. Pick an element, drop a comment, get three production-quality variants, accept one, and it writes back to source. /impeccable live.', - bodyHtml: renderLiveModeMain(), - activeNav: 'live', - canonicalPath: '/live-mode', - bodyClass: 'sub-page live-mode-page-body', - extraHead, - }); - const out = path.join(outDirs.liveMode, 'index.html'); - fs.writeFileSync(out, html, 'utf-8'); - generated.push(out); - } - - // Designing: orientation page about the core loop. - { - const html = renderPage({ - title: 'Designing with Impeccable', - description: - 'The core loop: start, iterate, polish, maintain. How to use Impeccable end-to-end, from a blank file to shipped feature to paid-down design debt.', - bodyHtml: renderDesigningMain(), - activeNav: 'designing', - canonicalPath: '/designing', - bodyClass: 'sub-page designing-page-body', - }); - const out = path.join(outDirs.designing, 'index.html'); - fs.writeFileSync(out, html, 'utf-8'); - generated.push(out); - } - - // Tutorial detail pages. - for (const tutorial of data.tutorials) { - const sidebar = renderDocsSidebar(data.skillsByCategory, data.tutorials, { kind: 'tutorial', slug: tutorial.slug }); - const main = renderTutorialDetail(tutorial, data.knownSkillIds); - const html = renderPage({ - title: `${tutorial.title} | Tutorials | Impeccable`, - description: tutorial.description || tutorial.tagline || '', - bodyHtml: wrapInDocsLayout(sidebar, main), - activeNav: 'docs', - canonicalPath: `/tutorials/${tutorial.slug}`, - bodyClass: 'sub-page skills-layout-page tutorials-page', - }); - const out = path.join(outDirs.tutorials, `${tutorial.slug}.html`); - fs.writeFileSync(out, html, 'utf-8'); - generated.push(out); - } - - return { files: generated }; -} diff --git a/scripts/build.js b/scripts/build.js index b7365ac92..c7bb7f6bc 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -21,7 +21,8 @@ import { fileURLToPath } from 'url'; import { readSourceFiles, readPatterns, stashPerProjectArtifacts, restorePerProjectArtifacts } from './lib/utils.js'; import { createTransformer, PROVIDERS } from './lib/transformers/index.js'; import { createAllZips } from './lib/zip.js'; -import { generateSubPages } from './build-sub-pages.js'; +// Sub-page generation is now handled by Astro content collections. +// import { generateSubPages } from './build-sub-pages.js'; /** * Generate authoritative counts from source data and write to public/js/generated/counts.js. @@ -137,12 +138,10 @@ function validateSkillFrontmatter(skills) { function validateNoEmDashes(rootDir) { const targets = [ 'content/site', - 'public/index.html', - 'public/privacy.html', - 'scripts/build-sub-pages.js', - 'scripts/lib/sub-pages-data.js', + 'site/components', + 'site/layouts', ]; - const extensions = new Set(['.html', '.md', '.js', '.mjs', '.css']); + const extensions = new Set(['.html', '.md', '.js', '.mjs', '.css', '.astro']); const emDashPatterns = [/—/g, /—/gi, /—/gi, /—/gi]; let errors = 0; @@ -188,30 +187,13 @@ function validateNoEmDashes(rootDir) { * * Returns the number of validation errors. Build fails if > 0. */ -function validateSiteHeader(rootDir) { - const pages = [ - 'public/index.html', - 'public/privacy.html', - ]; - const marker = ''; - let errors = 0; - for (const rel of pages) { - const full = path.join(rootDir, rel); - if (!fs.existsSync(full)) { - console.error(` ❌ ${rel} is missing`); - errors++; - continue; - } - const src = fs.readFileSync(full, 'utf-8'); - if (!src.includes(marker)) { - console.error(` ❌ ${rel} is missing the shared site header marker '${marker}'`); - errors++; - } - } - if (errors === 0) { - console.log(`✓ Validated site header on ${pages.length} hand-authored pages`); - } - return errors; +function validateSiteHeader(_rootDir) { + // With Astro, the shared header is a component (site/components/Header.astro). + // There's nothing to validate per-page — the component is imported by Base.astro + // and rendered identically everywhere. This function is kept as a no-op so the + // call site doesn't need to change. + console.log('✓ Site header is a shared Astro component (no per-page validation needed)'); + return 0; } /** @@ -236,11 +218,10 @@ const __dirname = path.dirname(__filename); const ROOT_DIR = path.resolve(__dirname, '..'); const DIST_DIR = path.join(ROOT_DIR, 'dist'); -/** - * Build static site using Bun's HTML bundler - * Bun's HTML loader resolves and inlines CSS @imports. - */ -async function buildStaticSite(extraEntrypoints = []) { +// buildStaticSite (Bun HTML bundler) removed — now handled by Astro. + +// Placeholder so the line-number-based edits below don't shift. +async function _REMOVED() { const entrypoints = [ path.join(ROOT_DIR, 'public', 'index.html'), path.join(ROOT_DIR, 'public', 'privacy.html'), @@ -563,43 +544,21 @@ function generateCFConfig(buildDir) { async function build() { console.log('🔨 Building cross-provider design skills...\n'); - // Pre-generate sub-pages (docs, slop, tutorials, live-mode, designing) from source - console.log('📝 Generating sub-pages...'); - const { files: subPageFiles } = await generateSubPages(ROOT_DIR); - console.log(`✓ Generated ${subPageFiles.length} sub-page(s)\n`); + // Sub-page generation, HTML bundling, and static-asset copying are now + // handled by Astro (bun run build:site). This script focuses on skills, + // API data, and Cloudflare config. - const casePageFiles = [ - path.join(ROOT_DIR, 'public', 'cases', 'neo-mirai', 'index.html'), - path.join(ROOT_DIR, 'public', 'neo-mirai', 'index.html'), - ].filter((pagePath) => fs.existsSync(pagePath)); - - // Bundle HTML, JS, and CSS with Bun (including generated sub-pages) - await buildStaticSite([...subPageFiles, ...casePageFiles]); - - // Copy root-level static assets that need stable (unhashed) URLs - const staticAssets = ['og-image.jpg', 'robots.txt', 'sitemap.xml', 'favicon.svg', 'apple-touch-icon.png']; - const buildDir = path.join(ROOT_DIR, 'build'); - for (const asset of staticAssets) { - const src = path.join(ROOT_DIR, 'public', asset); - if (fs.existsSync(src)) { - fs.copyFileSync(src, path.join(buildDir, asset)); - } - } - - // Copy antipattern examples (self-contained HTML, not Bun entrypoints) - const examplesDir = path.join(ROOT_DIR, 'public', 'antipattern-examples'); - if (fs.existsSync(examplesDir)) { - copyDirSync(examplesDir, path.join(buildDir, 'antipattern-examples')); - } - - // Copy browser detector script (referenced by antipattern examples at /js/...) + // Copy browser detector to public/js/ so the antipattern examples can + // reference it (Astro serves public/ as-is). const detectorSrc = path.join(ROOT_DIR, 'src', 'detect-antipatterns-browser.js'); if (fs.existsSync(detectorSrc)) { - const jsDir = path.join(buildDir, 'js'); + const jsDir = path.join(ROOT_DIR, 'public', 'js'); fs.mkdirSync(jsDir, { recursive: true }); fs.copyFileSync(detectorSrc, path.join(jsDir, 'detect-antipatterns-browser.js')); } + const buildDir = path.join(ROOT_DIR, 'build'); + // Read source files (unified skills architecture) const { skills } = readSourceFiles(ROOT_DIR); const patterns = readPatterns(ROOT_DIR); @@ -628,9 +587,12 @@ async function build() { await createAllZips(DIST_DIR); // Generate static API data and Cloudflare Pages config - generateApiData(buildDir, skills, patterns); - copyDistToBuild(DIST_DIR, buildDir); - generateCFConfig(buildDir); + // Write API data and CF config to public/ so Astro copies them to build/. + // Astro wipes build/ before writing, so anything written directly to build/ + // during build:skills would be destroyed when build:site runs. + const publicDir = path.join(ROOT_DIR, 'public'); + generateApiData(publicDir, skills, patterns); + generateCFConfig(publicDir); // Copy all provider outputs to project root for local testing. // `.codex/` is intentionally excluded: Codex no longer consumes that layout; keep diff --git a/scripts/lib/render-page.js b/scripts/lib/render-page.js deleted file mode 100644 index 437844537..000000000 --- a/scripts/lib/render-page.js +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Page template wrapper for generated sub-pages. - * - * Reads the shared site header partial once and wraps content bodies with - * a minimal HTML scaffold that imports tokens.css + sub-pages.css. - * - * Used by scripts/build-sub-pages.js (wired up in commit 3). - */ - -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const ROOT_DIR = path.resolve(__dirname, '..', '..'); -const HEADER_PARTIAL = path.join(ROOT_DIR, 'content', 'site', 'partials', 'header.html'); - -let cachedHeader = null; - -/** - * Read the shared site header partial. - * Cached after first read. - */ -export function readHeaderPartial() { - if (cachedHeader === null) { - cachedHeader = fs.readFileSync(HEADER_PARTIAL, 'utf8').trim(); - } - return cachedHeader; -} - -/** - * Mark a nav item as current by adding aria-current="page" and removing - * the default nav href state. Matches on `data-nav="{activeNav}"`. - * - * @param {string} headerHtml - * @param {string} activeNav - one of: home, designing, docs, slop, live, github - * @returns {string} - */ -export function applyActiveNav(headerHtml, activeNav) { - if (!activeNav) return headerHtml; - return headerHtml.replace( - new RegExp(`data-nav="${activeNav}"`, 'g'), - `data-nav="${activeNav}" aria-current="page"`, - ); -} - -/** - * Wrap body HTML in a full page shell. - * - * @param {object} opts - * @param {string} opts.title - text - * @param {string} opts.description - meta description - * @param {string} opts.bodyHtml - main content HTML (will be placed inside <main>) - * @param {string} [opts.activeNav] - which nav item to mark current - * @param {string} [opts.canonicalPath] - relative URL path for <link rel="canonical"> - * @param {string} [opts.extraHead] - raw HTML to inject into <head> - * @param {string} [opts.bodyClass] - optional class on <body> - * @param {number} [opts.assetDepth] - how many `..` to prepend for Bun's HTML loader to resolve on-disk paths. 1 = page is one dir deep under public/ (e.g. public/skills/polish.html). Defaults to 1. - * @returns {string} full HTML document - */ -export function renderPage({ - title, - description, - bodyHtml, - activeNav, - canonicalPath, - extraHead = '', - bodyClass = 'sub-page', - assetDepth = 1, -}) { - const header = applyActiveNav(readHeaderPartial(), activeNav); - const safeTitle = escapeHtml(title); - const safeDesc = escapeAttr(description || ''); - const canonical = canonicalPath - ? `<link rel="canonical" href="https://impeccable.style${canonicalPath}">` - : ''; - - // Relative prefix for on-disk resolution by Bun's HTML loader. - // Bun rewrites these to hashed absolute URLs at build time, so runtime - // serving works regardless of the request path. - const rel = assetDepth > 0 ? '../'.repeat(assetDepth) : './'; - - return `<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>${safeTitle} - - - ${canonical} - - - - - - ${extraHead} - - - - ${header} -
    -${bodyHtml} -
    - - - -`; -} - -function escapeHtml(str) { - return String(str || '') - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -function escapeAttr(str) { - return String(str || '').replace(/"/g, '"'); -} diff --git a/server/index.js b/server/index.js deleted file mode 100644 index 80da5c273..000000000 --- a/server/index.js +++ /dev/null @@ -1,233 +0,0 @@ -import { serve, file } from "bun"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import homepage from "../public/index.html"; -import privacy from "../public/privacy.html"; -import { - getSkills, - getCommands, - getCommandSource, - getPatterns, - handleFileDownload, - handleBundleDownload -} from "./lib/api-handlers.js"; -import { generateSubPages } from "../scripts/build-sub-pages.js"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const ROOT_DIR = path.resolve(__dirname, ".."); - -// Pre-generate sub-pages so dev + prod share the same output shape. -console.log("📝 Generating sub-pages for dev server..."); -const { files: subPageFiles } = await generateSubPages(ROOT_DIR); -console.log(`✓ Generated ${subPageFiles.length} sub-page(s)`); - -// Helper: serve a generated HTML file by absolute path, 404 if missing. -async function serveGenerated(pagePath) { - const f = file(pagePath); - if (!(await f.exists())) return new Response("Not Found", { status: 404 }); - return new Response(f, { - headers: { - "Content-Type": "text/html;charset=utf-8", - "X-Content-Type-Options": "nosniff", - "X-Frame-Options": "DENY", - }, - }); -} - -const server = serve({ - port: process.env.PORT || 3000, - - routes: { - "/": homepage, - "/privacy": privacy, - - // Legacy URL redirects (kept stable for external links and existing users). - "/cheatsheet": Response.redirect("/docs", 301), - "/gallery": Response.redirect("/slop#try-it-live", 301), - "/skills": Response.redirect("/docs", 301), - "/skills/:id": (req) => Response.redirect(`/docs/${req.params.id}`, 301), - "/anti-patterns": Response.redirect("/slop#catalog", 301), - "/visual-mode": Response.redirect("/slop#see-it", 301), - - // Generated sub-pages — served directly from the pre-generated files - "/docs": () => serveGenerated(path.join(ROOT_DIR, "public/docs/index.html")), - "/docs/": () => serveGenerated(path.join(ROOT_DIR, "public/docs/index.html")), - "/docs/:id": (req) => { - const id = req.params.id.replace(/[^a-z0-9-]/gi, ""); - return serveGenerated(path.join(ROOT_DIR, `public/docs/${id}.html`)); - }, - "/slop": () => serveGenerated(path.join(ROOT_DIR, "public/slop/index.html")), - "/slop/": () => serveGenerated(path.join(ROOT_DIR, "public/slop/index.html")), - "/live-mode": () => serveGenerated(path.join(ROOT_DIR, "public/live-mode/index.html")), - "/live-mode/": () => serveGenerated(path.join(ROOT_DIR, "public/live-mode/index.html")), - "/designing": () => serveGenerated(path.join(ROOT_DIR, "public/designing/index.html")), - "/designing/": () => serveGenerated(path.join(ROOT_DIR, "public/designing/index.html")), - "/neo-mirai": () => Response.redirect("/neo-mirai/", 302), - "/neo-mirai/": () => serveGenerated(path.join(ROOT_DIR, "public/neo-mirai/index.html")), - "/neon-mirai": () => Response.redirect("/neo-mirai/", 301), - "/neon-mirai/": () => Response.redirect("/neo-mirai/", 301), - "/cases/neon-mirai": () => Response.redirect("/cases/neo-mirai", 301), - "/cases/neon-mirai/": () => Response.redirect("/cases/neo-mirai", 301), - "/cases/:slug": (req) => { - const slug = req.params.slug.replace(/[^a-z0-9-]/gi, ""); - return serveGenerated(path.join(ROOT_DIR, `public/cases/${slug}/index.html`)); - }, - "/cases/:slug/": (req) => { - const slug = req.params.slug.replace(/[^a-z0-9-]/gi, ""); - return serveGenerated(path.join(ROOT_DIR, `public/cases/${slug}/index.html`)); - }, - "/tutorials": () => serveGenerated(path.join(ROOT_DIR, "public/tutorials/index.html")), - "/tutorials/": () => serveGenerated(path.join(ROOT_DIR, "public/tutorials/index.html")), - "/tutorials/:slug": (req) => { - const slug = req.params.slug.replace(/[^a-z0-9-]/gi, ""); - return serveGenerated(path.join(ROOT_DIR, `public/tutorials/${slug}.html`)); - }, - - // Static assets - all public subdirectories - "/assets/*": async (req) => { - const url = new URL(req.url); - if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - const filePath = `./public${url.pathname}`; - const assetFile = file(filePath); - if (await assetFile.exists()) { - return new Response(assetFile, { - headers: { "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" } - }); - } - return new Response("Not Found", { status: 404 }); - }, - "/css/*": async (req) => { - const url = new URL(req.url); - if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - const filePath = `./public${url.pathname}`; - const assetFile = file(filePath); - if (await assetFile.exists()) { - return new Response(assetFile, { - headers: { "Content-Type": "text/css", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" } - }); - } - return new Response("Not Found", { status: 404 }); - }, - "/js/*": async (req) => { - const url = new URL(req.url); - if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - // Check public/js/ first, then fall back to built artifacts - const headers = { "Content-Type": "application/javascript", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" }; - const publicFile = file(`./public${url.pathname}`); - if (await publicFile.exists()) return new Response(publicFile, { headers }); - // Browser detector served from impeccable package - if (url.pathname === '/js/detect-antipatterns-browser.js') { - const pkgFile = file('./src/detect-antipatterns-browser.js'); - if (await pkgFile.exists()) return new Response(pkgFile, { headers }); - } - return new Response("Not Found", { status: 404 }); - }, - // Test fixtures (for browser visual testing) - "/fixtures/*": async (req) => { - const url = new URL(req.url); - if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - const filePath = `./tests${url.pathname}`; - const assetFile = file(filePath); - if (await assetFile.exists()) { - const ext = url.pathname.split('.').pop(); - const types = { html: 'text/html', css: 'text/css', js: 'application/javascript' }; - return new Response(assetFile, { - headers: { "Content-Type": types[ext] || "application/octet-stream", "X-Content-Type-Options": "nosniff" } - }); - } - return new Response("Not Found", { status: 404 }); - }, - "/antipattern-images/*": async (req) => { - const url = new URL(req.url); - if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - const filePath = `./public${url.pathname}`; - const assetFile = file(filePath); - if (await assetFile.exists()) { - return new Response(assetFile, { - headers: { "X-Content-Type-Options": "nosniff" } - }); - } - return new Response("Not Found", { status: 404 }); - }, - "/antipattern-examples/*": async (req) => { - const url = new URL(req.url); - if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - const filePath = `./public${url.pathname}`; - const assetFile = file(filePath); - if (await assetFile.exists()) { - return new Response(assetFile, { - headers: { "Content-Type": "text/html", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "SAMEORIGIN" } - }); - } - return new Response("Not Found", { status: 404 }); - }, - - // API: Get all skills - "/api/skills": { - async GET() { - const skills = await getSkills(); - return Response.json(skills); - }, - }, - - // API: Get all commands - "/api/commands": { - async GET() { - const commands = await getCommands(); - return Response.json(commands); - }, - }, - - // API: Get patterns and antipatterns - "/api/patterns": { - async GET() { - const patterns = await getPatterns(); - return Response.json(patterns); - }, - }, - - // API: Get command source content - "/api/command-source/:id": async (req) => { - const { id } = req.params; - const result = await getCommandSource(id); - if (result && result.error) { - return Response.json({ error: result.error }, { status: result.status }); - } - if (!result) { - return Response.json({ error: "Command not found" }, { status: 404 }); - } - return Response.json({ content: result }); - }, - - // API: Download individual file - "/api/download/:type/:provider/:id": async (req) => { - const { type, provider, id } = req.params; - return handleFileDownload(type, provider, id); - }, - - // API: Download provider bundle ZIP - "/api/download/bundle/:provider": async (req) => { - const { provider } = req.params; - return handleBundleDownload(provider); - }, - }, - - // Serve root-level static files (og-image.png, favicon, robots.txt, etc.) - fetch(req) { - const url = new URL(req.url); - if (url.pathname.includes('..')) { - return new Response("Bad Request", { status: 400 }); - } - const filePath = `./public${url.pathname}`; - const staticFile = file(filePath); - if (staticFile.size > 0) { - return new Response(staticFile); - } - return new Response("Not Found", { status: 404 }); - }, - - development: process.env.NODE_ENV !== "production", -}); - -console.log(`🎨 impeccable.style running at ${server.url}`); diff --git a/server/lib/api-handlers.js b/server/lib/api-handlers.js deleted file mode 100644 index 1714c25a1..000000000 --- a/server/lib/api-handlers.js +++ /dev/null @@ -1,223 +0,0 @@ -import { readdir, readFile } from "fs/promises"; -import { basename, join, dirname } from "path"; -import { existsSync } from "fs"; -import { fileURLToPath } from "url"; -import { readPatterns, parseFrontmatter } from "../../scripts/lib/utils.js"; -import { FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS } from "../../lib/download-providers.js"; -import { - isAllowedBundleProvider, - isAllowedFileProvider, - isAllowedType, - isValidId, - sanitizeFilename -} from "./validation.js"; - -// Get project root directory (works in both Node.js and Bun, including Vercel) -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const PROJECT_ROOT = join(__dirname, "..", ".."); - -// Helper to read file content (works in both Node.js and Bun) -async function readFileContent(filePath) { - return readFile(filePath, "utf-8"); -} - -// Read all skills from source/skills/ subdirectories -export async function getSkills() { - const skillsDir = join(PROJECT_ROOT, "source", "skills"); - const entries = await readdir(skillsDir, { withFileTypes: true }); - const skills = []; - - for (const entry of entries) { - if (!entry.isDirectory()) continue; - const skillMdPath = join(skillsDir, entry.name, "SKILL.md"); - if (!existsSync(skillMdPath)) continue; - - const content = await readFileContent(skillMdPath); - const { frontmatter } = parseFrontmatter(content); - - skills.push({ - id: entry.name, - name: frontmatter.name || entry.name, - description: frontmatter.description || "No description available", - userInvocable: frontmatter['user-invocable'] === true || frontmatter['user-invocable'] === 'true', - }); - } - - return skills; -} - -// Read a short tagline for a command from its editorial file -// (content/site/skills/.md). Returns null if the file or tagline is -// missing. Taglines are used by UI surfaces that need a human-friendly -// one-liner; `description` stays optimized for auto-trigger matching. -async function readCommandTagline(id) { - const editorialPath = join(PROJECT_ROOT, "content/site/skills", `${id}.md`); - if (!existsSync(editorialPath)) return null; - try { - const raw = await readFileContent(editorialPath); - const match = raw.match(/^---\n([\s\S]*?)\n---/); - if (!match) return null; - const taglineMatch = match[1].match(/tagline:\s*"([^"]+)"/); - return taglineMatch ? taglineMatch[1] : null; - } catch { - return null; - } -} - -// Read commands. After the v3.0 consolidation, commands are sub-commands of -// /impeccable. Read them from command-metadata.json and include the root -// impeccable skill itself so UI surfaces (cheatsheet, magazine spread) can -// list them. -export async function getCommands() { - const allSkills = await getSkills(); - const metadataPath = join(PROJECT_ROOT, "source/skills/impeccable/scripts/command-metadata.json"); - - const commands = []; - const impeccable = allSkills.find(s => s.name === "impeccable"); - if (impeccable) { - commands.push({ - id: "impeccable", - name: "impeccable", - description: impeccable.description, - tagline: await readCommandTagline("impeccable"), - userInvocable: true, - }); - } - - if (existsSync(metadataPath)) { - try { - const raw = await readFileContent(metadataPath); - const metadata = JSON.parse(raw); - for (const [id, meta] of Object.entries(metadata)) { - commands.push({ - id, - name: id, - description: meta.description, - tagline: await readCommandTagline(id), - userInvocable: true, - }); - } - } catch (error) { - console.error("Error reading command metadata:", error); - } - } - - // Fallback: return just user-invocable skills if no metadata - if (commands.length === 0) { - return allSkills.filter(s => s.userInvocable); - } - - return commands; -} - -// Get command/skill source content -export async function getCommandSource(id) { - if (!isValidId(id)) { - return { error: "Invalid command ID", status: 400 }; - } - - const skillPath = join(PROJECT_ROOT, "source", "skills", id, "SKILL.md"); - - try { - if (!existsSync(skillPath)) { - return null; - } - const content = await readFileContent(skillPath); - return content; - } catch (error) { - console.error("Error reading skill source:", error); - return null; - } -} - -// Get the appropriate file path for a provider -export function getFilePath(type, provider, id) { - const distDir = join(PROJECT_ROOT, "dist"); - const configDir = FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS[provider]; - if (!configDir) return null; - - // Everything is a skill now - if (type === "skill" || type === "command") { - return join(distDir, provider, configDir, "skills", id, "SKILL.md"); - } - - return null; -} - -// Handle individual file download -export async function handleFileDownload(type, provider, id) { - if (!isAllowedType(type)) { - return new Response("Invalid type", { status: 400 }); - } - - if (!isAllowedFileProvider(provider)) { - return new Response("Invalid provider", { status: 400 }); - } - - if (!isValidId(id)) { - return new Response("Invalid file ID", { status: 400 }); - } - - const filePath = getFilePath(type, provider, id); - - if (!filePath) { - return new Response("Invalid provider", { status: 400 }); - } - - try { - if (!existsSync(filePath)) { - return new Response("File not found", { status: 404 }); - } - - const content = await readFile(filePath); - const fileName = sanitizeFilename(basename(filePath)); - return new Response(content, { - headers: { - "Content-Type": "application/octet-stream", - "Content-Disposition": `attachment; filename="${fileName}"`, - }, - }); - } catch (error) { - console.error("Error downloading file:", error); - return new Response("Error downloading file", { status: 500 }); - } -} - -// Extract patterns from SKILL.md using the shared utility -export async function getPatterns() { - try { - return readPatterns(PROJECT_ROOT); - } catch (error) { - console.error("Error reading patterns:", error); - return { patterns: [], antipatterns: [] }; - } -} - -// Handle bundle download -export async function handleBundleDownload(provider) { - if (!isAllowedBundleProvider(provider)) { - return new Response("Invalid provider", { status: 400 }); - } - - const distDir = join(PROJECT_ROOT, "dist"); - const zipPath = join(distDir, `${provider}.zip`); - - try { - if (!existsSync(zipPath)) { - return new Response("Bundle not found", { status: 404 }); - } - - const content = await readFile(zipPath); - const safeProvider = sanitizeFilename(provider); - return new Response(content, { - headers: { - "Content-Type": "application/zip", - "Content-Disposition": `attachment; filename="impeccable-style-${safeProvider}.zip"`, - }, - }); - } catch (error) { - console.error("Error downloading bundle:", error); - return new Response("Error downloading bundle", { status: 500 }); - } -} diff --git a/server/lib/validation.js b/server/lib/validation.js deleted file mode 100644 index b2a1f9285..000000000 --- a/server/lib/validation.js +++ /dev/null @@ -1,40 +0,0 @@ -// Shared validation helpers for input sanitization - -import { - BUNDLE_DOWNLOAD_PROVIDERS, - DOWNLOAD_PROVIDERS, - FILE_DOWNLOAD_PROVIDERS, -} from '../../lib/download-providers.js'; - -// Only allow alphanumeric, hyphens, and underscores in IDs -export const VALID_ID = /^[a-zA-Z0-9_-]+$/; - -export const ALLOWED_PROVIDERS = DOWNLOAD_PROVIDERS; -export const ALLOWED_FILE_PROVIDERS = FILE_DOWNLOAD_PROVIDERS; -export const ALLOWED_BUNDLE_PROVIDERS = BUNDLE_DOWNLOAD_PROVIDERS; -export const ALLOWED_TYPES = ['skill', 'command']; - -export function isValidId(id) { - return typeof id === 'string' && VALID_ID.test(id); -} - -export function isAllowedProvider(provider) { - return ALLOWED_PROVIDERS.includes(provider); -} - -export function isAllowedFileProvider(provider) { - return ALLOWED_FILE_PROVIDERS.includes(provider); -} - -export function isAllowedBundleProvider(provider) { - return ALLOWED_BUNDLE_PROVIDERS.includes(provider); -} - -export function isAllowedType(type) { - return ALLOWED_TYPES.includes(type); -} - -// Sanitize a filename for use in Content-Disposition headers -export function sanitizeFilename(filename) { - return filename.replace(/[^a-zA-Z0-9._-]/g, ''); -} diff --git a/site/components/Footer.astro b/site/components/Footer.astro new file mode 100644 index 000000000..1d2309478 --- /dev/null +++ b/site/components/Footer.astro @@ -0,0 +1,29 @@ +--- +--- + + diff --git a/content/site/partials/header.html b/site/components/Header.astro similarity index 69% rename from content/site/partials/header.html rename to site/components/Header.astro index 131c29604..46e4e0425 100644 --- a/content/site/partials/header.html +++ b/site/components/Header.astro @@ -1,4 +1,10 @@ - +--- +interface Props { + activeNav?: 'home' | 'designing' | 'docs' | 'slop' | 'live'; +} +const { activeNav } = Astro.props; +--- +
    + + + + + +

    Live Mode mid-cycle: the picker outlines the element you chose, the context bar shows which variant you're on, and the global bar stays pinned to the bottom. Accept on this one writes Variant 2 back to source.

    + + +## When to use it + +Reach for `/impeccable live` when you want to iterate on something visually the way you would in a design tool, but keep production code as the output. The canvas-like flow of Figma without the round trip to an implementation step. + +Use it for: + +- **Exploring directions on a real element.** A hero section, a newsletter card, a pricing tier. Three genuinely different takes, side by side, on the actual page with the actual context. +- **Polishing a piece of UI that is almost right.** You know what feels off but cannot quite say it. Pick the element, scribble "more playful" or draw a stroke through the bit that bugs you, hit Go. +- **A quick A/B between two directions your team is debating.** Generate variants, accept nothing, walk away. The point was the comparison. + +It is NOT for new greenfield features (reach for `/impeccable craft`) or whole-page redesigns (reach for `/impeccable` or a specialized refine command). + +## How it works + +One command brings up a picker overlay on top of your running dev server. You pick any element. A small context bar appears next to it. Type a freeform description or pick one of the action chips (`bolder`, `quieter`, `distill`, `polish`, `typeset`, `colorize`, `layout`, `animate`, `delight`, `overdrive`). Optionally drop comment pins or draw strokes directly on the element first, and the skill reads those as intent. + +Hit Go. Three **production-quality variants** get generated, each anchored to a genuinely different design archetype (not three riffs on color) and hot-swapped into the page via your framework's HMR. Cycle through them with arrow keys. Accept one and the variant is written back to source. Discard all three and the original stays. + +It supports Vite, Next.js (including monorepos), SvelteKit, Astro, Nuxt, and plain static HTML. If your dev server has a strict Content Security Policy, the first-run setup detects it and offers a one-time, dev-only patch so the picker can load. `DESIGN.md` wins on visual decisions, `PRODUCT.md` wins on voice: if you have both, variants stay on-brand without being told. + +## Try it + +``` +/impeccable live +``` + +Open your dev server URL, pick the newsletter signup card, click the `delight` chip, hit Go. You will get three variants that vary across personality dimensions (a stamp-and-postcard feel, a typographic-surprise version, an illustrated-accent one), not three riffs on the same treatment. + +Or pick a hero, type "more editorial, less SaaS", hit Go. The three variants anchor to different editorial archetypes (broadsheet masthead, catalog-style spec rows, oversized-glyph poster) rather than three shades of the same idea. + +Stop live mode when you are done: say "stop live mode", close the tab, or hit the exit button on the picker bar. + +## Pitfalls + +- **Running it on a page that is still half-written.** Live variant generation needs context. If the element has placeholder copy, generic Lorem ipsum, or pre-stylesheet default formatting, variants will reflect that. Fill the content first. +- **Expecting it to make macro decisions.** Live mode iterates on a single picked element. For "redo the entire pricing page", reach for `/impeccable` or `/impeccable craft` instead. +- **Ignoring the fallback messages.** If the element lives in a generated file (a compiled template, a build output), the picker says so explicitly and offers to route the accept into true source. Do not force the accept into the generated file: the next build will wipe it. +- **Running it without PRODUCT.md or DESIGN.md when you care about brand fit.** Live will still generate, but the variants will lean toward generic defaults. Run `/impeccable teach` and `/impeccable document` first if the result needs to sound like your product. diff --git a/site/content/skills/onboard.md b/site/content/skills/onboard.md new file mode 100644 index 000000000..dd4e2c9ca --- /dev/null +++ b/site/content/skills/onboard.md @@ -0,0 +1,40 @@ +--- +tagline: "Design first-run experiences, empty states, and paths to value." +--- + +## When to use it + +`/impeccable onboard` is for the moments that decide whether a new user sticks around: the first screen, the empty state, the setup flow, the product tour, the "what do I do now" gap. Reach for it when activation is weak, when new users drop off before reaching value, or when your product has empty states that say "no items yet" and stop there. + +## How it works + +The command starts from one question: what is the aha moment, and how fast can a new user get there. Every design decision points at that moment. + +It works across the surfaces that shape first impressions: + +1. **First-run experience**: the moments immediately after sign-up. Should the user see a tour, a blank canvas, a filled example, or nothing at all. Pick the approach that matches the product. +2. **Empty states**: every zero-data screen gets oriented. Where am I, why is this empty, what do I do next, what will it look like once it is full. +3. **Setup and installation**: required configuration is minimized, defaults are smart, each step explains why it matters. +4. **Progressive disclosure**: advanced features stay out of the way until they are earned. +5. **Activation events**: the moment a user first experiences the core value is instrumented and celebrated, quietly. + +The command resists two common failure modes: over-tutorialized onboarding where users click through a carousel before they can touch anything, and zero-onboarding where users are dropped into an empty app and expected to figure it out. + +## Try it + +``` +/impeccable onboard the editor +``` + +Typical output: + +- First-run: replaces empty editor with a filled example document the user can modify. Cancel button discards the example, edit replaces the content with the user's work. +- Empty state on document list: "No documents yet. Create your first, or import from Notion, Google Docs, or Markdown." +- Setup: reduced from 6 required fields to 1 (workspace name). Everything else has a smart default and can be edited later in settings. +- Activation: the first time a user saves a document, a quiet toast says "Saved. Your work is in the cloud now." One-time, not repeated. + +## Pitfalls + +- **Adding a product tour as the default answer.** Most products do not need a tour. They need a better first screen. Tours are a crutch. +- **Designing onboarding without defining the aha moment.** If you cannot say in one sentence what the user should feel in the first 60 seconds, go back to `/impeccable shape` first. +- **Running onboard on a broken flow.** Fix the flow first. Onboarding cannot rescue a product where the core action is broken. diff --git a/site/content/skills/optimize.md b/site/content/skills/optimize.md new file mode 100644 index 000000000..545d596c1 --- /dev/null +++ b/site/content/skills/optimize.md @@ -0,0 +1,56 @@ +--- +tagline: "Diagnose and fix UI performance from LCP to bundle size." +--- + +## When to use it + +`/impeccable optimize` is for interfaces that feel slow. First paint takes forever, scrolling janks, images pop in late, interactions feel laggy, the bundle ships 800KB of JavaScript. Use it when the Web Vitals are bad or when users are complaining that things are sluggish. + +Do not use it as premature optimization. If LCP is 1.1s and INP is 80ms, stop. The design work matters more. + +## How it works + +The skill works through five perf dimensions: + +1. **Loading and Web Vitals**: LCP, INP, CLS. Identify what is blocking the first paint, what is delaying interaction, what is shifting layout. +2. **Rendering**: unnecessary re-renders, missing memoization, expensive reconciliation, layout thrash in loops. +3. **Animations**: is anything animating layout properties, are transforms and opacity the only thing touched, does `will-change` help or hurt here. +4. **Images and assets**: lazy loading, responsive images (`srcset`, `sizes`), modern formats (WebP, AVIF), dimensions set to prevent CLS. +5. **Bundle size**: unused imports, oversized dependencies, missing code-splitting, dead code. + +The skill measures before and after. Every fix gets quantified. If a change does not move a metric, it gets rolled back. + +## Try it + +``` +/impeccable optimize the homepage +``` + +Expected shape: + +``` +LCP: 3.2s → 1.4s + - Hero image preloaded (-800ms) + - Removed render-blocking font stylesheet (-240ms) + - Deferred analytics script (-180ms) + +INP: 240ms → 90ms + - Debounced scroll handler + - Memoized expensive list render + - Removed synchronous layout read in event loop + +CLS: 0.18 → 0.02 + - Set dimensions on hero image and logo + - Reserved space for async header badge + +Bundle: 340KB → 180KB + - Removed unused lodash import (52KB) + - Code-split the playground route (78KB) + - Dropped deprecated icon set (30KB) +``` + +## Pitfalls + +- **Optimizing before measuring.** Without baseline metrics, you cannot tell what helped. Run `/impeccable optimize` with specific Web Vitals numbers, not vibes. +- **Chasing tiny wins.** A 20ms improvement in INP that takes a week is rarely worth it. Optimize has diminishing returns; know when to stop. +- **Forgetting to re-measure after every change.** The build could have made things worse in a way the skill did not predict. Verify. diff --git a/site/content/skills/overdrive.md b/site/content/skills/overdrive.md new file mode 100644 index 000000000..61a2a26e8 --- /dev/null +++ b/site/content/skills/overdrive.md @@ -0,0 +1,30 @@ +--- +tagline: "Push an interface past conventional limits. Shaders, physics, 60fps, cinematic transitions." +--- + +## When to use it + +`/impeccable overdrive` is for the moments where you want to impress. A hero that uses WebGL. A table that handles a million rows. A dialog that morphs out of its trigger element. A form that validates in real-time with streaming feedback. A page transition that feels cinematic. Use it when the project budget allows for technical ambition and the outcome needs to feel extraordinary. + +Do not use it on operator tools, dashboards, or anything where reliability beats spectacle. Overdrive burns complexity for effect, and that trade-off is only worth it on moments that matter. + +## How it works + +The skill picks one moment to make extraordinary and commits to it, rather than spreading effort across the whole interface. It then reaches for techniques most AI-generated UIs never touch: WebGL shaders, spring physics, Scroll Timeline, View Transitions, canvas animation, GPU-accelerated filters. Everything is budgeted, profiled, and tested at 60fps, with reduced-motion fallbacks baked in. + +Overdrive output is announced with `──── ⚡ OVERDRIVE ────` so you know you are entering a more ambitious mode. Expect larger diffs, new dependencies, and implementation depth beyond what other skills produce. + +## Try it + +``` +/impeccable overdrive the landing hero +``` + +One concrete run might replace a static hero with a WebGL shader background driven by mouse position, a display headline that reveals with a mask on scroll using the Scroll Timeline API, and a View Transition on the CTA that morphs into the next page. Plus a reduced-motion fallback that swaps all of it for a clean static composition. + +## Pitfalls + +- **Using it everywhere.** Overdrive works because it is rare. If every page has cinematic moments, none of them are cinematic. +- **Shipping without reduced-motion fallbacks.** Non-negotiable. Overdrive adds them automatically; do not remove them. +- **Ignoring performance.** Extraordinary moments still need to hit 60fps. If the effect drops frames, cut it or optimize. Slow spectacle is worse than simple done well. +- **Running overdrive before the base interface is solid.** Spectacle on a broken foundation reads as distraction, not delight. diff --git a/site/content/skills/polish.md b/site/content/skills/polish.md new file mode 100644 index 000000000..9c7a5f0e4 --- /dev/null +++ b/site/content/skills/polish.md @@ -0,0 +1,46 @@ +--- +tagline: "The meticulous final pass between good and great." +--- + +## When to use it + +`/impeccable polish` is the last thing you run before shipping. It hunts down the small details that separate a shipped feature from a polished one: half-pixel misalignments, inconsistent spacing, forgotten focus states, loading transitions that flash, copy that drifts in tone. It also aligns the feature with your design system, replacing hard-coded values with tokens, swapping custom components for shared ones, and fixing any drift from established patterns. + +Reach for it when the feature is functionally complete, nothing is broken, and something still feels off. Also reach for it when a feature has drifted from the design system and needs to be pulled back in line. + +## How it works + +Polish starts by discovering the design system (tokens, spacing scale, shared components), then works methodically across six dimensions: + +1. **Visual alignment and spacing**: pixel-perfect grid adherence, consistent spacing scale, optical alignment on icons. +2. **Typography**: hierarchy consistency, line length, widows and orphans, kerning on headlines. +3. **Color and contrast**: token usage, theme parity, WCAG ratios, focus indicators. +4. **Interaction states**: hover, focus, active, disabled, loading, error, success. Every state accounted for. +5. **Transitions and motion**: smooth easing, no layout jank, respect for `prefers-reduced-motion`. +6. **Copy**: consistent voice, correct tense, no placeholder strings, no stray TODOs. + +The skill is explicit about one thing: polish is the last step, not the first. If the feature is not functionally complete, polishing it is wasted work. + +## Try it + +``` +/impeccable polish the pricing page +``` + +A healthy run looks like: + +``` +Visual alignment: fixed 3 off-grid elements (8px baseline) +Typography: tightened h1 kerning, fixed widow on testimonial +Interaction: added hover state on FAQ items, focus ring on email input +Motion: softened modal entrance, added reduced-motion fallback +Copy: removed one "Lorem ipsum" stray, aligned button voice +``` + +Five small fixes, no rewrites. That is the shape of a good polish pass. + +## Pitfalls + +- **Polishing work that is not done.** If there are TODOs in the code, you are not ready. Run `/impeccable polish` on finished features only. +- **Treating polish as redesign.** Polish refines what exists. If you find yourself rearchitecting a layout, you needed `/impeccable critique` or `/impeccable layout` instead. +- **Running `/impeccable polish` without `/impeccable audit` first.** Polish catches feel-based issues. Audit catches measurable ones. Use both. diff --git a/site/content/skills/quieter.md b/site/content/skills/quieter.md new file mode 100644 index 000000000..e589df5c6 --- /dev/null +++ b/site/content/skills/quieter.md @@ -0,0 +1,40 @@ +--- +tagline: "Tone down designs that are shouting without losing their intent." +--- + +## When to use it + +`/impeccable quieter` is the counterweight to `/impeccable bolder`. Reach for it when an interface is visually aggressive, overstimulating, or trying to do too many things at full volume. Neon on dark, gradient text everywhere, 6 accent colors, everything animated, 20px shadows. Use quieter when the design needs to breathe and you want refinement without losing the point of view. + +Also useful after `/impeccable bolder` goes a little too far. + +## How it works + +The skill works by reduction across four axes: + +1. **Color**: desaturate, lower chroma in OKLCH, pull accents back to a single primary plus muted support. No more than two intentional colors. +2. **Contrast**: soften extreme darks and lights, pull the range in. Backgrounds move from pure white and pure black to paper and ink. +3. **Decoration**: remove shadows that are not doing work, drop borders that are not carrying structure, retire gradients that exist for energy rather than hierarchy. +4. **Motion and effect**: slow animations down, remove anything that auto-plays, drop parallax and blur unless they serve readability. + +The skill preserves the design's intent. If the original had a point of view, the quieter version has the same point of view with more confidence. Refinement, not neutralization. + +## Try it + +``` +/impeccable quieter the pricing page +``` + +Typical diff: + +- Gradient text on the price removed, replaced with solid ink at one weight heavier +- Three accent colors reduced to one (magenta), the other two become neutral variants +- Card shadows reduced from `0 20px 40px rgba(0,0,0,0.2)` to `0 1px 0 var(--color-mist)` (a hairline) +- Background switches from dark gradient to paper with a subtle cream wash at the top +- Hero animation from 1.2s easeOut with 3 staggered elements to a single 260ms fade-in + +## Pitfalls + +- **Over-applying.** Quieter can strip personality if you run it on something that was already measured. Use it when the design is too loud, not when it is correctly assertive. +- **Confusing quieter with distill.** Quieter reduces intensity. Distill removes elements. They are different moves. +- **Running it in response to a critique that says "too busy".** Busy usually means too many things, not too loud. Try `/impeccable distill` first. diff --git a/site/content/skills/shape.md b/site/content/skills/shape.md new file mode 100644 index 000000000..7f51beedc --- /dev/null +++ b/site/content/skills/shape.md @@ -0,0 +1,73 @@ +--- +tagline: "Think before you build. Produce a design brief through discovery, not guesswork." +--- + +
    +
    +
    + brief.md + Output of /impeccable shape +
    +
    +
    + Purpose + Let committed subscribers change what they get without losing them to unsubscribe. +
    +
    + User + Rushed, on mobile, mid-meeting. Reading fast, low patience. +
    +
    + Content + 4 digest types, 2 cadences, one opt-out-all at the bottom. +
    +
    + Feeling + Calm, trustworthy, no dark patterns. +
    +
    + Constraints + Mobile-first. WCAG AA contrast. One column, no modals. +
    +
    + +
    +

    A shape brief is a compass, not a spec. It captures intent, not UI. Implementation skills read it before writing a line of code.

    +
    + +## When to use it + +`/impeccable shape` is where a feature starts. Before anyone writes code, before anyone argues about the hero treatment, before anyone picks a font. Use it to force a discovery conversation about purpose, users, content, and constraints, then capture the answers as a design brief the implementation skills can lean on. + +Reach for it whenever a feature is about to start, a ticket is vague, or you catch yourself writing JSX to figure out what the product should be. + +## How it works + +Most AI-generated UIs fail not because of bad code, but because of skipped thinking. The model jumps to "here is a card grid" without asking "what is the user trying to accomplish". `/impeccable shape` inverts that order. + +The skill runs a structured discovery interview in conversation. It will not write code during this phase. The questions cover: + +- **Purpose and context**: what the feature is for, who uses it, what state of mind they are in +- **Content and data**: what is displayed, realistic ranges, edge cases, what is dynamic +- **Design goals**: the single most important thing, the intended feeling, reference examples +- **Constraints**: technical, content, accessibility, localization + +You answer naturally. The skill asks follow-ups, not a form. At the end it produces a design brief: a structured artifact you can hand to `/impeccable` or any other implementation skill. + +Note: if you want the full flow (discovery interview, then straight into building), use `/impeccable craft` instead. It runs `/impeccable shape` internally, then continues into implementation with visual iteration. `/impeccable shape` standalone is for when you want just the brief, so you can take it to whatever implementation approach you prefer. + +## Try it + +``` +/impeccable shape a daily digest email preferences page +``` + +Expect a 5 to 10 question conversation. The skill asks things like "who is the person opening this, and are they already committed or still curious" and "what happens when the user has unsubscribed from everything, do we hide the feature or show something". You answer, and a brief materializes. + +From there you can hand the brief to `/impeccable`, `/impeccable polish`, or any other skill. Or just use it as a reference while you build by hand. + +## Pitfalls + +- **Skipping it because it feels slow.** The interview is maybe 5 minutes. The rewrites you avoid are measured in hours. +- **Treating the brief as a spec.** It is a compass, not a checklist. It captures intent, not UI. +- **Answering with "standard" or "normal".** Specificity is the whole point. If a user is "rushed, on mobile, between meetings", say so. That changes everything downstream. diff --git a/site/content/skills/teach.md b/site/content/skills/teach.md new file mode 100644 index 000000000..c57da2eca --- /dev/null +++ b/site/content/skills/teach.md @@ -0,0 +1,70 @@ +--- +tagline: "Teach Impeccable who your product is for, once per project." +--- + +
    +
    +
    + PRODUCT.md + Loaded on every command +
    +
    +
    + Register + Product. Design serves the task. +
    +
    + Users + SREs on call, reading fast, often in the dark. +
    +
    + Brand voice + Calm, clinical, no hype. +
    +
    + Anti-references + Purple gradients. Glassmorphism. "Boost your productivity." +
    +
    + +
    +

    A finished PRODUCT.md. Strategy only: who, what, why. No colors, no fonts, no pixel values, those live in DESIGN.md.

    +
    + +## When to use it + +Run `/impeccable teach` once at the start of a project. It is the onramp. Without it, every other command will produce design that is technically competent but generically toned: stock SaaS voice, safe-default fonts, the AI color palette. With it, every command reads your answers before it generates. + +Reach for it when: + +- **You just installed Impeccable in a new project.** First thing to run. Other commands will nudge you toward it if you skip. +- **The project's brand direction has shifted.** New positioning, new audience, new voice. Re-run `teach` and the updated context flows through every command. +- **Another command said "no design context found"** and stopped. That is the signal: run teach, then resume. + +## How it works + +Teach writes two complementary files at the project root: + +- **`PRODUCT.md`** is the strategic file. Register (brand or product), target users, product purpose, brand personality, anti-references, design principles, accessibility needs. Answers "who, what, why". +- **`DESIGN.md`** is the visual file. Colors, typography, elevation, components, do's and don'ts. Answers "how it looks". Written by the delegated `/impeccable document` command, which teach invokes at the end. + +The flow scans the codebase first (README, package.json, components, tokens, brand assets) and forms a **register hypothesis**: brand (landing, marketing, portfolio, where design IS the product) or product (app UI, dashboards, tools, where design SERVES the product). Register is the first question, because it shapes every downstream answer: typography defaults, motion energy, color strategy, the reference set commands like `/impeccable typeset` pull from. After register, teach asks only what it could not infer: users, personality in three real words, references and anti-references, accessibility requirements. + +PRODUCT.md is strategic only. No colors, no fonts, no pixel values. Those live in DESIGN.md. Keeping the two files separate is deliberate: strategy can stay stable while the visual system evolves. + +## Try it + +``` +/impeccable teach +``` + +Expect a 5 to 8 minute interview. The first question is usually about register; the rest are short. Teach will quote back what it inferred from your code ("from the routes, this looks like a product surface, match?") so you are confirming, not starting from scratch. + +At the end, teach offers to run `/impeccable document` for you. Say yes unless you have a specific reason to hold off. A real DESIGN.md is what keeps variants, polishes, and audits on-brand. + +## Pitfalls + +- **Skipping it to "just try a command quickly".** Every other command will interview you mid-flight instead. Running teach first is faster, not slower. +- **Giving generic answers.** "Modern and clean" is not useful. "Warm, mechanical, opinionated" is. Be specific. Be willing to disagree with safe defaults. +- **Treating PRODUCT.md as immutable.** The file is yours. If teach put something in there that is not quite right, edit it. Every command reads the current file. +- **Listing only adjectives for references.** Brands, products, printed objects: named, not described. "Klim Type Foundry specimen pages", not "technical and clean". Anti-references should be equally specific. diff --git a/site/content/skills/typeset.md b/site/content/skills/typeset.md new file mode 100644 index 000000000..8c31a0864 --- /dev/null +++ b/site/content/skills/typeset.md @@ -0,0 +1,42 @@ +--- +tagline: "Fix typography that feels generic, inconsistent, or accidental." +--- + +## When to use it + +Reach for `/impeccable typeset` when the text on a page looks like default typography instead of designed typography. Muddy hierarchy, three sizes that look the same, body copy at 14px, a display font that is actually just Inter bold, headlines with no kerning attention. + +Common triggers: "hierarchy feels flat", "readability is off", "fonts look generic". + +## How it works + +The skill assesses typography across five dimensions: + +1. **Font choices**: are you using invisible defaults (Inter, Roboto, Arial, Open Sans), does the typeface match the brand, are there more than 2 to 3 families. +2. **Hierarchy**: are heading, body, and caption clearly different at a glance, is the size contrast at least 1.25x between steps, are weight contrasts legible. +3. **Sizing and scale**: is there a coherent type scale, does body text meet 16px minimum, is the scale fixed-rem for app UIs or fluid-clamp for marketing pages. +4. **Readability**: line length 45 to 75 characters, line-height tuned for font and context, contrast. +5. **Consistency**: same element uses same treatment everywhere, no one-off font-size overrides. + +It then fixes what it finds: picks distinctive typefaces, builds a modular scale, widens hierarchy contrast, sets proper line length and leading. + +## Try it + +``` +/impeccable typeset the article layout +``` + +Expected diff: + +- Display font swapped from Inter 700 to a real display face +- Type scale rebuilt: 3rem / 2rem / 1.25rem / 1rem / 0.875rem, ratio 1.333 +- Body text bumped from 14px to 16px +- Line length clamped to 68ch on the article column +- Line-height 1.6 for body, 1.1 for display +- Removed four one-off `font-size` values scattered in component styles + +## Pitfalls + +- **Asking for a new font without context.** Typeset will pick based on the `PRODUCT.md` brand voice. If you have not run `/impeccable teach`, the suggestion will be generic. +- **Reaching for typeset when the issue is layout.** If paragraphs are fine but the page feels cramped, you want `/impeccable layout`. +- **Expecting fluid clamp scales on app UIs.** Typeset uses fixed rem scales for app interfaces. Fluid typography is for marketing and content pages where line length varies dramatically. diff --git a/site/content/tutorials/brand-vs-product.md b/site/content/tutorials/brand-vs-product.md new file mode 100644 index 000000000..6aa7c5b11 --- /dev/null +++ b/site/content/tutorials/brand-vs-product.md @@ -0,0 +1,130 @@ +--- +title: Brand vs product, pick a register +tagline: "Two worlds, two sets of defaults. Pick the right one and every command downstream benefits." +order: 3 +description: "Impeccable treats brand work (landing pages, campaigns, portfolios) and product work (app UI, dashboards, tools) as different worlds with different defaults. Learn how to pick a register and how it shapes every command that reads it." +--- + +## See the divergence + +Same element, one register each. A newsletter signup, twice. + +
    +
    +
    +
    + Brand + Editorial-magazine +
    +
    + No. 04  ·  Dispatch +

    Letters, occasionally.

    +

    A postcard from the editor, once a month. No tracking pixels, no "just checking in."

    + Send me one +
    +
    + Serif display, italic display weight + Drenched in the primary hue + Monospaced kicker, editorial voice +
    +
    +
    +
    + Product + Utility / app shell +
    +
    + Newsletter +

    Subscribe to updates

    +

    Product changes and release notes, once a month. Unsubscribe at any time.

    + Subscribe +
    +
    + Neutral sans, semibold for hierarchy + Restrained palette, accent only on state + Short, scannable, mobile-readable copy +
    +
    +
    +

    The table below lists what's different. This is what it looks like at the pixel.

    +
    + +## Why register matters + +Every design task belongs to one of two worlds: + +- **Brand** is where design IS the product. Marketing sites, landing pages, portfolios, long-form content, campaign surfaces. Distinctiveness is the bar. Fonts, motion, density, and color all push toward "this looks like nothing else in the category." +- **Product** is where design SERVES the product. App UI, admin, dashboards, tools. Earned familiarity is the bar. Fluent users of Linear, Figma, Notion, Raycast, or Stripe should trust the output on sight. + +If you ask the same AI to design a dashboard and a campaign page without naming which world, you'll get the average of the two. Brand surfaces will feel too careful. Product surfaces will feel too precious. Register is how Impeccable avoids that. + +Impeccable tracks register as a single field in `PRODUCT.md`: + +```markdown +## Register + +product +``` + +That is it: a bare value, `brand` or `product`. Every command that does register-sensitive work (`typeset`, `animate`, `colorize`, `layout`, `bolder`, `quieter`, `delight`) loads a different reference file based on what it finds here. + +## How the two worlds diverge + +This is not an exhaustive list, the full divergence lives in the `brand.md` and `product.md` reference files, but the shape of the difference: + +| Dimension | Brand | Product | +|---|---|---| +| **Type lanes** | Editorial-magazine, luxury, brutalist, consumer-warm, tech-minimal, all available. Swing. | Tighter set: neutral sans + optional mono, sized for dense reading, fluid type reserved for marketing surfaces. | +| **Motion** | Choreographed entrances, scroll-driven sequences, decorative moments earn their place. | Restrained. State changes only. Animation serves feedback, not atmosphere. | +| **Color** | Full palette, Committed, or Drenched are all on the table. | Restrained by default. Accents carry meaning; color is not decoration. | +| **Density** | Whatever the narrative wants. Generous whitespace or packed rule-divided columns both valid. | Comfortable to dense. Every pixel earns its place. | +| **References** | Real-world, from the right lane. *Klim specimen pages* or *Broadsheet masthead*, not "modern SaaS". | Category best-tool. *Linear*, *Figma*, *Notion*, *Raycast*, *Stripe*. | + +The same command, `/impeccable typeset`, pulls from different fonts in the two worlds. The same command, `/impeccable animate`, picks different motion vocabularies. The same command, `/impeccable layout`, assumes different density defaults. You do not re-learn the command: you answer the register question once, and the command adapts. + +## Step 1. Decide or inherit + +If you haven't run `/impeccable teach` yet, run it now. The first question is about register: + +``` +/impeccable teach +``` + +Teach scans your codebase first and forms a hypothesis: routes like `/`, `/pricing`, `/blog`, hero sections, scroll-driven content point toward brand. Routes like `/app`, `/dashboard`, `/settings`, forms and tables point toward product. It leads with the hypothesis rather than starting cold: + +> From the codebase, this looks like a product surface, does that match your intent, or should we treat it differently? + +If the project genuinely spans both (a product with a big marketing landing), teach asks which register describes the **primary** surface. Register is per-project, not per-page, but you can override it per task when needed. + +## Step 2. Verify the register landed + +Open `PRODUCT.md` and look for the `## Register` section. It should carry a bare value, not prose: + +```markdown +## Register + +brand +``` + +If the section is missing (you're on an older `PRODUCT.md` from pre-v3.0), re-run `/impeccable teach`. It will detect the gap and add the field without re-interviewing you on everything else. + +## Step 3. Override per task when you need to + +Most of the time, register is set once and forgotten. But a product project might occasionally need a single brand surface (a launch landing, an investor one-pager) without flipping the whole project. + +You have two options: + +- **Name it in the brief.** "`/impeccable craft a launch landing for v2, brand register for this one page.`" The skill honors the override for that task only. +- **Set a per-surface register.** If the override is lasting, add a short note in `PRODUCT.md` under an explicit section: `## Register overrides: /launch is brand.` Commands that read PRODUCT.md will respect it. + +## What to try next + +- Run a command that is register-sensitive and watch the divergence: `/impeccable typeset the pricing page` on a product project vs. a brand project will pick different type families, different scale ratios, and different pairings. +- Pair with [getting started](/tutorials/getting-started) if you haven't installed Impeccable yet. +- Reach for `/impeccable document` after teach to capture the visual side (colors, components) into DESIGN.md. + +## Common issues + +- **Register keeps slipping the wrong way.** If you set `product` but commands keep producing brand-feeling output, check that `PRODUCT.md` is at the project root and the `## Register` section has a bare value (no prose, no explanation, just the word). Commands can only read what is there. +- **The hypothesis teach formed is wrong.** Disagree in the answer. Teach is asking, not telling. +- **A project is genuinely 50/50.** Pick the primary surface, then use per-task overrides for the minority one. Trying to average the two in PRODUCT.md produces worse output than committing to one. diff --git a/site/content/tutorials/critique-with-overlay.md b/site/content/tutorials/critique-with-overlay.md new file mode 100644 index 000000000..76e2f119a --- /dev/null +++ b/site/content/tutorials/critique-with-overlay.md @@ -0,0 +1,129 @@ +--- +title: Critique with the visual overlay +tagline: "Use /impeccable critique plus the browser overlay to review a live page with ground truth." +order: 4 +description: "Run a full design critique that combines LLM assessment, the automated detector, and a live browser overlay so you can see exactly which elements trigger which anti-patterns on the page you're looking at." +--- + +## What you'll build + +You will run a complete design critique against a live page in your browser, with every flagged anti-pattern highlighted directly on the element that caused it. No screenshots, no guesswork, no paragraph of findings you have to map back to the code. + +Total time: about ten minutes. + +## Prerequisites + +- Impeccable installed in your project (see [getting started](/tutorials/getting-started) if you have not). +- A harness with browser automation available (Claude Code with the Chrome extension, or similar). +- A page you want to critique, either local (`localhost:3000/pricing`) or deployed. + +## Step 1. Run /impeccable critique + +From your harness, run: + +``` +/impeccable critique the pricing page at localhost:3000/pricing +``` + +The skill kicks off two independent assessments in parallel. They run in separate sub-agents so one does not bias the other. + +### What the LLM assessment does + +The first assessment reads your source code and, if browser automation is available, opens the live page in a new tab. It walks the full impeccable skill DO/DON'T catalog and scores the page against Nielsen's 10 heuristics, the 8-item cognitive load checklist, and the brand fit from your `PRODUCT.md`. + +It labels the tab it opens with `[LLM]` in the title so you can tell which one is which. + +### What the automated detector does + +The second assessment runs `npx impeccable detect` against the page. This is deterministic: around thirty specific pattern checks that fire or do not fire. Gradient text, purple palettes, side-tab borders, nested cards, line length problems, low contrast, tiny body text, and the rest. The [full catalog](/anti-patterns) lists every rule and which layer (CLI, browser, or LLM-only) catches it. + +You get back a JSON list of every finding with its element selector, the rule that fired, and a short description. + +## Step 2. Open the visual overlay + +Impeccable ships with a visual mode that highlights every detected anti-pattern directly on the page. Here is what it looks like running on a deliberately-bad synthwave landing page: + +
    +
    + + + + Live detection overlay +
    + +
    + +Every outlined element has a floating label naming the rule that fired. Hover an outline to see the full finding. This is exactly what you will see on your own page. + +You have two ways to open it: + +1. **[Chrome extension](https://chromewebstore.google.com/detail/impeccable/bdkgmiklpdmaojlpflclinlofgjfpabf)**: one-click activation on any page. Click the Impeccable icon in the toolbar and every anti-pattern gets highlighted instantly. +2. **Inside `/impeccable critique`**: the skill opens a browser tab labeled `[Human]` with the detector active during the browser portion of the assessment. You do not need to do anything extra. + +For this tutorial, the easiest option is the Chrome extension. Install it, navigate to your pricing page, and click the Impeccable icon. You will see the overlay appear immediately on the live page. + +## Step 3. Merge the two assessments + +Back in your harness, `/impeccable critique` has finished and produced a combined report. It looks something like: + +``` +AI slop verdict: FAIL + Detected tells: gradient-text (2), ai-color-palette (1), + nested-cards (1), side-tab (3) + +Heuristic scores (avg 2.8/4): + Visibility of status: 3 (good) + Match between system and real world: 2 (partial) + Consistency and standards: 2 (partial) + ... + +Cognitive load: 3/8 failures (moderate) + Visible options at primary decision: 6 (flag) + Decision points stacked at top: yes (flag) + Progressive disclosure: absent on advanced pricing toggles + +What's working: + - Clear price hierarchy + - Strong headline + +Priority issues: + 1. Hero uses gradient text on the main price + Why: AI tell, reduces contrast, hurts scannability + Fix: solid ink color at one weight heavier + 2. Feature comparison table has 4 nested card levels + Why: visual noise, unclear hierarchy + Fix: flatten to a table with zebra striping + +Questions to answer: + - Is the free tier a real product or a funnel? + - What does a user feel when they land here from an ad vs from search? +``` + +## Step 4. Fix the findings + +The report gives you a priority list. You can work through them one at a time, ask the model to fix them all at once, or anything in between. What matters is using the overlay to verify: + +1. Keep the overlay open in one tab. +2. Make fixes in code (or ask the model to fix everything). +3. Reload. The overlay re-scans and resolved findings disappear. + +This feedback loop is the reason the overlay matters. You see fixes land in real time, and you never ship a "fix" that did not actually satisfy the rule. + +## Step 5. Re-run when you are done + +After you have worked through the priority list, run `/impeccable critique` again. The goal is a clean AI slop verdict and at least a 3.5 average on the heuristics. Cognitive load should be below 2 failures. + +If something still fires, fix it or write a suppression comment explaining why the rule does not apply in your context (the detector respects a small set of opt-out pragmas, but use them sparingly). + +## What to try next + +- [Iterate on the critique findings with Live Mode](/tutorials/iterate-live). Pick the element critique flagged, drop a comment, get three redirections hot-swapped in place, and write the accepted one back to source. +- `/impeccable audit the same page` to catch the implementation issues critique does not cover (accessibility, performance, theming). +- `/impeccable polish` if the critique report is clean and you want the last-mile refinement pass. +- `/impeccable distill` if critique flagged "too busy" or "cognitive load". Distill removes what should not be there. + +## Common issues + +- **The overlay shows no findings but critique says there are problems**. The detector catches deterministic patterns. Critique catches judgment calls. They are complementary, not redundant. +- **The LLM assessment and the detector disagree**. That is normal. The LLM is subjective. The detector is deterministic. When they disagree, look at both and make a call. +- **The overlay breaks the page layout**. Rare, but some CSS can interact with the injected overlay styles. Use the [Chrome extension](https://chromewebstore.google.com/detail/impeccable/bdkgmiklpdmaojlpflclinlofgjfpabf) for the most reliable experience, or run `npx impeccable detect` from the CLI and apply findings manually. diff --git a/site/content/tutorials/getting-started.md b/site/content/tutorials/getting-started.md new file mode 100644 index 000000000..37d1289cb --- /dev/null +++ b/site/content/tutorials/getting-started.md @@ -0,0 +1,106 @@ +--- +title: Getting started +tagline: "From zero to your first polish pass in five minutes." +order: 1 +description: "Install Impeccable, run /impeccable teach once to establish project context, and run /impeccable polish on something that already exists. The fastest path to seeing what Impeccable changes about AI-generated design." +--- + +## What you'll build + +You will end this tutorial with Impeccable installed in your project, a `PRODUCT.md` plus `DESIGN.md` pair that captures your brand, audience, and visual system, and one hand-polished page that went through a polish pass. Total time: about ten minutes. + +## Prerequisites + +- An AI coding harness: Claude Code, Cursor, Gemini CLI, Codex CLI, or any of the other supported tools. +- A project with at least one HTML or component file you want to improve. A fresh scaffolded landing page works fine. + +## How Impeccable works + +Impeccable installs as a single agent skill called `impeccable`. You access all 23 sub-commands through it: + +``` +/impeccable +``` + +For example: `/impeccable polish the pricing page`, or `/impeccable audit the checkout`. Type `/impeccable` alone to see the full list. + +If you use a command often, pin it with `/impeccable pin ` to create a standalone shortcut (for example, `/impeccable pin audit` gives you `/audit` directly). + +## Step 1. Install + +From the root of your project, run: + +``` +npx skills add pbakaus/impeccable +``` + +This auto-detects your harness and writes the skill files to the right location (e.g., `.claude/skills/`, `.cursor/skills/`). Reload your harness and type `/`. You should see `/impeccable` in the autocomplete. Type it and the skill's argument hint will show all available commands. + +## Step 2. Teach Impeccable about your project + +This is the most important step. Design without context produces generic output. The `/impeccable teach` command runs a short discovery interview and writes a `PRODUCT.md` file at the root of your project. + +Run: + +``` +/impeccable teach +``` + +The first question is about **register**: is this a brand surface (marketing site, landing page, portfolio, where design IS the product) or a product surface (app UI, dashboard, tools, where design SERVES the product)? Register shapes every downstream default, from type lanes to motion energy. See [brand vs product](/tutorials/brand-vs-product) for how the two diverge. Teach will form a hypothesis from your codebase and ask you to confirm, rather than starting cold. + +Then a handful of shorter questions: + +- **Who is this product for?** Be specific. Not "users" but "solo founders evaluating a new tool on their phone between meetings". +- **What is the brand voice in three words?** Pick real words. "Warm and mechanical and opinionated" is better than "modern and clean". +- **Any visual references?** Named brands, products, or printed objects, not adjectives. "Klim Type Foundry specimen pages", not "technical and clean". +- **Anti-references?** Things the product should explicitly not look like, equally named. + +Answer in your own words. The skill writes `PRODUCT.md` with the answers. Every future command run reads it automatically. + +Open `PRODUCT.md` and read what it wrote. Edit anything that does not feel right. The file is yours. + +## Step 2.5. Capture the visual system + +At the end of `/impeccable teach`, the skill offers to run `/impeccable document` for you. Say yes. It scans your tokens (CSS custom properties, Tailwind config, CSS-in-JS themes), extracts colors and typography, asks one grouped question for the parts that need creative input (a Creative North Star, descriptive color names), and writes a `DESIGN.md` that follows the [Google Stitch DESIGN.md format](https://stitch.withgoogle.com/docs/design-md/format/). + +On a fresh project with no tokens yet, document runs in seed mode: five quick questions about color strategy, type direction, and motion energy, and writes a scaffold you can refresh once there is code. + +`PRODUCT.md` carries strategy (who, what, why). `DESIGN.md` carries visuals (colors, typography, components). Every command reads both before generating. + +## Step 3. Polish something + +Pick a page that already exists. An about page, a settings screen, a pricing table, anything. Run: + +``` +/impeccable polish the pricing page +``` + +The skill will walk through alignment, spacing, typography, color, interaction states, transitions, and copy. It makes targeted fixes, not a rewrite. Expect a handful of small diffs that together lift the page from "done" to "done well". + +A typical polish pass looks like: + +``` +Visual alignment: fixed 3 off-grid elements +Typography: tightened h1 kerning, fixed widow on feature list +Color: replaced one hardcoded hex with --color-accent token +Interaction: added missing hover state on FAQ items +Motion: softened modal entrance to 220ms ease-out-quart +Copy: removed stray 'Lorem' placeholder +``` + +Review the diff. If something does not feel right, ask the model to explain the change. If it still does not feel right, revert it. Impeccable is opinionated but not infallible. + +## What to try next + +- [Iterate visually with Live Mode](/tutorials/iterate-live) opens a browser picker on your dev server, generates three production-quality variants per element, and writes the accepted one back to source. +- `/impeccable critique the landing page` runs a full design review with scoring, persona tests, and automated detection. It is the best way to find what to fix next. +- `/impeccable audit the checkout` runs accessibility, performance, theming, responsive, and anti-pattern checks against the implementation. Useful before shipping. +- `/impeccable craft a pricing page for enterprise customers` runs the full shape-then-build flow on a brand new feature. +- **Pin your favorites.** If you reach for one command constantly, `/impeccable pin audit` makes `/audit` work as a standalone shortcut without reversing the consolidation. +- `/impeccable redo this hero section` works too. Any description after `/impeccable` applies the design principles to the task. + +## Common issues + +- **The skill says "no design context found"**. You skipped step 2. Run `/impeccable teach` first. +- **Commands do not appear in the harness**. Reload the harness after installing. If they still do not appear, check that the installer wrote files into the expected location (`.claude/skills/`, `.cursor/skills/`, etc.) and that your harness is picking up that directory. +- **The polish pass rewrote something you liked**. Say so. Revert the change, tell the model which specific edit to undo, and continue from there. diff --git a/site/content/tutorials/iterate-live.md b/site/content/tutorials/iterate-live.md new file mode 100644 index 000000000..471357788 --- /dev/null +++ b/site/content/tutorials/iterate-live.md @@ -0,0 +1,123 @@ +--- +title: Iterate on UI with Live Mode +tagline: "Pick an element, generate three variants, accept one. Canvas-like iteration without leaving your code." +order: 2 +description: "Use /impeccable live to visually iterate on a real element in your dev server: pick, annotate, generate three variants, accept the one you want, and have it written back to source." +--- + +## What you'll build + +You will use `/impeccable live` on your dev server to iterate on a single piece of UI (a hero, a card, a section) and end with one of three AI-generated variants written back to source as real code. You'll see the canvas-style picking, annotation, and three-up cycling flow. + +Total time: about ten minutes. Most of that is picking what to iterate on. + +## Prerequisites + +- Impeccable installed (see [getting started](/tutorials/getting-started) if you have not). Run `/impeccable teach` first if you haven't yet: variants lean on `PRODUCT.md` and `DESIGN.md` for brand fit. +- A running dev server with HMR (Vite, Next.js, SvelteKit, Astro, Nuxt, Bun) OR a static HTML file open in a browser. +- A page with at least one piece of UI you'd like to iterate on. A newsletter card, a hero, a pricing tier, something small enough to hold in your head. + +## Step 1. Start live mode + +From your harness, run: + +``` +/impeccable live +``` + +The skill starts a small local helper server on port 8400 and injects a ` + diff --git a/site/pages/cases/neo-mirai.astro b/site/pages/cases/neo-mirai.astro new file mode 100644 index 000000000..f001b7f7f --- /dev/null +++ b/site/pages/cases/neo-mirai.astro @@ -0,0 +1,105 @@ +--- +import Base from '../../layouts/Base.astro'; +import '../../styles/sub-pages.css'; +--- + + + +
    +
    + Designing with Impeccable + Case study +

    Neo Mirai: generated mock to shipped page.

    +

    A retro-futurist AI design conference became a real static site through the full Impeccable loop: visual reference, brand direction, implementation, asset regeneration, responsive fixes, animation polish, and browser verification.

    + +
    + + The shipped Neo Mirai conference website hero and agenda, showing a warm retro-futurist Tokyo skyline and large NEO MIRAI headline. + +
    + +
    +
    + 01 · Visualize + Generated hi-fi website mock for the Neo Mirai conference page. +
    Hi-fi north star. A concrete composition to build toward, not a paragraph to interpret.
    +
    +
    + 02 · Shape + Generated Neo Mirai brand toolkit plate with palette, typography, symbols, and application mockups. +
    Brand toolkit. Identity, palette, type, image language, and motion direction before code.
    +
    +
    + 03 · Ship + + Full-page screenshot of the implemented Neo Mirai website. + +
    Implemented page. Semantic HTML, responsive layout, regenerated assets, real states, and polish.
    +
    +
    + +
    +
    + +

    The mock did not become a screenshot. It became a system.

    +
    +
    +
    +

    Composition matching

    +

    The build preserved the mock's asymmetric rhythm: full-bleed hero artwork, dark agenda block, orange manifesto band, drifting installation grid, and structured ticket field.

    +
    +
    +

    Asset regeneration

    +

    Image-native pieces stayed image-native. The manifesto city, speaker portraits, pine overlay, and supporting illustrations were regenerated or isolated where raster detail mattered.

    +
    +
    +

    Browser iteration

    +

    The page was tested in the browser after each pass. Overlaps, bad crops, active nav state, speaker carousel behavior, mobile heights, and large-viewport balance were fixed visually.

    +
    +
    +
    + +
    +
    + Generated hi-fi Neo Mirai page mock. +
    North-star mock The reference image preserved the intended rhythm: full-bleed hero, dark agenda, speaker carousel, installation field, manifesto, and tickets.
    +
    +
    + + Full-page screenshot of the implemented Neo Mirai website. + +
    Live build The shipped page keeps the same visual ambition while becoming responsive markup, real links, carousel controls, hover states, and browser-tested layout.
    +
    +
    + +
    +
    + +

    Use craft when the output has to feel designed.

    +

    /impeccable craft is the right command when a feature needs shaping, visual direction, implementation, and browser iteration in one run.

    +
    +
    /impeccable craft retro-futurist AI design conference website
    +
    + + + + diff --git a/public/designing/index.html b/site/pages/designing/index.astro similarity index 72% rename from public/designing/index.html rename to site/pages/designing/index.astro index 8f2e1eea8..e37d42bd8 100644 --- a/public/designing/index.html +++ b/site/pages/designing/index.astro @@ -1,43 +1,15 @@ - - - - - - Designing with Impeccable - - - - - - - - - - - - - - -
    +--- +import Base from '../../layouts/Base.astro'; +import '../../styles/sub-pages.css'; +--- + +
    @@ -50,7 +22,7 @@
    The core loop
    - + 01 Start @@ -90,10 +62,10 @@ - - - - + + + +
    designing impeccable @@ -156,7 +128,7 @@
    - Auto-generated brand toolkit plate: identity lockups, colour palette, type specimens, icon system, and application mocks for a fictional AI design conference, rendered in warm earth tones. + Auto-generated brand toolkit plate: identity lockups, colour palette, type specimens, icon system, and application mocks for a fictional AI design conference, rendered in warm earth tones.
    Shape @@ -166,7 +138,7 @@
    - Auto-generated hi-fi landing-page mock: a long vertical editorial comp for a fictional Tokyo AI design conference, in warm earth tones with committed serif display type. + Auto-generated hi-fi landing-page mock: a long vertical editorial comp for a fictional Tokyo AI design conference, in warm earth tones with committed serif display type.
    Visualize @@ -176,7 +148,7 @@
    - Full-page screenshot of the implemented Neo Mirai website. + Full-page screenshot of the implemented Neo Mirai website.
    Ship @@ -230,9 +202,9 @@
    - + 2 / 3 - +
    @@ -240,7 +212,7 @@ / Pick - +
    @@ -404,28 +376,28 @@
    • - +
      Running both Impeccable and Anthropic's frontend-design skill

      Anthropic still promotes their skill in Claude Code, but it's been unmaintained and is now behind on recommended patterns. Run both and they collide on vocabulary, cancelling each other out. Pick one.

    • - +
      Pinning every command

      Pinning brings back /audit, /polish, /critique as shortcuts. Pin everything and you've re-exploded the / menu the v3.0 consolidation cleaned up. Pin the two or three you reach for daily.

    • - +
      Skipping teach

      Commands still run without PRODUCT.md and DESIGN.md. They default to generic SaaS patterns. The floor is meaningfully higher with context. Run teach once; every later command benefits.

    • - +
      Treating it like a linter

      Impeccable is an opinionated design partner, not a validator. It has a point of view. Push back with a reason and it'll work with you. Ignore the opinion without a reason and output gets worse, not better.

      @@ -448,132 +420,132 @@
      -
    - - wrapper.addEventListener('pointerleave', (e) => { - endDrag(e); - if (hovering) { - hovering = false; - setTarget(DEFAULT_POSITION); - } - }); - } - })(); - - - + diff --git a/site/pages/docs/[...slug].astro b/site/pages/docs/[...slug].astro new file mode 100644 index 000000000..1a725538b --- /dev/null +++ b/site/pages/docs/[...slug].astro @@ -0,0 +1,44 @@ +--- +import { getCollection, render } from 'astro:content'; +import fs from 'node:fs'; +import path from 'node:path'; +import Doc from '../../layouts/Doc.astro'; +import { SKILL_CATEGORIES } from '../../data/sub-pages-data'; + +export async function getStaticPaths() { + const entries = await getCollection('skills'); + + const metadataPath = path.join(process.cwd(), 'source/skills/impeccable/scripts/command-metadata.json'); + const commandMetadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8')); + + const allCommands = entries.map(e => ({ + slug: e.id, + category: SKILL_CATEGORIES[e.id] || 'system', + })); + + return entries.map(entry => ({ + params: { slug: entry.id }, + props: { + entry, + metadata: commandMetadata[entry.id] || { description: entry.data.tagline, argumentHint: '' }, + allCommands, + }, + })); +} + +const { entry, metadata, allCommands } = Astro.props; +const { Content } = await render(entry); +const slug = entry.id; +const category = SKILL_CATEGORIES[slug] || 'system'; +--- + + + + diff --git a/site/pages/docs/index.astro b/site/pages/docs/index.astro new file mode 100644 index 000000000..cb1ce0285 --- /dev/null +++ b/site/pages/docs/index.astro @@ -0,0 +1,119 @@ +--- +import { getCollection } from 'astro:content'; +import Base from '../../layouts/Base.astro'; +import '../../styles/sub-pages.css'; +import { + SKILL_CATEGORIES, + CATEGORY_ORDER, + CATEGORY_LABELS, + CATEGORY_DESCRIPTIONS, +} from '../../data/sub-pages-data'; + +const entries = await getCollection('skills'); + +const grouped: Record = {}; +for (const cat of CATEGORY_ORDER) { + grouped[cat] = entries + .filter(e => SKILL_CATEGORIES[e.id] === cat) + .sort((a, b) => a.id.localeCompare(b.id)); +} + +const allCommands = entries.map(e => ({ + slug: e.id, + category: SKILL_CATEGORIES[e.id] || 'system', +})); + +const sidebarGroups: Record = {}; +for (const cat of CATEGORY_ORDER) { + sidebarGroups[cat] = allCommands + .filter(c => c.category === cat) + .sort((a, b) => a.slug.localeCompare(b.slug)); +} +--- + + +
    + + +
    +
    +
    +

    Commands

    +

    23 commands for design fluency. One skill, six categories, from shaping a new feature to hardening it for production.

    +
    + + {CATEGORY_ORDER.map(cat => ( +
    +
    +

    {CATEGORY_LABELS[cat]}

    +

    {CATEGORY_DESCRIPTIONS[cat]}

    +
    +
    + {grouped[cat].map(entry => ( + +

    /impeccable {entry.id}

    +

    {entry.data.tagline}

    +
    + ))} +
    +
    + ))} +
    +
    +
    + + + + + diff --git a/public/index.html b/site/pages/index.astro similarity index 92% rename from public/index.html rename to site/pages/index.astro index 436864c07..24467731f 100644 --- a/public/index.html +++ b/site/pages/index.astro @@ -1,76 +1,39 @@ - - - - - - + + + - - - Impeccable: The missing upgrade to Anthropic's impeccable skill - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - +