From 22fa18460c3f65edef3e14c69a856ad0a1e9b2c6 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:43:53 -0500 Subject: [PATCH 01/26] feat(website): add base layout, site config, and UI component library - BaseLayout.astro: custom layout replacing StarlightPage wrapper with head, navigation, footer, and slot-based content area - Site config (site.ts, nav.ts): centralized site metadata, navigation links, and footer links - Layout components: Navigation.astro (desktop + mobile drawer), Footer.astro (links, social, copyright) - UI primitives: Button, Card, Badge, Icon, Container, Accordion, 8BitButton, RetroResourceCard - Add copilot-icon.png for navigation branding --- website/public/copilot-icon.png | Bin 0 -> 141122 bytes website/src/components/layout/Footer.astro | 71 +++++ .../src/components/layout/Navigation.astro | 230 +++++++++++++++ website/src/components/ui/8BitButton.astro | 267 ++++++++++++++++++ website/src/components/ui/Accordion.astro | 114 ++++++++ website/src/components/ui/Badge.astro | 55 ++++ website/src/components/ui/Button.astro | 138 +++++++++ website/src/components/ui/Card.astro | 27 ++ website/src/components/ui/Container.astro | 22 ++ website/src/components/ui/Icon.astro | 108 +++++++ .../src/components/ui/RetroResourceCard.astro | 131 +++++++++ website/src/config/nav.ts | 20 ++ website/src/config/site.ts | 26 ++ website/src/layouts/BaseLayout.astro | 41 +++ 14 files changed, 1250 insertions(+) create mode 100644 website/public/copilot-icon.png create mode 100644 website/src/components/layout/Footer.astro create mode 100644 website/src/components/layout/Navigation.astro create mode 100644 website/src/components/ui/8BitButton.astro create mode 100644 website/src/components/ui/Accordion.astro create mode 100644 website/src/components/ui/Badge.astro create mode 100644 website/src/components/ui/Button.astro create mode 100644 website/src/components/ui/Card.astro create mode 100644 website/src/components/ui/Container.astro create mode 100644 website/src/components/ui/Icon.astro create mode 100644 website/src/components/ui/RetroResourceCard.astro create mode 100644 website/src/config/nav.ts create mode 100644 website/src/config/site.ts create mode 100644 website/src/layouts/BaseLayout.astro diff --git a/website/public/copilot-icon.png b/website/public/copilot-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4d82c8fbe817deda7ed4a56c9e00d6db39cc30a7 GIT binary patch literal 141122 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%0A8n16y9N~wo;Vu-05dE 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�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 +

+ {SITE.title} Β· static-first Astro implementation +

+
+ + + diff --git a/website/src/components/layout/Navigation.astro b/website/src/components/layout/Navigation.astro new file mode 100644 index 000000000..be538ebe5 --- /dev/null +++ b/website/src/components/layout/Navigation.astro @@ -0,0 +1,230 @@ +--- +import { primaryNav } from '../../config/nav'; +import { SITE } from '../../config/site'; +import Icon from '../ui/Icon.astro'; + +const currentPath = Astro.url.pathname.replace(/\/$/, '') || '/'; +--- + + + + + diff --git a/website/src/components/ui/8BitButton.astro b/website/src/components/ui/8BitButton.astro new file mode 100644 index 000000000..c4013d3a0 --- /dev/null +++ b/website/src/components/ui/8BitButton.astro @@ -0,0 +1,267 @@ +--- +export interface Props { + href?: string; + variant?: 'default' | 'secondary' | 'ghost' | 'link'; + size?: 'sm' | 'md' | 'lg' | 'icon'; + type?: 'button' | 'submit' | 'reset'; + disabled?: boolean; + ariaLabel?: string; +} + +const { href, variant = 'default', size = 'md', type = 'button', disabled = false, ariaLabel } = Astro.props; +const Tag = href ? 'a' : 'button'; +--- + + + + + + {variant !== 'ghost' && variant !== 'link' && size !== 'icon' && ( + <> +
+
+
+
+
+
+
+
+
+
+ + {variant !== 'secondary' && ( + <> +
+
+
+
+ + )} + + )} + + {size === 'icon' && ( + <> +
+
+
+
+
+
+ + )} + + + + diff --git a/website/src/components/ui/Accordion.astro b/website/src/components/ui/Accordion.astro new file mode 100644 index 000000000..31988e064 --- /dev/null +++ b/website/src/components/ui/Accordion.astro @@ -0,0 +1,114 @@ +--- +export interface Props { + question: string; + answer: string; + index: number; +} +const { question, answer, index } = Astro.props; +const panelId = `faq-panel-${index}`; +const buttonId = `faq-btn-${index}`; +--- +
+ + +
+ + + + diff --git a/website/src/components/ui/Badge.astro b/website/src/components/ui/Badge.astro new file mode 100644 index 000000000..1d4126cb2 --- /dev/null +++ b/website/src/components/ui/Badge.astro @@ -0,0 +1,55 @@ +--- +import type { Accent } from '../../lib/types'; + +export interface Props { + tone?: Accent | 'neutral'; +} + +const { tone = 'neutral' } = Astro.props; +--- + + + + + diff --git a/website/src/components/ui/Button.astro b/website/src/components/ui/Button.astro new file mode 100644 index 000000000..af17b03b3 --- /dev/null +++ b/website/src/components/ui/Button.astro @@ -0,0 +1,138 @@ +--- +export interface Props { + href?: string; + variant?: 'primary' | 'secondary' | 'ghost' | 'danger'; + size?: 'sm' | 'md'; + type?: 'button' | 'submit' | 'reset'; + disabled?: boolean; + ariaLabel?: string; +} + +const { + href, + variant = 'primary', + size = 'md', + type = 'button', + disabled = false, + ariaLabel, +} = Astro.props; +--- + +{ + href ? ( + + + + ) : ( + + ) +} + + diff --git a/website/src/components/ui/Card.astro b/website/src/components/ui/Card.astro new file mode 100644 index 000000000..f7e6234d5 --- /dev/null +++ b/website/src/components/ui/Card.astro @@ -0,0 +1,27 @@ +--- +export interface Props { + class?: string; + highlighted?: boolean; +} + +const { highlighted = false } = Astro.props; +--- +
+ +
+ + diff --git a/website/src/components/ui/Container.astro b/website/src/components/ui/Container.astro new file mode 100644 index 000000000..211176cc4 --- /dev/null +++ b/website/src/components/ui/Container.astro @@ -0,0 +1,22 @@ +--- +export interface Props { + narrow?: boolean; +} + +const { narrow = false } = Astro.props; +--- +
+ +
+ + diff --git a/website/src/components/ui/Icon.astro b/website/src/components/ui/Icon.astro new file mode 100644 index 000000000..b5c799572 --- /dev/null +++ b/website/src/components/ui/Icon.astro @@ -0,0 +1,108 @@ +--- +/** + * Pixel-art SVG icon component. + * + * Icons are 24Γ—24 pixel-grid SVGs rendered with crispEdges for a + * retro-futuristic console aesthetic. + */ +export interface Props { + name: string; + label?: string; + size?: number; +} + +const { name, label, size = 20 } = Astro.props; + +const paths: Record = { + brand: + 'M12 3h3v3h3v3h3v6h-3v3h-3v3H9v-3H6v-3H3V9h3V6h3V3h3Zm0 4H9v3H7v4h2v3h6v-3h2v-4h-2V7h-3Z', + github: + 'M12 .5a12 12 0 0 0-3.8 23.39c.6.11.82-.26.82-.58v-2.04c-3.34.73-4.04-1.42-4.04-1.42-.55-1.39-1.34-1.76-1.34-1.76-1.09-.75.08-.73.08-.73 1.2.08 1.84 1.24 1.84 1.24 1.08 1.83 2.82 1.3 3.5.99.11-.78.43-1.3.78-1.6-2.66-.31-5.46-1.34-5.46-5.94 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.53.12-3.18 0 0 1.01-.32 3.3 1.23a11.44 11.44 0 0 1 6.01 0c2.29-1.55 3.3-1.23 3.3-1.23.66 1.65.25 2.88.12 3.18.77.84 1.23 1.91 1.23 3.22 0 4.62-2.81 5.63-5.49 5.93.44.38.83 1.11.83 2.24v3.32c0 .32.21.7.83.58A12 12 0 0 0 12 .5Z', + search: + 'M7 4h7v2h2v7h-2v2h-2v2h2v2h2v2h-3v-2h-2v-2H9v-2H7v-2H5V6h2V4Zm1 3v5h5V7H8Z', + copy: 'M7 4h11v11h-3v3H4V7h3V4Zm2 3h6V6H9v1Zm-3 3v6h6v-6H6Zm8 3h2V9h-2v4Z', + check: 'M9 18H7v-2H5v-3h3v2h2v-2h2v-2h2V9h2V7h3v3h-2v2h-2v2h-2v2h-2v2H9Z', + x: 'M5 5h4v3h2v2h2V8h2V5h4v4h-3v2h-2v2h2v2h3v4h-4v-3h-2v-2h-2v2H9v3H5v-4h3v-2h2v-2H8V9H5V5Z', + bolt: 'M12 2h5l-3 7h4L9 22l2-9H6l6-11Z', + robot: + 'M10 3h4v3h4v3h2v8h-2v2H6v-2H4V9h2V6h4V3Zm-2 7v7h8v-7H8Zm2 2h2v3h-2v-3Zm4 0h2v3h-2v-3Z', + arrow: 'M4 11h11V8h3v2h2v4h-2v2h-3v-3H4v-2Z', + 'arrow-left': 'M20 11H9V8H6v2H4v4h2v2h3v-3h11v-2Z', + 'arrow-right': 'M4 11h11V8h3v2h2v4h-2v2h-3v-3H4v-2Z', + menu: 'M4 6h16v3H4V6Zm0 5h16v3H4v-3Zm0 5h16v3H4v-3Z', + agents: + 'M9 3h6v3h3v3h2v8h-2v2H6v-2H4V9h2V6h3V3Zm-1 7v6h8v-6H8Zm1 2h2v2H9v-2Zm4 0h2v2h-2v-2Z', + instructions: + 'M6 3h9l3 3v15H6V3Zm2 3v12h8V8h-3V6H8Zm1 4h6v2H9v-2Zm0 4h5v2H9v-2Z', + skills: + 'M11 3h2v4h4v2h-4v4h-2V9H7V7h4V3Zm-6 9h5v5H5v-5Zm9 3h5v5h-5v-5Z', + hooks: + 'M6 4h8v3h-2v4h3v-2h3v4h-3v2h-3v5H8v-5H6v-4h2V7H6V4Zm4 3v4h2V7h-2Z', + workflows: + 'M5 4h6v6H9v2h6v-2h-2V4h6v6h-2v4H9v2h2v4H5v-6h2v-4H5V4Zm2 2v2h2V6H7Zm8 0v2h2V6h-2Zm-8 10v2h2v-2H7Z', + plugins: + 'M8 3h3v4h2V3h3v4h2v5h-3v3h3v3h-3v3h-3v-3H9v3H6v-3H3v-3h3v-3H3V7h5V3Zm1 7v5h6v-5H9Z', + tools: + 'M4 5h16v10H4V5Zm2 2v6h12V7H6Zm2 11h8v2H8v-2Zm1-9h2v2H9V9Zm3 0h4v2h-4V9Z', + learning: + 'M5 4h7v3h2V4h5v15h-6v2h-2v-2H5V4Zm2 3v9h4V7H7Zm7 2v7h3V7h-3v2Z', + open: 'M5 5h8v3H8v8h8v-5h3v8H5V5Zm9-2h7v7h-3V8h-2V6h-2V3Z', + download: + 'M11 3h3v8h3l-5 6-5-6h3V3h1Zm-6 15h14v3H5v-3Z', + docs: 'M5 4h10l4 4v12H5V4Zm3 4h5V6H8v2Zm0 4h8v2H8v-2Zm0 4h6v2H8v-2Z', + filter: 'M4 5h16v3h-2v2h-4v8l-4 2v-10H6V8H4V5Z', + star: 'M11 3h2v4h4v2h3v3h-5v3h-2v3h-2v-3H9v-3H4V9h3V7h4V3Z', + delete: + 'M7 4h10v2h5v3h-2v11h-2v2H6v-2H4V9H2V6h5V4Zm2 3v10h8V7H9Zm1 2h2v6h-2V9Zm4 0h2v6h-2V9Z', + archive: + 'M4 5h16v3h-2v2H6V8H4V5Zm2 5v9h12v-9H6Zm4 2h4v2h-4v-2Z', + share: + 'M18 8h3v3h-3v2h-2v2h-2v2h-2v2h-2v2H8v-2H6v-2H4v-2H2v-3h3v2h2v2h2v2h2v-2h2v-2h2v-2h2V8Zm-4-4h4v4h-4V4Z', + close: + 'M5 5h4v3h2v2h2V8h2V5h4v4h-3v2h-2v2h2v2h3v4h-4v-3h-2v-2h-2v2H9v3H5v-4h3v-2h2v-2H8V9H5V5Z', + terminal: + 'M3 5h18v14H3V5Zm2 3v8h14V8H5Zm2 2h2v2H7v-2Zm3 3h5v2h-5v-2Z', + warning: + 'M11 3h2v2h2v4h2v4h2v6H5v-6h2V9h2V5h2V3Zm0 6v5h2V9h-2Zm0 7v2h2v-2h-2Z', + error: 'M7 4h10v2h2v10h-2v2H7v-2H5V6h2V4Zm2 4v2h6V8H9Zm0 4v2h6v-2H9Z', + info: 'M9 4h6v2h2v12h-2v2H9v-2H7V6h2V4Zm2 3v2h2V7h-2Zm0 4v6h2v-6h-2Z', + dot: 'M10 10h4v4h-4v-4Z', + 'filter-alt': 'M4 5h16v2h-2v3h-4v8H9v-8H5V7H4V5Zm2 2v2h4v8h2v-8h4V7H6Z', + 'keyboard-arrow-down': 'M7 9h2v2h2v2h2v-2h2V9h2v2h-2v2h-2v2h-2v2h-2v-2H9v-2H7v-2H5V9h2Z', +} as const; +--- + +{ + label ? ( + + + + ) : ( + + ) +} + + diff --git a/website/src/components/ui/RetroResourceCard.astro b/website/src/components/ui/RetroResourceCard.astro new file mode 100644 index 000000000..a40c0b7c1 --- /dev/null +++ b/website/src/components/ui/RetroResourceCard.astro @@ -0,0 +1,131 @@ +--- +import Icon from './Icon.astro'; +import type { Accent } from '../../lib/types'; + +interface Props { + title: string; + description: string; + href: string; + icon: string; + accent: Accent; + /** Section name for CTA (e.g. "Skills"). Falls back to title if omitted. */ + label?: string; +} + +const { title, description, href, icon, accent, label } = Astro.props; +const ctaLabel = label ?? title; +--- + + + + diff --git a/website/src/config/nav.ts b/website/src/config/nav.ts new file mode 100644 index 000000000..0a2981040 --- /dev/null +++ b/website/src/config/nav.ts @@ -0,0 +1,20 @@ +import type { NavItem } from '../lib/types'; + +/** + * Primary navigation structure. + * + * Matches the routes defined in the spec and the Starlight sidebar + * (see `astro.config.mjs` for comparison). The Learning Hub routes + * are included here so the main nav covers all top-level sections. + */ + +export const primaryNav: NavItem[] = [ + { label: 'Agents', href: '/agents/' }, + { label: 'Instructions', href: '/instructions/' }, + { label: 'Skills', href: '/skills/' }, + { label: 'Hooks', href: '/hooks/' }, + { label: 'Workflows', href: '/workflows/' }, + { label: 'Plugins', href: '/plugins/' }, + { label: 'Tools', href: '/tools/' }, + { label: 'Learning Hub', href: '/learning-hub/' }, +]; diff --git a/website/src/config/site.ts b/website/src/config/site.ts new file mode 100644 index 000000000..b65941e5b --- /dev/null +++ b/website/src/config/site.ts @@ -0,0 +1,26 @@ +/** + * Site-wide configuration constants. + * + * Replacements for `virtual:starlight/user-config` and inline config + * previously defined in `astro.config.mjs`. + */ + +export const SITE = { + title: 'Awesome GitHub Copilot', + description: + 'Community-contributed agents, instructions, and skills to enhance your GitHub Copilot experience', + url: 'https://awesome-copilot.github.com/', + base: '/', + favicon: '/images/favicon.svg', + socialImage: '/images/social-image.png', + editBaseUrl: + 'https://github.com/github/awesome-copilot/edit/staged/website/', + themeColor: '#0A0D0D', +} as const; + +/** Public repository metadata. */ +export const REPO = { + owner: 'github', + name: 'awesome-copilot', + url: 'https://github.com/github/awesome-copilot', +} as const; diff --git a/website/src/layouts/BaseLayout.astro b/website/src/layouts/BaseLayout.astro new file mode 100644 index 000000000..19a5ee71b --- /dev/null +++ b/website/src/layouts/BaseLayout.astro @@ -0,0 +1,41 @@ +--- +import Navigation from '../components/layout/Navigation.astro'; +import Footer from '../components/layout/Footer.astro'; +import { SITE } from '../config/site'; +import '../styles/global.css'; + +export interface Props { + title?: string; + description?: string; +} + +const { + title = `${SITE.title} Β· GitHub Copilot resource console`, + description = SITE.description, +} = Astro.props; +--- + + + + + + + + + + + + {title} + + + + +
+ +
+
+ + From 62ed5d1f9f2369f43a1f8b819828eb66eb9ab5ff Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:44:10 -0500 Subject: [PATCH 02/26] feat(website): add resource components and page sections Resource components: - ResourceCard.astro: pixel-art card with type icon, badges, and hover - ResourceFilters.astro: terminal-style filter bar with checkboxes - ResourceGrid.astro: responsive 3-column grid layout - ResourceModal.astro: detail modal with client:only interactivity - ResourceSearch.astro: search input with debounce and clear button Page sections: - HeroSection.astro: hero banner with gradient and CTA - Hero.astro: legacy hero (kept for backward compat) - CTASection.astro: call-to-action with retro styling - FaqSection.astro: accordion-based FAQ - SearchBar.astro: standalone search bar component - SectionCards.astro: resource type summary cards - Stats.astro: pixel stat counter cards --- .../components/resource/ResourceCard.astro | 237 +++++++++++++++++ .../components/resource/ResourceFilters.astro | 164 ++++++++++++ .../components/resource/ResourceGrid.astro | 43 ++++ .../components/resource/ResourceModal.astro | 238 ++++++++++++++++++ .../components/resource/ResourceSearch.astro | 105 ++++++++ .../src/components/sections/CTASection.astro | 25 ++ .../src/components/sections/FaqSection.astro | 82 ++++++ website/src/components/sections/Hero.astro | 133 ++++++++++ .../src/components/sections/HeroSection.astro | 193 ++++++++++++++ .../src/components/sections/SearchBar.astro | 141 +++++++++++ .../components/sections/SectionCards.astro | 168 +++++++++++++ website/src/components/sections/Stats.astro | 83 ++++++ 12 files changed, 1612 insertions(+) create mode 100644 website/src/components/resource/ResourceCard.astro create mode 100644 website/src/components/resource/ResourceFilters.astro create mode 100644 website/src/components/resource/ResourceGrid.astro create mode 100644 website/src/components/resource/ResourceModal.astro create mode 100644 website/src/components/resource/ResourceSearch.astro create mode 100644 website/src/components/sections/CTASection.astro create mode 100644 website/src/components/sections/FaqSection.astro create mode 100644 website/src/components/sections/Hero.astro create mode 100644 website/src/components/sections/HeroSection.astro create mode 100644 website/src/components/sections/SearchBar.astro create mode 100644 website/src/components/sections/SectionCards.astro create mode 100644 website/src/components/sections/Stats.astro diff --git a/website/src/components/resource/ResourceCard.astro b/website/src/components/resource/ResourceCard.astro new file mode 100644 index 000000000..34c0f3088 --- /dev/null +++ b/website/src/components/resource/ResourceCard.astro @@ -0,0 +1,237 @@ +--- +/** + * Resource card β€” renders a single ResultItem inside a catalog listing. + * + * Exposes data attributes so client scripts can wire click-to-modal and + * action handlers (install, download, share, GitHub). + */ +import type { ResultItem } from '../../lib/types'; +import Badge from '../ui/Badge.astro'; +import Icon from '../ui/Icon.astro'; + +export interface Props { + item: ResultItem; + index?: number; +} + +const { item, index = 0 } = Astro.props; + +// Compute human-readable accent colour label +const accentLabel = item.accent ?? 'purple'; +const lastUpdated = item.detail ? item.detail : ''; +--- +
+ +
+
+ {item.label} + {item.model && {item.model}} +
+ +
+ {item.actions?.includes('install') && ( + + )} + {item.actions?.includes('download') && ( + + )} + {item.actions?.includes('share') && ( + + )} + {item.actions?.includes('github') && item.detail && ( + + + + )} +
+
+ + +

+ {item.title} +

+

{item.description}

+ + + {item.tags.length > 0 && ( +
+ {item.tags.map((tag) => ( + {tag} + ))} +
+ )} + + +
+ {item.items != null && ( + + {item.items} {item.items === 1 ? 'item' : 'items'} + + )} + {item.category && ( + {item.category} + )} +
+
+ + diff --git a/website/src/components/resource/ResourceFilters.astro b/website/src/components/resource/ResourceFilters.astro new file mode 100644 index 000000000..92c0aadc2 --- /dev/null +++ b/website/src/components/resource/ResourceFilters.astro @@ -0,0 +1,164 @@ +--- +/** + * Resource filter bar β€” renders filter controls for catalog pages. + * + * Filter configs drive the UI; actual filtering is handled by + * `scripts/filters.ts` at runtime. The rendered controls expose the + * selectors and data attributes that the client script expects. + * + * This component is reusable across all catalog pages. + */ +import type { FilterConfig } from '../../scripts/filters'; + +export interface Props { + filters: FilterConfig[]; + /** Text for the clear-filters action. */ + clearLabel?: string; +} + +const { filters, clearLabel = 'Clear all filters' } = Astro.props; +--- + + + diff --git a/website/src/components/resource/ResourceGrid.astro b/website/src/components/resource/ResourceGrid.astro new file mode 100644 index 000000000..d88e6d1ec --- /dev/null +++ b/website/src/components/resource/ResourceGrid.astro @@ -0,0 +1,43 @@ +--- +/** + * Resource grid layout β€” renders a responsive CSS grid of cards. + * + * Reusable by all catalog pages (agents, skills, instructions, hooks, + * workflows, plugins, tools). + * + * Uses a named slot so pages can pass both the resource cards and an + * empty-state fallback when there are no results. + */ +export interface Props { + /** Optional HTML id for the grid container. */ + id?: string; + /** Optional additional CSS classes. */ + class?: string; +} + +const { id, class: className } = Astro.props; +--- +
+ +
+ + diff --git a/website/src/components/resource/ResourceModal.astro b/website/src/components/resource/ResourceModal.astro new file mode 100644 index 000000000..33df34670 --- /dev/null +++ b/website/src/components/resource/ResourceModal.astro @@ -0,0 +1,238 @@ +--- +/** + * Resource file-viewer modal shell. + * + * This component provides the modal markup and CSS. It is controlled + * by `scripts/modal-client.ts` and the page-level scripts that call + * `openModal()` / `closeModal()`. + * + * States: loading, raw (code), rendered (markdown), error. + * Keyboard: Escape β†’ close, Tab cycles inside the modal. + * + * The modal content is populated by client scripts; the server renders + * only the structural shell with aria attributes for accessibility. + */ +--- + + + diff --git a/website/src/components/resource/ResourceSearch.astro b/website/src/components/resource/ResourceSearch.astro new file mode 100644 index 000000000..3c14de7a7 --- /dev/null +++ b/website/src/components/resource/ResourceSearch.astro @@ -0,0 +1,105 @@ +--- +/** + * Scoped search input for catalog pages. + * + * Wired by search-client.ts at runtime. Works with the + * `initSearch()` orchestrator for fuzzy search + URL sync. + */ +import Icon from '../ui/Icon.astro'; + +export interface Props { + id?: string; + placeholder?: string; +} + +const { + id = 'resource-search', + placeholder = 'Search…', +} = Astro.props; +--- + + + diff --git a/website/src/components/sections/CTASection.astro b/website/src/components/sections/CTASection.astro new file mode 100644 index 000000000..d15c6be2a --- /dev/null +++ b/website/src/components/sections/CTASection.astro @@ -0,0 +1,25 @@ +--- +/** + * CTA section β€” ported from awesome-copilot-jh base UI. + * + * Renders the "next step" terminal-label CTA box with pixel button. + */ +import BitButton from '../ui/8BitButton.astro'; +import Container from '../ui/Container.astro'; +import Icon from '../ui/Icon.astro'; +--- +
+ +
+

$ next step

+

Ship Copilot resources without losing the system model.

+ Explore console +
+
+
+ diff --git a/website/src/components/sections/FaqSection.astro b/website/src/components/sections/FaqSection.astro new file mode 100644 index 000000000..2812a7aec --- /dev/null +++ b/website/src/components/sections/FaqSection.astro @@ -0,0 +1,82 @@ +--- +/** + * FAQ section β€” ported from awesome-copilot-jh base UI. + * + * Renders the accordion FAQ list with terminal kicker. + */ +import Accordion from '../ui/Accordion.astro'; +import Container from '../ui/Container.astro'; + +const faqItems = [ + { + question: 'What platforms do you support?', + answer: 'We support GitHub Copilot on VS Code, Visual Studio, JetBrains IDEs, Neovim, and any editor that supports the Copilot extension. Our agents and instructions work across all platforms where Copilot runs.' + }, + { + question: 'Is there a free tier?', + answer: 'Yes. The marketplace and all public resources are free to browse. Some advanced agents and private team instructions may require a subscription, but the core catalog is always open source.' + }, + { + question: 'Can I cancel anytime?', + answer: 'Absolutely. There are no long-term contracts. You can cancel your subscription at any time from your account settings, and you will retain access until the end of your current billing period.' + }, + { + question: 'Do you offer refunds?', + answer: 'We offer refunds within 14 days of purchase if you are not satisfied. Just contact support and we will process it β€” no questions asked.' + }, + { + question: 'How do I upgrade my plan?', + answer: 'You can upgrade from your account dashboard at any time. The change takes effect immediately, and you will be charged a prorated amount for the remainder of the billing cycle.' + } +]; +--- +
+ +
+

$ cat ~/faq.md

+

Frequently Asked Questions

+

Got questions? We've got answers.

+
+
+ {faqItems.map((item, index) => ( + + ))} +
+
+
+ + diff --git a/website/src/components/sections/Hero.astro b/website/src/components/sections/Hero.astro new file mode 100644 index 000000000..9be8e1ddd --- /dev/null +++ b/website/src/components/sections/Hero.astro @@ -0,0 +1,133 @@ +--- +/** + * Homepage hero section. + * + * Renders the main heading, subtitle, and a primary CTA (GitHub link). + */ +import Container from '../ui/Container.astro'; +import Icon from '../ui/Icon.astro'; +import { REPO } from '../../config/site'; + +export interface Props { + title: string; + subtitle: string; +} + +const { title, subtitle } = Astro.props; +--- +
+ +
+

+ {title} +

+

+ {subtitle} +

+ +
+
+
+ + diff --git a/website/src/components/sections/HeroSection.astro b/website/src/components/sections/HeroSection.astro new file mode 100644 index 000000000..7ad7ee8f7 --- /dev/null +++ b/website/src/components/sections/HeroSection.astro @@ -0,0 +1,193 @@ +--- +/** + * Homepage hero section β€” ported from awesome-copilot-jh base UI. + * + * Preserves exact visual motifs: terminal label, headline, pixel mascot, + * terminal search preview panel, retro resource cards. + * + * Resource cards and search preview are wired to upstream manifest/search data. + */ +import Badge from '../ui/Badge.astro'; +import Container from '../ui/Container.astro'; +import RetroResourceCard from '../ui/RetroResourceCard.astro'; +import type { SearchResultEntry } from '../../lib/adapters/search'; +import type { ResourceCard, Accent } from '../../lib/types'; + +interface Props { + resources: ResourceCard[]; + searchEntries: SearchResultEntry[]; +} + +const { resources, searchEntries } = Astro.props; + +/** Icon name per section β€” maps manifest icon keys to Icon component names. */ +const SECTION_ICON_MAP: Record = { + agent: 'agents', + instruction: 'instructions', + skill: 'skills', + hook: 'hooks', + workflow: 'workflows', + plugin: 'plugins', + tool: 'tools', + user: 'robot', + docs: 'docs', +}; + +/** Accent colour for search result rows. */ +const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; +--- + +
+ +
+
+

$ pwd ~/awesome-copilot/search-index

+

Build a sharper Copilot setup.

+

Discover agents, skills, instructions, hooks, workflows, plugins and tools from one searchable retro-futuristic control panel.

+
+ + Pixel-art GitHub Copilot robot mascot + + +
+ +
+ {resources.map((resource) => ( + + ))} +
+
+
+ + diff --git a/website/src/components/sections/SearchBar.astro b/website/src/components/sections/SearchBar.astro new file mode 100644 index 000000000..f8aed242c --- /dev/null +++ b/website/src/components/sections/SearchBar.astro @@ -0,0 +1,141 @@ +--- +/** + * Search bar input component. + * + * Used by the homepage for global search across all resource types. + * The actual search logic is wired by search-client.ts at runtime. + */ +import Icon from '../ui/Icon.astro'; + +export interface Props { + /** The element ID for the search input. */ + id?: string; + /** Placeholder text. */ + placeholder?: string; + /** Label for screen readers. */ + label?: string; +} + +const { + id = 'global-search', + placeholder = 'Search agents, skills, instructions, tools…', + label = 'Search resources', +} = Astro.props; +--- +
+
+ +
+
+ + diff --git a/website/src/components/sections/SectionCards.astro b/website/src/components/sections/SectionCards.astro new file mode 100644 index 000000000..6d89d80a0 --- /dev/null +++ b/website/src/components/sections/SectionCards.astro @@ -0,0 +1,168 @@ +--- +/** + * Grid of navigation cards for each resource section. + * + * Each card links to a resource page with icon, title (including count), + * description, and accent colour treatment. + */ +import type { ResourceCard } from '../../lib/types'; +import Icon from '../ui/Icon.astro'; + +export interface Props { + cards: ResourceCard[]; +} + +const { cards } = Astro.props; + +/** Map ResourceCard icon names to the Icon component's SVGs. */ +const ICON_MAP: Record = { + agent: 'agents', + instruction: 'instructions', + skill: 'skills', + hook: 'hooks', + workflow: 'workflows', + plugin: 'plugins', + tool: 'tools', + user: 'robot', + docs: 'docs', +}; +--- +
+
+

Browse Resources

+
+ {cards.map((card, i) => ( + + +
+

{card.title}

+

{card.description}

+
+ +
+ ))} +
+
+
+ + diff --git a/website/src/components/sections/Stats.astro b/website/src/components/sections/Stats.astro new file mode 100644 index 000000000..41fd94e90 --- /dev/null +++ b/website/src/components/sections/Stats.astro @@ -0,0 +1,83 @@ +--- +/** + * Stats bar showing resource counts from manifest data. + * + * Each stat is a labelled number rendered alongside an icon. + */ +import Container from '../ui/Container.astro'; +import Icon from '../ui/Icon.astro'; + +export interface StatItem { + label: string; + value: number; + icon: string; +} + +export interface Props { + stats: StatItem[]; +} + +const { stats } = Astro.props; +--- +
+ +
+ {stats.map((stat) => ( +
+ + {stat.value.toLocaleString()} + {stat.label} +
+ ))} +
+
+
+ + From 101665927f58bbb34b422653e36567eca3586bb0 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Fri, 12 Jun 2026 20:44:28 -0500 Subject: [PATCH 03/26] feat(website): add lib adapters, types, and client scripts Library layer: - types.ts: shared TypeScript interfaces (ResourceItem, FilterState, ModalState, ResourceCardProps) - upstream-types.ts: typed interfaces for manifest, search index, agents, instructions, skills, and hooks JSON schemas - adapters/: manifest and search index adapters that normalize upstream JSON data into typed summaries - search/: filter logic, fuzzy search, and URL state sync utilities Client scripts: - filters.ts: filter state management with URL sync - search-client.ts: Pagefind search integration - url-state.ts: URL parameter encoding/decoding for filters - modal-client.ts: modal open/close/keyboard handling - Add openspec/ directory for specification tracking --- openspec/design-website-replacement.md | 123 +++++ openspec/explore-website-replacement.md | 588 +++++++++++++++++++++++ openspec/proposal-website-replacement.md | 127 +++++ openspec/spec-website-replacement.md | 251 ++++++++++ openspec/tasks-website-replacement.md | 68 +++ website/src/lib/adapters/agents.ts | 99 ++++ website/src/lib/adapters/config.ts | 30 ++ website/src/lib/adapters/hooks.ts | 37 ++ website/src/lib/adapters/index.ts | 22 + website/src/lib/adapters/instructions.ts | 49 ++ website/src/lib/adapters/manifest.ts | 84 ++++ website/src/lib/adapters/plugins.ts | 41 ++ website/src/lib/adapters/samples.ts | 83 ++++ website/src/lib/adapters/search.ts | 56 +++ website/src/lib/adapters/skills.ts | 70 +++ website/src/lib/adapters/tools.ts | 57 +++ website/src/lib/adapters/workflows.ts | 32 ++ website/src/lib/search/fuzzy.ts | 148 ++++++ website/src/lib/types.ts | 81 ++++ website/src/lib/upstream-types.ts | 239 +++++++++ website/src/scripts/filters.ts | 189 ++++++++ website/src/scripts/modal-client.ts | 190 ++++++++ website/src/scripts/search-client.ts | 200 ++++++++ website/src/scripts/url-state.ts | 165 +++++++ 24 files changed, 3029 insertions(+) create mode 100644 openspec/design-website-replacement.md create mode 100644 openspec/explore-website-replacement.md create mode 100644 openspec/proposal-website-replacement.md create mode 100644 openspec/spec-website-replacement.md create mode 100644 openspec/tasks-website-replacement.md create mode 100644 website/src/lib/adapters/agents.ts create mode 100644 website/src/lib/adapters/config.ts create mode 100644 website/src/lib/adapters/hooks.ts create mode 100644 website/src/lib/adapters/index.ts create mode 100644 website/src/lib/adapters/instructions.ts create mode 100644 website/src/lib/adapters/manifest.ts create mode 100644 website/src/lib/adapters/plugins.ts create mode 100644 website/src/lib/adapters/samples.ts create mode 100644 website/src/lib/adapters/search.ts create mode 100644 website/src/lib/adapters/skills.ts create mode 100644 website/src/lib/adapters/tools.ts create mode 100644 website/src/lib/adapters/workflows.ts create mode 100644 website/src/lib/search/fuzzy.ts create mode 100644 website/src/lib/types.ts create mode 100644 website/src/lib/upstream-types.ts create mode 100644 website/src/scripts/filters.ts create mode 100644 website/src/scripts/modal-client.ts create mode 100644 website/src/scripts/search-client.ts create mode 100644 website/src/scripts/url-state.ts diff --git a/openspec/design-website-replacement.md b/openspec/design-website-replacement.md new file mode 100644 index 000000000..cfff3b694 --- /dev/null +++ b/openspec/design-website-replacement.md @@ -0,0 +1,123 @@ +# Design: Upstream Website Replacement with Custom Astro UI + +Replace Starlight with a static-first Astro UI ported from `awesome-copilot-jh`, while keeping `eng/generate-website-data.mjs`, `website/public/data/*.json`, markdown docs, CI paths, and `website/dist/` unchanged. + +## Technical Approach + +Use Astro pages/layouts/components only: no framework islands. Generated JSON is imported at build time for first render, embedded for client filters/search, and normalized through adapters before reaching UI components. Learning Hub markdown moves from Starlight routing to a native Astro `docs` collection with custom docs chrome. + +## New `website/` Structure + +```text +website/ +β”œβ”€ astro.config.mjs # Astro static config: sitemap, redirects, no Starlight +β”œβ”€ package.json # remove Starlight/Pagefind unless re-added directly +β”œβ”€ public/data/*.json # unchanged generator output +β”œβ”€ src/content/docs/** # unchanged Learning Hub markdown +β”œβ”€ src/content.config.ts # Astro glob() docs loader + schema +β”œβ”€ src/layouts/BaseLayout.astro # head, header, footer, global CSS +β”œβ”€ src/layouts/DocsLayout.astro # sidebar, breadcrumbs, TOC, article shell +β”œβ”€ src/components/layout/ # Header, Footer, DocsSidebar, Breadcrumbs, Toc +β”œβ”€ src/components/ui/ # Button, Badge, Card, Icon, Container, 8BitButton +β”œβ”€ src/components/resource/ # ResourceShell, FilterBar, ResultGrid, ResourceCard, FileModal +β”œβ”€ src/components/sections/ # Hero, resource landing sections, CTA/Learning sections +β”œβ”€ src/lib/ +β”‚ β”œβ”€ types.ts # user UI view models +β”‚ β”œβ”€ upstream-types.ts # raw generated JSON contracts +β”‚ β”œβ”€ adapters/ # raw JSON/docs β†’ UI models +β”‚ β”œβ”€ config/site.ts # title, description, social image, edit URL +β”‚ β”œβ”€ config/nav.ts # primary nav + docs sidebar map +β”‚ └─ search/fuzzy.ts # client fuzzy search helpers +└─ src/pages/ # rewritten routes + llms.txt.ts +``` + +## Architecture Decisions + +| Decision | Choice | Rationale | +|---|---|---| +| Layout | Replace every `StarlightPage` with `BaseLayout`/`DocsLayout` | Removes Starlight while preserving semantic head, skip link, nav, footer, and slots. | +| Data | Direct JSON imports + embedded client payloads | Existing pages already import `public/data/*.json`; keeps static render and avoids build-time fetch. | +| Adapters | `src/lib/adapters/*` | Keeps upstream generator schemas untouched and isolates UI drift. | +| Docs | Astro `glob()` collection | Current Astro docs require explicit loaders; this replaces `docsLoader()`/`docsSchema()` cleanly. | +| Search | Client fuzzy search over generated resources + docs metadata | Removes Starlight/Pagefind coupling; Pagefind can be reintroduced later as a direct dependency if needed. | +| Styling | Port `awesome-copilot-jh` tokens/global CSS; delete `starlight-overrides.css` | Custom design becomes the source of truth; no Starlight variables remain. | + +## Component Architecture + +```text +BaseLayout +β”œβ”€ Header(navItems) +β”œβ”€ page content +└─ Footer + +ResourcePage +β”œβ”€ ResourceShell(stats, title) +β”œβ”€ FilterBar(client URL state) +β”œβ”€ ResultGrid(ResourceCard[]) +└─ FileModal(client, hash deep links) + +DocsLayout +β”œβ”€ DocsSidebar(nav tree) +β”œβ”€ ArticleContent(rendered collection entry) +└─ Toc(headings 2-3) +``` + +User components map directly: `BaseLayout`, `Header`, `Footer`, `HeroSection`, `*Section`, and `ui/*` move from `awesome-copilot-jh`; demo constants are replaced by adapter outputs. Existing upstream pure components/scripts (`Modal`, `EmbeddedPageData`, page filter scripts, `marked`, `shiki`, `jszip`, `choices.js`) can be reused or wrapped by the new resource components. + +## Data Flow + +```text +eng/generate-website-data.mjs ──→ public/data/*.json ──→ upstream-types + └─→ adapters ──→ UI models +src/content/docs/**/*.md ──→ Astro docs collection ──→ docs adapter ──→ DocsLayout +UI models ──→ Astro HTML render ──→ embedded JSON ──→ client filters/search/modal +``` + +Catalog routes load their matching JSON (`agents.json`, `skills.json`, etc.) via direct imports. Adapters produce `ResultItem`, `ToolCard`, `ResourceFile`, cookbook models, and search records with safe fallbacks for `null`, empty arrays, missing configuration, and external plugins. Client hydration is limited to search/filter controls, URL sync, copy/share/download actions, and the file modal. + +## Starlight Replacement Map + +| Starlight feature | Replacement | +|---|---| +| `StarlightPage` | `BaseLayout` or `DocsLayout` | +| `docsLoader()` / `docsSchema()` | `glob({ base: './src/content/docs', pattern: '**/*.md' })` + Zod schema accepting existing frontmatter (`title`, `description`, `authors`, `lastUpdated`, `prev`, `next`, etc.) | +| Sidebar config | `src/lib/config/nav.ts` + `DocsSidebar.astro` | +| Pagefind integration | Remove for MVP; use fuzzy JSON/docs search. Delete `pagefind-resources.ts` unless standalone Pagefind is later chosen. | +| `virtual:starlight/user-config` | `src/lib/config/site.ts` | +| `Astro.locals.starlightRoute` | Layout props + content entry frontmatter + computed route metadata | + +## Configuration and Dependencies + +`astro.config.mjs` keeps `site`, `base: '/'`, `output: 'static'`, `trailingSlash: 'always'`, `build.assets`, sitemap, and `/samples/` redirect. Remove `starlight()` and Starlight chunk-warning comments. Keep `astro`, `@astrojs/sitemap`, `choices.js`, `front-matter`, `gray-matter`, `jszip`, `marked`, `shiki`. Remove `@astrojs/starlight`; remove `pagefind` unless added as a direct standalone search dependency later. + +## File Changes + +| File | Action | Description | +|---|---|---| +| `website/src/layouts/*` | Create | Custom site/docs shells. | +| `website/src/components/layout,ui,resource,sections/*` | Create/Modify | Port user UI and wrap upstream modal/filter behavior. | +| `website/src/lib/**` | Create | Types, adapters, nav/site/search config. | +| `website/src/pages/**` | Modify/Create | Rewrite home, catalog, docs, cookbook, contributors, `llms.txt`. | +| `website/src/content.config.ts`, `astro.config.mjs`, `package.json` | Modify | Remove Starlight and configure native Astro. | +| `starlight-overrides.css`, Starlight Head/Footer, `pagefind-resources.ts` | Delete/replace | Remove Starlight-only code. | + +## Migration / Rollout + +1. Add adapters/types/config and port UI primitives/styles. +2. Add `BaseLayout`; rewrite homepage and one catalog page as proof. +3. Convert remaining catalog pages and modal/search hydration. +4. Add `DocsLayout`, `glob()` collection, dynamic Learning Hub routes, cookbook parity. +5. Remove Starlight imports/dependency and stale CSS/integration. +6. Verify `npm run website:data`, `npm run website:build`, and `website/dist/` artifact upload paths. + +## Testing Strategy + +| Layer | What | Approach | +|---|---|---| +| Unit | Adapters and null/empty fallback mapping | Add lightweight TS assertions or fixture smoke checks. | +| Build | Routes, docs collection, `llms.txt`, sitemap, assets | `npm run website:data` then `npm run website:build`. | +| Manual | Search, filters, modal, keyboard nav, responsive docs | Browser pass at 320px, tablet, desktop; validate focus and hash deep links. | + +## Open Questions + +- None blocking. Standalone Pagefind is intentionally deferred unless maintainers require full-text docs search parity. diff --git a/openspec/explore-website-replacement.md b/openspec/explore-website-replacement.md new file mode 100644 index 000000000..1fc71673f --- /dev/null +++ b/openspec/explore-website-replacement.md @@ -0,0 +1,588 @@ +# Exploration: Upstream `website/` Replacement with Custom Astro UI + +> Generated: 2026-06-08 +> Scope: Full inventory of `website/` directory for Astro + Starlight β†’ custom Astro replacement + +--- + +## 1. File Inventory Summary + +### Configuration Files (4 files) + +| File | Role | +|------|------| +| `astro.config.mjs` | Main Astro config β€” Starlight integration with sidebar, custom CSS, redirects, sitemap, pagefind integration, OG meta | +| `src/content.config.ts` | Content collection config β€” uses `docsLoader()` and `docsSchema()` from Starlight for MD docs | +| `package.json` | Dependencies: `@astrojs/starlight ^0.37.6`, `@astrojs/sitemap ^3.7.0`, `astro ^5.16.15`, `choices.js`, `front-matter`, `gray-matter`, `jszip`, `marked`, `shiki` | +| `tsconfig.json` | Extends `astro/tsconfigs/base` | +| `src/env.d.ts` | Standard Astro client types reference | + +### Pages (10 files) + +| File | Route | Data Source | Key Features | +|------|-------|-------------|--------------| +| `src/pages/index.astro` | `/` | `manifest.json` (counts), `search-index.json` (global search) | Hero, search bar, card grid with live counts, recent searches | +| `src/pages/agents.astro` | `/agents/` | `agents.json` | Search, multi-select filters (model, tool, handoffs), sort, modal | +| `src/pages/instructions.astro` | `/instructions/` | `instructions.json` | Search, filter by extension, sort | +| `src/pages/skills.astro` | `/skills/` | `skills.json` | Search, filter by category + hasAssets, sort | +| `src/pages/hooks.astro` | `/hooks/` | `hooks.json` | Search, filter by hook event + tags, sort | +| `src/pages/workflows.astro` | `/workflows/` | `workflows.json` | Search, filter by trigger, sort | +| `src/pages/plugins.astro` | `/plugins/` | `plugins.json` | Search, filter by tags, local + external plugins | +| `src/pages/tools.astro` | `/tools/` | `tools.json` | Search, filter by category (single-select), install buttons | +| `src/pages/contributors.astro` | `/contributors/` | Static HTML table | All-contributors table (hardcoded) | +| `src/pages/learning-hub/cookbook/index.astro` | `/learning-hub/cookbook/` | `samples.json` | Search, filter by language + tags, recipe cards | +| `src/pages/llms.txt.ts` | `/llms.txt` | JSON data + `getCollection("docs")` | LLM-readable index endpoint | + +**All 9 resource pages + cookbook wrap content in `` with `template: "splash"`.** + +### Components (8 files) + +| File | Role | Starlight Deps | +|------|------|----------------| +| `src/components/Head.astro` | Custom head/meta override | Imports `@astrojs/starlight/components/Head.astro`, reads `Astro.locals.starlightRoute` | +| `src/components/Footer.astro` | Custom footer with theme toggle | Imports `EditLink`, `LastUpdated`, `Pagination`, `Icon` from Starlight; reads `virtual:starlight/user-config` | +| `src/components/Modal.astro` | File viewer modal (raw/render, copy, download, share, install) | None β€” pure custom | +| `src/components/PageHeader.astro` | Page title + description + contribute link | None β€” pure custom | +| `src/components/EmbeddedPageData.astro` | Embeds JSON data as ` diff --git a/website/src/pages/agents.astro b/website/src/pages/agents.astro index a43ae08d7..cccbeb623 100644 --- a/website/src/pages/agents.astro +++ b/website/src/pages/agents.astro @@ -1,53 +1,557 @@ --- -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; +/** + * Agents catalog page β€” visual shell ported from awesome-copilot-jh base UI. + * + * Retains upstream data adapters, fuzzy search, filter logic, URL sync, and modal. + * Visual structure: console header card, pixel stat cards, terminal filter bar, + * 3-column pixel card grid β€” all using base class names. + */ +import BaseLayout from '../layouts/BaseLayout.astro'; +import Container from '../components/ui/Container.astro'; +import Card from '../components/ui/Card.astro'; +import Badge from '../components/ui/Badge.astro'; +import Icon from '../components/ui/Icon.astro'; +import ResourceModal from '../components/resource/ResourceModal.astro'; import agentsData from '../../public/data/agents.json'; -import ContributeCTA from '../components/ContributeCTA.astro'; -import EmbeddedPageData from '../components/EmbeddedPageData.astro'; -import PageHeader from '../components/PageHeader.astro'; -import BackToTop from '../components/BackToTop.astro'; -import Modal from '../components/Modal.astro'; -import { renderAgentsHtml, sortAgents } from '../scripts/pages/agents-render'; - -const initialItems = sortAgents(agentsData.items, 'title'); +import type { AgentItem } from '../lib/upstream-types'; + +/* ── Read upstream data for static stats ──── */ +const rawItems = (agentsData as { items: AgentItem[] }).items; + +/* ── Compute stats ────────────────────────── */ +const toolEnabledCount = rawItems.filter(a => (a.tools ?? []).length > 0).length; +const handoffCount = rawItems.filter(a => a.hasHandoffs).length; + --- - -
- - -
-
-
-
-
{initialItems.length} agents
-
- Sort -
-
-
- - -
-
-
-
-
+ +
+ + + +
+

/agents Β· fuzzy search + URL-synced filters

+

Agent marketplace console

+

Filter by model, tools and handoffs. Open any resource in a keyboard-safe file modal.

+
+ +
+
{rawItems.length}
agents
+
{toolEnabledCount}
tool-enabled
+
{handoffCount}
handoff chains
+
+
+ + +
+ + +
+ model: all + +
+ +
+ tool: all +
-
- + + + +
+ sort: Recently Updated + +
+ + +
+ + +
+
+

{rawItems.length} matching agents Β· debounce 200ms Β· q/model/tool/handoffs/sort in URL

+
+
+ + +
+ + + + + -
-
- - - - - - - + + +
+
+ + + + + + + + + + diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index e10427da6..30e083e7e 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -1,243 +1,184 @@ --- -import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro"; -import Modal from "../components/Modal.astro"; -import Icon from "../components/Icon.astro"; -import BackToTop from "../components/BackToTop.astro"; +/** + * Homepage β€” ported from awesome-copilot-jh base UI. + * + * Renders HeroSection, CTASection, and FaqSection from base visual shell. + * Resource cards and search preview are wired to upstream manifest/search data. + */ +import BaseLayout from '../layouts/BaseLayout.astro'; +import HeroSection from '../components/sections/HeroSection.astro'; +import CTASection from '../components/sections/CTASection.astro'; +import FaqSection from '../components/sections/FaqSection.astro'; +import { SITE } from '../config/site'; +import { adaptManifest, type ManifestSummary } from '../lib/adapters/manifest'; +import { adaptSearchIndex, type SearchResultEntry } from '../lib/adapters/search'; +import manifestData from '../../public/data/manifest.json'; +import searchIndexData from '../../public/data/search-index.json'; +import type { ManifestItem, SearchIndexData } from '../lib/upstream-types'; -const base = import.meta.env.BASE_URL; +const manifest: ManifestSummary = adaptManifest(manifestData as ManifestItem); +const searchEntries: SearchResultEntry[] = adaptSearchIndex(searchIndexData as SearchIndexData); --- - - -
- -
-
-

- Awesome GitHub Copilot -

-

- Community-contributed agents, instructions, and skills to enhance your - GitHub Copilot experience -

-

- - View the Awesome Copilot repository on GitHub - -

-
-
- - - -
- - - - - - - -
+ + + + + + + + + + + + + diff --git a/website/src/pages/instruction.astro b/website/src/pages/instruction.astro new file mode 100644 index 000000000..216c9f50d --- /dev/null +++ b/website/src/pages/instruction.astro @@ -0,0 +1,290 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import Button from '../components/ui/Button.astro'; +import Container from '../components/ui/Container.astro'; +import Icon from '../components/ui/Icon.astro'; +--- + + +
+ + + +
+
+
+

cat ./instructions/loading.instructions.md

+
+ loading +

Loading instruction

+

Fetching instruction details on demand.

+
+
+ + +
+
+ +
+

Overview

+

What this instruction standardizes

+

Loading instruction overview.

+
+

Apply to

loading

+

Extensions

loading

+

Patterns

loading

+
+
+ +
+

Usage

+

Install and run from your workspace

+
+
npx
+
npx awesome-copilot add instructions/loading.instructions.md
+
+

Use this command from your project root, then apply the instruction from your Copilot setup.

+
+ +
+

instructions/loading.instructions.md

readonly
+

Instruction file metadata

+
+
name
loading
+
applyTo
loading
+
extensions
loading
+
updated
loading
+
+
+
+ + +
+
+
+
+ + + + diff --git a/website/src/pages/instructions.astro b/website/src/pages/instructions.astro index 15dabebf9..e9237237a 100644 --- a/website/src/pages/instructions.astro +++ b/website/src/pages/instructions.astro @@ -1,58 +1,533 @@ --- -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; +/** + * Instructions catalog page β€” visual shell matching agents.astro pattern. + * + * Mirrors agents.astro: cards navigate to a query-param detail UI. + * Visual structure: console header card, pixel stat cards, terminal filter bar, + * 3-column pixel card grid β€” all using base class names from global.css. + */ +import BaseLayout from '../layouts/BaseLayout.astro'; +import Container from '../components/ui/Container.astro'; +import Card from '../components/ui/Card.astro'; +import Badge from '../components/ui/Badge.astro'; +import Icon from '../components/ui/Icon.astro'; import instructionsData from '../../public/data/instructions.json'; -import ContributeCTA from '../components/ContributeCTA.astro'; -import EmbeddedPageData from '../components/EmbeddedPageData.astro'; -import PageHeader from '../components/PageHeader.astro'; -import BackToTop from '../components/BackToTop.astro'; -import Modal from '../components/Modal.astro'; -import { renderInstructionsHtml, sortInstructions } from '../scripts/pages/instructions-render'; - -const initialItems = sortInstructions(instructionsData.items, 'title'); + +const rawItems = instructionsData.items; + +const withExtensions = rawItems.filter(i => (i.extensions ?? []).length > 0).length; +const withApplyTo = rawItems.filter(i => (i.applyTo ?? '').length > 0 && i.applyTo !== '**').length; +const extensionCount = instructionsData.filters?.extensions?.length ?? 0; +const serializedInstructionsData = JSON.stringify(instructionsData).replace(/ -
- - -
-
-
-
-
{initialItems.length} instructions
-
- Sort & Filter -
-
-
- - -
-
- - -
- -
-
-
-
+ +
+ + + +
+

/instructions Β· fuzzy search + URL-synced filters

+

Instruction standards console

+

Filter by file extension. Open any resource in a keyboard-safe file modal.

+
+ +
+
{rawItems.length}
instructions
+
{withExtensions}
with extensions
+
{withApplyTo}
scoped patterns
+
+
+ + +
+ + +
+ extension: all + +
+ +
+ sort: Name (A-Z) +
-
- + + +
+ + +
+
+

{rawItems.length} matching instructions Β· debounce 200ms Β· q/extension/sort in URL

+
+
+ + +
+ + + + + -
-
- - - - - - - + + +
+
+ + + + + + + + + From 310674a5feab07c0d0b2f6e87ffc8cc024b1c45f Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Sat, 13 Jun 2026 01:44:52 -0500 Subject: [PATCH 06/26] refactor(website): scope starlight overrides to learning-hub pages - Add PageFrame.astro: custom Starlight page frame with sidebar handling and Navigation integration - Add Empty.astro: disable Starlight chrome (ThemeSelect) replaced by site navigation - Register PageFrame and ThemeSelect overrides in astro.config.mjs - Scope starlight-overrides.css to :root:has(learning-hub canonical) instead of global :root, using design system CSS custom properties for all Starlight color tokens - Set content width (58rem) and sidebar width (18.75rem) for learning-hub readability --- website/astro.config.mjs | 2 + website/src/components/starlight/Empty.astro | 4 + .../src/components/starlight/PageFrame.astro | 75 ++++ website/src/styles/starlight-overrides.css | 364 ++++++++++++++---- 4 files changed, 378 insertions(+), 67 deletions(-) create mode 100644 website/src/components/starlight/Empty.astro create mode 100644 website/src/components/starlight/PageFrame.astro diff --git a/website/astro.config.mjs b/website/astro.config.mjs index c343d8a6a..1356a8e24 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -146,7 +146,9 @@ export default defineConfig({ components: { Head: "./src/components/Head.astro", Footer: "./src/components/Footer.astro", + PageFrame: "./src/components/starlight/PageFrame.astro", Search: "./src/components/Search.astro", + ThemeSelect: "./src/components/starlight/Empty.astro", }, }), sitemap(), diff --git a/website/src/components/starlight/Empty.astro b/website/src/components/starlight/Empty.astro new file mode 100644 index 000000000..d68bcd11d --- /dev/null +++ b/website/src/components/starlight/Empty.astro @@ -0,0 +1,4 @@ +--- +// Intentionally empty: used to disable Starlight chrome that is replaced by +// the site's shared navigation and theme handling. +--- diff --git a/website/src/components/starlight/PageFrame.astro b/website/src/components/starlight/PageFrame.astro new file mode 100644 index 000000000..9a0a1695c --- /dev/null +++ b/website/src/components/starlight/PageFrame.astro @@ -0,0 +1,75 @@ +--- +import MobileMenuToggle from 'virtual:starlight/components/MobileMenuToggle'; +import Navigation from '../layout/Navigation.astro'; + +const { hasSidebar } = Astro.locals.starlightRoute; +--- + +
+ + { + hasSidebar && ( + + ) + } +
+
+ + diff --git a/website/src/styles/starlight-overrides.css b/website/src/styles/starlight-overrides.css index 6497d0ce6..eaa1fb1ad 100644 --- a/website/src/styles/starlight-overrides.css +++ b/website/src/styles/starlight-overrides.css @@ -1,24 +1,32 @@ /* Brand color overrides for Starlight */ -/* Purple (#8534F3) and Orange (#FE4C25) theme */ +/* Retro terminal theme ported from awesome-copilot-jh */ /* ── Dark theme (default) ─────────────────────────────────── */ -:root { - --sl-color-accent-low: #1a0d33; - --sl-color-accent: #8534f3; - --sl-color-accent-high: #c9a5fa; - --sl-color-text-accent: #c9a5fa; - - --sl-color-white: #f0f0f5; - --sl-color-gray-1: #d0d0da; - --sl-color-gray-2: #9898a6; - --sl-color-gray-3: #60607a; - --sl-color-gray-4: #30304a; - --sl-color-gray-5: #1a1a2e; - --sl-color-gray-6: #0d0d12; - --sl-color-black: #0d0d12; - - --sl-font-system: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, - Arial, sans-serif; +:root:has(link[rel="canonical"][href*="/learning-hub/"]) { + --sl-color-accent-low: rgb(169 112 255 / 14%); + --sl-color-accent: var(--color-primary); + --sl-color-accent-high: var(--color-green); + --sl-color-text-accent: var(--color-green); + + --sl-color-white: var(--color-text); + --sl-color-gray-1: #d8dddf; + --sl-color-gray-2: var(--color-muted); + --sl-color-gray-3: var(--color-subtle); + --sl-color-gray-4: #30383a; + --sl-color-gray-5: var(--color-surface-2); + --sl-color-gray-6: var(--color-bg-elevated); + --sl-color-black: var(--color-bg); + --sl-color-bg: var(--color-bg); + --sl-color-bg-nav: var(--color-bg-elevated); + --sl-color-bg-sidebar: var(--color-bg-elevated); + --sl-color-hairline: var(--color-border-soft); + --sl-color-hairline-light: var(--color-border-soft); + --sl-color-hairline-shade: var(--color-border); + + --sl-font-system: var(--font-mono); + --sl-font-system-mono: var(--font-mono); + --sl-content-width: 80rem; + --sl-sidebar-width: 18.75rem; } /* ── Light theme ───────────────────────────────────────────── */ @@ -41,11 +49,6 @@ --sl-color-black: #fafafa; } -:root[data-theme="light"] header.header { - background: rgba(250, 250, 250, 0.6) !important; - border-bottom: 1px solid rgba(0, 0, 0, 0.08) !important; -} - /* ── Sidebar readability ───────────────────────────────────── */ nav[aria-label="Main"] a { font-weight: 500 !important; @@ -57,50 +60,6 @@ nav[aria-label="Main"] a[aria-current="page"] { border-inline-start-color: var(--sl-color-accent) !important; } -/* ── Header brand styling ──────────────────────────────────── */ -.site-title { - font-weight: 700 !important; -} -header .site-title:hover { - color: var(--sl-color-accent-high) !important; -} - -/* ── Unified background ─────────────────────────────────────── */ -/* Clean header - solid background */ -header.header { - background: var(--sl-color-gray-6) !important; - border-bottom: 1px solid var(--sl-color-gray-5) !important; - z-index: 1000; - position: relative; -} - -/* Header inner content for alignment */ -header.header .header-inner { - display: flex; - align-items: center; - justify-content: space-between; - max-width: 100%; -} - -header.header .header-actions { - display: flex; - align-items: center; - gap: 12px; -} - -/* Hide social icons in header - GitHub link moved to hero */ -header .social-icons { - display: none !important; -} - -/* Move theme toggle inside header - align with header elements */ -.theme-toggle-container { - position: fixed !important; - top: 12px !important; - right: 80px !important; - z-index: 1001; -} - /* On custom splash pages, body already has the gradient bg. Make Starlight's wrapper layers transparent so it shows through. */ body:has(#main-content) { @@ -237,3 +196,274 @@ main:has(#main-content) > .content-panel:has(h1#_top) { #starlight__search .pagefind-ui__filter-checkbox:checked + .pagefind-ui__filter-label:hover { opacity: 0.9; } + +/* ── Learning Hub docs shell ────────────────────────────────── */ +/* Starlight owns the content model; these overrides reshape the docs UI + to match the awesome-copilot-jh Learning Hub visual language. */ +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) body { + background: + radial-gradient(circle at 82% 10%, rgb(169 112 255 / 5%), transparent 26rem), + radial-gradient(circle at 18% 28%, rgb(45 140 255 / 3%), transparent 24rem), + var(--color-bg); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) body::before { + content: ""; + position: fixed; + inset: 0; + z-index: -1; + pointer-events: none; + background-image: linear-gradient(rgb(255 255 255 / 2%) 1px, transparent 1px); + background-size: 100% 32px; + mask-image: linear-gradient(to bottom, #000 0 18%, transparent 78%); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .main-frame { + background: var(--color-bg); + min-width: 0; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) main { + padding: 0 !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .lg\:sl-flex { + align-items: flex-start; + gap: 1.75rem; + min-height: calc(100vh - var(--site-header-height, 4.25rem)); + padding: 1.875rem clamp(1.25rem, 3vw, 2.25rem); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sidebar-pane { + border-right: 1px solid var(--color-border) !important; + background: var(--color-bg-elevated) !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sidebar-content { + gap: 0.62rem !important; + padding: 1.35rem 1.15rem !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) nav[aria-label="Main"] > ul > li > details > summary, +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) nav[aria-label="Main"] > ul > li > a { + color: var(--color-green) !important; + font-size: 0.78rem !important; + font-weight: 900 !important; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) nav[aria-label="Main"] a { + min-height: 2.35rem; + border: 1px solid transparent; + border-radius: var(--radius-1); + color: var(--color-muted) !important; + padding: 0.35rem 0.65rem !important; + font-size: 0.9rem !important; + font-weight: 700 !important; + text-decoration: none !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) nav[aria-label="Main"] a:hover { + border-color: var(--color-border-soft); + background: rgb(255 255 255 / 4%); + color: var(--color-text) !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) nav[aria-label="Main"] a[aria-current="page"] { + border-color: var(--color-primary) !important; + background: rgb(169 112 255 / 11%) !important; + color: var(--color-primary) !important; + box-shadow: none; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel { + flex: 1 1 auto; + min-width: 0; + border: 0 !important; + padding: 0 !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel + .content-panel { + padding-top: 0 !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel > .sl-container { + width: 100%; + max-width: var(--sl-content-width); + margin-inline: 0 !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:first-of-type > .sl-container, +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:nth-of-type(2) > .sl-container { + position: relative; + border: 1px solid var(--color-border); + background: + linear-gradient(180deg, rgb(255 255 255 / 1.4%), transparent), + var(--color-bg-elevated); + box-shadow: var(--shadow-card); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:first-of-type > .sl-container { + border-bottom: 0; + border-radius: var(--radius-2) var(--radius-2) 0 0; + padding: clamp(1.5rem, 3vw, 2rem) clamp(1.5rem, 3vw, 2rem) 0; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:nth-of-type(2) > .sl-container { + border-top: 0; + border-radius: 0 0 var(--radius-2) var(--radius-2); + padding: 1rem clamp(1.5rem, 3vw, 2rem) clamp(1.5rem, 3vw, 2rem); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:first-of-type > .sl-container::before { + content: "$ cat ~/docs/learning-hub"; + display: block; + margin-bottom: 0.75rem; + color: var(--color-green); + font-size: 0.88rem; + font-weight: 900; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) h1 { + color: var(--color-text) !important; + font-family: var(--font-heading); + font-size: clamp(2.1rem, 4.4vw, 3.25rem) !important; + font-weight: 800 !important; + letter-spacing: -0.05em; + line-height: 1.05; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content { + color: var(--color-muted); + font-size: 1rem; + line-height: 1.65; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content :is(h2, h3, h4):not(:where(.not-content *)) { + color: var(--color-text); + font-family: var(--font-heading); + letter-spacing: -0.035em; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content h2:not(:where(.not-content *)) { + border-top: 1px solid var(--color-border-soft); + padding-top: 1.4rem; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content a:not(:where(.not-content *)) { + color: var(--color-green); + text-decoration-color: rgb(144 255 177 / 45%); + text-underline-offset: 0.2em; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content a:hover:not(:where(.not-content *)) { + color: var(--color-primary); + text-decoration-color: var(--color-primary); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content :is(code, pre):not(:where(.not-content *)) { + border-color: var(--color-border-soft); + background: var(--color-bg); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content blockquote:not(:where(.not-content *)), +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content .starlight-aside:not(:where(.not-content *)) { + border: 1px solid var(--color-primary); + border-radius: var(--radius-1); + background: rgb(169 112 255 / 10%); + color: var(--color-muted); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar { + flex: 0 0 14.375rem; + width: 14.375rem; + border-left: 0 !important; + background: transparent; + padding-top: 0 !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel { + position: sticky; + top: calc(var(--site-header-height, 4.25rem) + 1.875rem); + padding: 0 !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel > .sl-container { + width: 100%; + border: 1px solid var(--color-border); + border-radius: var(--radius-2); + background: var(--color-bg-elevated); + padding: 1.35rem; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel h2 { + margin: 0 0 0.75rem !important; + color: var(--color-green) !important; + font-size: 0.78rem !important; + font-weight: 900 !important; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel ul { + display: grid; + gap: 0.75rem; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel li { + margin: 0 !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel a { + display: block; + border: 1px solid transparent; + padding: 0.1rem 0.2rem; + color: var(--color-muted) !important; + font-size: 0.88rem !important; + font-weight: 700; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel a:hover { + color: var(--color-text) !important; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel a[aria-current="true"] { + border-color: var(--color-primary); + background: var(--color-primary); + color: var(--color-bg) !important; + box-shadow: 4px 0 0 var(--color-primary); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .pagination-links a { + border: 2px solid var(--color-border-soft); + border-radius: 0; + background: var(--color-surface-2); + box-shadow: 3px 3px 0 var(--color-black); + color: var(--color-text); +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .pagination-links a:hover { + border-color: var(--color-green); + background: rgb(144 255 177 / 14%); +} + +@media (max-width: 72rem) { + html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .lg\:sl-flex { + padding-inline: 1.25rem; + } + + html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar { + display: none; + } +} + +@media (max-width: 50rem) { + html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sidebar-pane { + background: var(--color-bg) !important; + } + + html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:first-of-type { + padding-top: 1.25rem !important; + } +} From 9f81762cfa44de506680d637ecd9a886da9a3c27 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Sat, 13 Jun 2026 01:45:07 -0500 Subject: [PATCH 07/26] refactor(website): use BitButton component in Navigation header - Replace inline GitHub button markup with BitButton component (variant="secondary", size="sm") - Remove duplicated .bit-btn CSS rules from Navigation.astro (now owned by 8BitButton.astro) - Simplify header layout by removing .bit-btn-inner wrapper --- .../src/components/layout/Navigation.astro | 59 +++---------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/website/src/components/layout/Navigation.astro b/website/src/components/layout/Navigation.astro index be538ebe5..3ff80a4c5 100644 --- a/website/src/components/layout/Navigation.astro +++ b/website/src/components/layout/Navigation.astro @@ -1,6 +1,7 @@ --- import { primaryNav } from '../../config/nav'; import { SITE } from '../../config/site'; +import BitButton from '../ui/8BitButton.astro'; import Icon from '../ui/Icon.astro'; const currentPath = Astro.url.pathname.replace(/\/$/, '') || '/'; @@ -41,17 +42,15 @@ const currentPath = Astro.url.pathname.replace(/\/$/, '') || '/'; } - - - - GITHUB -> - - + + GITHUB -> + - + + + diff --git a/website/src/pages/plugins.astro b/website/src/pages/plugins.astro index 89dfe9445..3ab745998 100644 --- a/website/src/pages/plugins.astro +++ b/website/src/pages/plugins.astro @@ -1,92 +1,156 @@ --- -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; -import pluginsData from '../../public/data/plugins.json'; +import BaseLayout from '../layouts/BaseLayout.astro'; +import Container from '../components/ui/Container.astro'; +import Card from '../components/ui/Card.astro'; +import Icon from '../components/ui/Icon.astro'; import ContributeCTA from '../components/ContributeCTA.astro'; -import EmbeddedPageData from '../components/EmbeddedPageData.astro'; -import PageHeader from '../components/PageHeader.astro'; import BackToTop from '../components/BackToTop.astro'; -import Modal from '../components/Modal.astro'; -import { renderPluginsHtml, sortPlugins } from '../scripts/pages/plugins-render'; +import pluginsData from '../../public/data/plugins.json'; -const initialItems = sortPlugins(pluginsData.items, 'title'); +const rawItems = pluginsData.items; +const externalCount = rawItems.filter((item) => item.external).length; +const itemCount = rawItems.reduce((total, item) => total + item.itemCount, 0); +const tags = [...new Set(rawItems.flatMap((item) => item.tags))].sort(); --- - -
- - -
-
-
-

Awesome Copilot is a default plugin marketplace β€” no setup required. Access it directly from your tools:

-
    -
  • GitHub Copilot CLI: Type /plugin marketplace browse awesome-copilot in a Copilot session
  • -
  • VS Code: Type @agentPlugins in the Extensions search view, or open the Command Palette and run Chat: Plugins
  • -
-

Install any plugin with: copilot plugin install <plugin-name>@awesome-copilot

+ +
+ + +
+

/plugins Β· fuzzy search + on-demand details

+

Plugin marketplace console

+

Browse curated plugin collections and external submissions through lightweight cards that open internal detail views.

-
-
-
{initialItems.length} plugins
-
- Sort & Filter -
-
-
- - -
-
- - -
- -
-
-
-
-
-
- +
+
{rawItems.length}
plugins
+
{itemCount}
resources
+
{externalCount}
external
+
+
+ + +

Awesome Copilot is a default plugin marketplace - no setup required. Access it directly from GitHub Copilot CLI or VS Code.

+

/plugin marketplace browse awesome-copilot Β· copilot plugin install <plugin-name>@awesome-copilot

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

Loading plugins...

+
+ +
+ + + + -
-
+ +
+
+ + + + - - - - - - - + + + + + diff --git a/website/src/pages/skills.astro b/website/src/pages/skills.astro index 644ea0e84..2448fb810 100644 --- a/website/src/pages/skills.astro +++ b/website/src/pages/skills.astro @@ -1,53 +1,128 @@ --- -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; +import BaseLayout from '../layouts/BaseLayout.astro'; +import Container from '../components/ui/Container.astro'; +import Card from '../components/ui/Card.astro'; +import Icon from '../components/ui/Icon.astro'; import ContributeCTA from '../components/ContributeCTA.astro'; -import PageHeader from '../components/PageHeader.astro'; -import EmbeddedPageData from '../components/EmbeddedPageData.astro'; import BackToTop from '../components/BackToTop.astro'; -import Modal from '../components/Modal.astro'; import skillsData from '../../public/data/skills.json'; -import { renderSkillsHtml, sortSkills } from '../scripts/pages/skills-render'; -const initialItems = sortSkills(skillsData.items, 'title'); +const rawItems = skillsData.items; +const assetEnabledCount = rawItems.filter((item) => item.hasAssets).length; +const bundledFileCount = rawItems.reduce((total, item) => total + item.files.length, 0); +const categories = [...new Set(rawItems.map((item) => item.category).filter(Boolean))].sort(); --- - -
- - -
-
-
-
-
{initialItems.length} skills
-
- Sort -
-
-
- - -
-
-
-
-
+ +
+ + +
+

/skills Β· fuzzy search + on-demand details

+

Skills marketplace console

+

Browse self-contained agent skills without mounting the full catalog up front.

-
- + +
+
{rawItems.length}
skills
+
{assetEnabledCount}
with assets
+
{bundledFileCount}
bundled files
+
+
+ +
+ + + + + + + +
+ +
+

Loading skills...

+
+ +
+ + + + -
-
+ +
+
+ + + + - - - - - + + + diff --git a/website/src/pages/tools.astro b/website/src/pages/tools.astro index c859bb6c7..db35a429d 100644 --- a/website/src/pages/tools.astro +++ b/website/src/pages/tools.astro @@ -1,277 +1,148 @@ --- -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; -import toolsData from '../../public/data/tools.json'; -import Modal from '../components/Modal.astro'; -import ContributeCTA from "../components/ContributeCTA.astro"; -import EmbeddedPageData from "../components/EmbeddedPageData.astro"; -import PageHeader from "../components/PageHeader.astro"; +import BaseLayout from '../layouts/BaseLayout.astro'; +import Container from '../components/ui/Container.astro'; +import Card from '../components/ui/Card.astro'; +import Icon from '../components/ui/Icon.astro'; +import ContributeCTA from '../components/ContributeCTA.astro'; import BackToTop from '../components/BackToTop.astro'; -import { renderToolsHtml, sortTools } from "../scripts/pages/tools-render"; +import toolsData from '../../public/data/tools.json'; -const initialItems = sortTools( - toolsData.items.map((item) => ({ - ...item, - title: item.name, - })), - "title" -); +const rawItems = toolsData.items; +const featuredCount = rawItems.filter((item) => item.featured).length; +const categories = toolsData.filters.categories; --- - -
- - -
-
-
-
-
{initialItems.length} tools
-
- Sort & Filter -
-
-
- - -
-
- - -
- -
-
-
-
+ +
+ + +
+

/tools Β· fuzzy search + on-demand details

+

Tools marketplace console

+

Filter Copilot-ready tools by category, then inspect capabilities and configuration in a dedicated internal detail UI.

-
- -
-

More Tools Coming Soon

-

- We're working on additional tools to enhance your GitHub Copilot - experience. Check back soon! -

-
- +
+
{rawItems.length}
tools
+
{featuredCount}
featured
+
{categories.length}
categories
+
+
+ +
+ + + + + + +
-
-
- - - - - - + + + - - + + + + + + diff --git a/website/src/pages/workflows.astro b/website/src/pages/workflows.astro index b6547d6fb..7e4ccece1 100644 --- a/website/src/pages/workflows.astro +++ b/website/src/pages/workflows.astro @@ -1,58 +1,123 @@ --- -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; -import workflowsData from '../../public/data/workflows.json'; +import BaseLayout from '../layouts/BaseLayout.astro'; +import Container from '../components/ui/Container.astro'; +import Card from '../components/ui/Card.astro'; +import Icon from '../components/ui/Icon.astro'; import ContributeCTA from '../components/ContributeCTA.astro'; -import EmbeddedPageData from '../components/EmbeddedPageData.astro'; -import PageHeader from '../components/PageHeader.astro'; import BackToTop from '../components/BackToTop.astro'; -import Modal from '../components/Modal.astro'; -import { renderWorkflowsHtml, sortWorkflows } from '../scripts/pages/workflows-render'; +import workflowsData from '../../public/data/workflows.json'; -const initialItems = sortWorkflows(workflowsData.items, 'title'); +const rawItems = workflowsData.items; +const triggers = [...new Set(rawItems.flatMap((item) => item.triggers))].sort(); +const scheduledCount = rawItems.filter((item) => item.triggers.includes('schedule')).length; --- - -
- - -
-
-
-
-
{initialItems.length} workflows
-
- Sort & Filter -
-
-
- - -
-
- - -
- -
-
-
-
+ +
+ + +
+

/workflows Β· fuzzy search + on-demand details

+

Agentic workflows console

+

Filter workflow recipes by trigger, then open a focused internal detail UI without mounting every workflow card up front.

-
- + +
+
{rawItems.length}
workflows
+
{triggers.length}
triggers
+
{scheduledCount}
scheduled
+
+
+ +
+ + + + + + + +
+ +
+

Loading workflows...

+
+ +
+ + + + -
-
+ +
+
+ + + + - - - - - + + + From 1763ac1e2213df48e0c3c0fdc76d712223cd517c Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Sat, 13 Jun 2026 01:46:22 -0500 Subject: [PATCH 11/26] feat(website): add resource detail pages and shared client scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detail pages (hook, plugin, skill, tool, workflow): - Each page wraps BaseLayout + ResourceDetailPage with kind-specific props and initResourceDetail() client script ResourceDetailPage.astro: - Shared detail layout with breadcrumbs, hero panel, terminal-style header, feature/spec/metadata sections, install command with copy - Client-side data fetching from JSON manifests Client scripts: - resource-catalog.ts: unified catalog client with search, filter (multi/single), URL state sync, accent color assignment, and keyboard navigation - resource-detail.ts: detail page initializer β€” fetches resource data by kind, populates DOM elements, handles copy-to-clipboard for install commands --- .../src/components/ResourceDetailPage.astro | 146 +++++++ website/src/pages/hook.astro | 15 + website/src/pages/plugin.astro | 15 + website/src/pages/skill.astro | 15 + website/src/pages/tool.astro | 15 + website/src/pages/workflow.astro | 15 + website/src/scripts/resource-catalog.ts | 275 +++++++++++++ website/src/scripts/resource-detail.ts | 374 ++++++++++++++++++ 8 files changed, 870 insertions(+) create mode 100644 website/src/components/ResourceDetailPage.astro create mode 100644 website/src/pages/hook.astro create mode 100644 website/src/pages/plugin.astro create mode 100644 website/src/pages/skill.astro create mode 100644 website/src/pages/tool.astro create mode 100644 website/src/pages/workflow.astro create mode 100644 website/src/scripts/resource-catalog.ts create mode 100644 website/src/scripts/resource-detail.ts diff --git a/website/src/components/ResourceDetailPage.astro b/website/src/components/ResourceDetailPage.astro new file mode 100644 index 000000000..8093dacb1 --- /dev/null +++ b/website/src/components/ResourceDetailPage.astro @@ -0,0 +1,146 @@ +--- +import Button from './ui/Button.astro'; +import Container from './ui/Container.astro'; +import Icon from './ui/Icon.astro'; + +interface Props { + kind: string; + plural: string; + title: string; + description: string; +} + +const { kind, plural, title, description } = Astro.props; +--- + +
+ + + +
+
+
+

cat ./loading

+
+ loading +

Loading {kind}

+

{description}

+
+
+ + +
+
+ +
+

Overview

+

What this {kind} provides

+

Loading details.

+
+
+ +
+

Usage

+

Install or configure from your workspace

+
+
command
+
Loading command...
+
+

Use this from your project root or follow the source documentation when the resource needs custom setup.

+
+ +
+

loading

readonly
+

Resource metadata

+
+
+
+ + +
+
+
+ + diff --git a/website/src/pages/hook.astro b/website/src/pages/hook.astro new file mode 100644 index 000000000..cb5b6b670 --- /dev/null +++ b/website/src/pages/hook.astro @@ -0,0 +1,15 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../components/ResourceDetailPage.astro'; +--- + + + + + + diff --git a/website/src/pages/plugin.astro b/website/src/pages/plugin.astro new file mode 100644 index 000000000..514d827dc --- /dev/null +++ b/website/src/pages/plugin.astro @@ -0,0 +1,15 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../components/ResourceDetailPage.astro'; +--- + + + + + + diff --git a/website/src/pages/skill.astro b/website/src/pages/skill.astro new file mode 100644 index 000000000..a230d24fa --- /dev/null +++ b/website/src/pages/skill.astro @@ -0,0 +1,15 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../components/ResourceDetailPage.astro'; +--- + + + + + + diff --git a/website/src/pages/tool.astro b/website/src/pages/tool.astro new file mode 100644 index 000000000..8586d287e --- /dev/null +++ b/website/src/pages/tool.astro @@ -0,0 +1,15 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../components/ResourceDetailPage.astro'; +--- + + + + + + diff --git a/website/src/pages/workflow.astro b/website/src/pages/workflow.astro new file mode 100644 index 000000000..3f693c81c --- /dev/null +++ b/website/src/pages/workflow.astro @@ -0,0 +1,15 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../components/ResourceDetailPage.astro'; +--- + + + + + + diff --git a/website/src/scripts/resource-catalog.ts b/website/src/scripts/resource-catalog.ts new file mode 100644 index 000000000..0da2e72bb --- /dev/null +++ b/website/src/scripts/resource-catalog.ts @@ -0,0 +1,275 @@ +import type { Searchable } from '../lib/search/fuzzy'; +import type { Accent } from '../lib/types'; +import { createSearch, initSearch } from './search-client'; +import { getQueryParam, getQueryParamValues, updateQueryParams } from './url-state'; + +type FilterType = 'multi' | 'single'; + +export interface CatalogFilterConfig { + param: string; + label: string; + type: FilterType; + defaultValue?: string; +} + +export interface CatalogClientItem extends Searchable { + slug: string; + title: string; + label: string; + description: string; + tags: string[]; + accent: Accent; + href: string; + detail: string; + sortDate?: string | null; + sortTitle?: string; + filters: Record; +} + +export interface CatalogConfig { + dataUrl: string; + gridSelector: string; + countSelector: string; + emptySelector: string; + searchInputSelector: string; + searchResultsSelector: string; + searchStatusSelector: string; + loadMoreSelector: string; + loadSentinelSelector: string; + clearSelector: string; + emptyClearSelector: string; + resourceName: string; + resourceNamePlural: string; + filterConfigs: CatalogFilterConfig[]; + defaultSort: string; + toClientItem: (item: TRaw) => CatalogClientItem; +} + +const INITIAL_RENDER_COUNT = 36; +const RENDER_BATCH_SIZE = 24; + +export function stableAccent(source: string): Accent { + const accents: Accent[] = ['purple', 'blue', 'green', 'yellow']; + let hash = 0; + for (const char of source) hash = (hash * 31 + char.charCodeAt(0)) >>> 0; + return accents[hash % accents.length]; +} + +function escapeHtml(value: unknown): string { + return String(value ?? '').replace(/[&<>'"]/g, (char) => { + switch (char) { + case '&': return '&'; + case '<': return '<'; + case '>': return '>'; + case "'": return '''; + case '"': return '"'; + default: return char; + } + }); +} + +async function loadItems(dataUrl: string, toClientItem: (item: TRaw) => CatalogClientItem): Promise { + const response = await fetch(`${import.meta.env.BASE_URL}${dataUrl}`); + if (!response.ok) throw new Error(`Failed to load ${dataUrl}: ${response.status}`); + const data = await response.json() as { items: TRaw[] }; + return data.items.map(toClientItem); +} + +function readFilterState(configs: CatalogFilterConfig[]): Record { + const state: Record = {}; + for (const config of configs) { + state[config.param] = config.type === 'multi' + ? getQueryParamValues(config.param) + : [getQueryParam(config.param) || config.defaultValue || ''].filter(Boolean); + } + return state; +} + +function applyFilterState(items: CatalogClientItem[], state: Record, configs: CatalogFilterConfig[]): CatalogClientItem[] { + return items.filter((item) => configs.every((config) => { + if (config.param === 'sort') return true; + const selected = state[config.param] ?? []; + const active = config.type === 'single' + ? selected.filter(value => value && value !== config.defaultValue) + : selected; + if (active.length === 0) return true; + const values = item.filters[config.param] ?? []; + return active.some(value => values.includes(value)); + })); +} + +function sortItems(items: CatalogClientItem[], sortBy: string): CatalogClientItem[] { + return [...items].sort((a, b) => { + if (sortBy === 'lastUpdated') return (b.sortDate ?? '').localeCompare(a.sortDate ?? ''); + if (sortBy === 'featured') { + const featured = Number((b.filters.featured ?? []).includes('true')) - Number((a.filters.featured ?? []).includes('true')); + if (featured !== 0) return featured; + } + return (a.sortTitle ?? a.title).localeCompare(b.sortTitle ?? b.title); + }); +} + +function renderCard(item: CatalogClientItem): string { + const accent = escapeHtml(item.accent); + const tags = item.tags + .slice(0, 3) + .map(tag => `${escapeHtml(tag)}`) + .join(''); + + return ` + + + ${escapeHtml(item.label)} + + + + ${escapeHtml(item.title)} + ${escapeHtml(item.description)} + + + ${tags} + Open details + + `; +} + +function updateFilterDisplays(configs: CatalogFilterConfig[], state: Record): void { + for (const config of configs) { + const display = document.querySelector(`.filter-display[data-param="${config.param}"]`); + const values = state[config.param] ?? []; + if (!display) continue; + const active = config.type === 'single' + ? values.filter(value => value && value !== config.defaultValue) + : values; + display.textContent = active.length > 0 ? `${config.label.toLowerCase()}: ${active.join(', ')}` : `${config.label.toLowerCase()}: all`; + } +} + +function wireFilterSelects(configs: CatalogFilterConfig[], onChange: () => void): void { + document.querySelectorAll('select[data-filter-param]').forEach((select) => { + const config = configs.find(item => item.param === select.dataset.filterParam); + if (!config) return; + + const values = config.type === 'multi' ? getQueryParamValues(config.param) : [getQueryParam(config.param) || config.defaultValue || '']; + for (const option of select.options) option.selected = values.includes(option.value); + + select.addEventListener('change', () => { + if (config.type === 'multi') { + updateQueryParams({ setAll: { [config.param]: Array.from(select.selectedOptions).map(option => option.value).filter(Boolean) } }); + } else { + const value = select.value && select.value !== config.defaultValue ? select.value : null; + updateQueryParams({ set: { [config.param]: value } }); + } + onChange(); + }); + }); +} + +export async function initResourceCatalog(config: CatalogConfig): Promise { + const grid = document.querySelector(config.gridSelector); + const countEl = document.querySelector(config.countSelector); + const emptyState = document.querySelector(config.emptySelector); + const loadMore = document.querySelector(config.loadMoreSelector); + const loadSentinel = document.querySelector(config.loadSentinelSelector); + const clearButton = document.querySelector(config.clearSelector); + const emptyClear = document.querySelector(config.emptyClearSelector); + const searchInput = document.querySelector(config.searchInputSelector); + + if (countEl) countEl.textContent = `Loading ${config.resourceNamePlural}...`; + + let items: CatalogClientItem[]; + try { + items = await loadItems(config.dataUrl, config.toClientItem); + } catch (error) { + console.error(error); + if (countEl) countEl.textContent = `Failed to load ${config.resourceNamePlural}`; + if (emptyState) emptyState.hidden = false; + return; + } + + const engine = createSearch(items); + let currentItems: CatalogClientItem[] = []; + let visibleCount = INITIAL_RENDER_COUNT; + + function renderProgress(): void { + const visibleItems = Math.min(visibleCount, currentItems.length); + if (grid) grid.innerHTML = currentItems.slice(0, visibleItems).map(renderCard).join(''); + if (countEl) countEl.textContent = `Showing ${visibleItems} of ${currentItems.length} matching ${config.resourceName}${currentItems.length === 1 ? '' : 's'} (${items.length} total)`; + if (loadMore) { + loadMore.hidden = visibleItems >= currentItems.length; + loadMore.textContent = `Load more ${config.resourceNamePlural} (${visibleItems}/${currentItems.length})`; + } + if (loadSentinel) loadSentinel.hidden = visibleItems >= currentItems.length; + } + + function applyAndRender(): void { + const query = searchInput?.value.trim() || getQueryParam('q') || ''; + const filteredBySearch = query ? engine.search(query).filter((item): item is CatalogClientItem => item != null) : [...items]; + const state = readFilterState(config.filterConfigs); + const sortBy = getQueryParam('sort') || config.defaultSort; + state.sort = [sortBy]; + currentItems = sortItems(applyFilterState(filteredBySearch, state, config.filterConfigs), sortBy); + visibleCount = INITIAL_RENDER_COUNT; + renderProgress(); + updateFilterDisplays(config.filterConfigs, state); + if (emptyState) emptyState.hidden = currentItems.length > 0; + if (grid) grid.style.display = currentItems.length > 0 ? '' : 'none'; + } + + function clearFilters(): void { + const remove: Record = { q: null }; + for (const filterConfig of config.filterConfigs) remove[filterConfig.param] = null; + updateQueryParams({ set: remove }); + if (searchInput) searchInput.value = ''; + document.querySelectorAll('select[data-filter-param]').forEach((select) => { + for (const option of select.options) option.selected = option.value === '' || option.value === select.dataset.defaultValue; + }); + applyAndRender(); + } + + wireFilterSelects(config.filterConfigs, applyAndRender); + clearButton?.addEventListener('click', clearFilters); + emptyClear?.addEventListener('click', clearFilters); + loadMore?.addEventListener('click', () => { + visibleCount = Math.min(visibleCount + RENDER_BATCH_SIZE, currentItems.length); + renderProgress(); + }); + + if (loadSentinel && 'IntersectionObserver' in window) { + const observer = new IntersectionObserver((entries) => { + if (!entries.some(entry => entry.isIntersecting)) return; + visibleCount = Math.min(visibleCount + RENDER_BATCH_SIZE, currentItems.length); + renderProgress(); + }, { rootMargin: '320px' }); + observer.observe(loadSentinel); + } + + let searchTimer = 0; + searchInput?.addEventListener('input', () => { + window.clearTimeout(searchTimer); + searchTimer = window.setTimeout(applyAndRender, 220); + }); + + initSearch(engine, { + inputSelector: config.searchInputSelector, + resultsSelector: config.searchResultsSelector, + statusSelector: config.searchStatusSelector, + minLength: 1, + maxResults: 40, + }, { + render(item, idx, query, highlight) { + const desc = item.description.length > 100 ? `${item.description.slice(0, 100)}...` : item.description; + return `
${highlight(escapeHtml(item.title), query)}${highlight(escapeHtml(desc), query)}
`; + }, + onSelect(item) { + window.location.href = item.href; + }, + statusText(query, resultCount, totalCount) { + if (!query) return ''; + if (resultCount === 0) return `No ${config.resourceNamePlural} match "${query}"`; + return `${resultCount} of ${totalCount} ${config.resourceNamePlural} match "${query}"`; + }, + }); + + applyAndRender(); +} diff --git a/website/src/scripts/resource-detail.ts b/website/src/scripts/resource-detail.ts new file mode 100644 index 000000000..79d000a12 --- /dev/null +++ b/website/src/scripts/resource-detail.ts @@ -0,0 +1,374 @@ +import type { HookItem, PluginItem, SkillItem, ToolItem, WorkflowItem } from '../lib/upstream-types'; +import { REPO_IDENTIFIER } from './utils'; +import { stableAccent } from './resource-catalog'; + +type ResourceKind = 'skill' | 'hook' | 'workflow' | 'plugin' | 'tool'; + +interface DetailData { + slug: string; + title: string; + description: string; + label: string; + accent: string; + terminalPath: string; + listingHref: string; + listingLabel: string; + installTitle: string; + installCommand: string; + sourcePath: string; + downloadHref: string; + shareHref: string; + tags: string[]; + features: Array<{ title: string; value: string }>; + specs: Array<{ name: string; value: string }>; + metadata: Array<{ name: string; value: string }>; +} + +const DATA_URLS: Record = { + skill: 'data/skills.json', + hook: 'data/hooks.json', + workflow: 'data/workflows.json', + plugin: 'data/plugins.json', + tool: 'data/tools.json', +}; + +const LISTING_HREFS: Record = { + skill: '/skills/', + hook: '/hooks/', + workflow: '/workflows/', + plugin: '/plugins/', + tool: '/tools/', +}; + +function joinOrNone(values: unknown[] | undefined): string { + return values?.length ? values.join(', ') : '(none)'; +} + +function setText(id: string, value: string): void { + const element = document.getElementById(id); + if (element) element.textContent = value; +} + +function setHref(label: string, href: string): void { + const link = document.querySelector(`a[aria-label="${label}"]`); + if (link) link.href = href; +} + +function renderTags(tags: string[]): void { + const tagList = document.getElementById('detail-tags'); + if (!tagList) return; + tagList.textContent = ''; + for (const tag of tags) { + const badge = document.createElement('span'); + badge.className = 'detail-badge detail-badge--neutral'; + badge.textContent = tag; + tagList.append(badge); + } +} + +function renderFeatureCards(features: DetailData['features']): void { + const grid = document.getElementById('detail-features'); + if (!grid) return; + grid.textContent = ''; + for (const feature of features) { + const article = document.createElement('article'); + const title = document.createElement('h3'); + const value = document.createElement('p'); + title.textContent = feature.title; + value.textContent = feature.value; + article.append(title, value); + grid.append(article); + } +} + +function renderDefinitionList(id: string, entries: Array<{ name: string; value: string }>): void { + const list = document.getElementById(id); + if (!list) return; + list.textContent = ''; + for (const entry of entries) { + const row = document.createElement('div'); + const term = document.createElement('dt'); + const desc = document.createElement('dd'); + term.textContent = entry.name; + desc.textContent = entry.value; + row.append(term, desc); + list.append(row); + } +} + +function adaptSkill(item: SkillItem): DetailData { + const command = `gh skills install ${REPO_IDENTIFIER} ${item.id}`; + return { + slug: item.id, + title: item.title, + description: item.description, + label: item.hasAssets ? `${item.assetCount} assets` : 'skill', + accent: stableAccent(item.id), + terminalPath: item.skillFile, + listingHref: LISTING_HREFS.skill, + listingLabel: 'skills', + installTitle: 'Install this skill', + installCommand: command, + sourcePath: `awesome-copilot/${item.path}`, + downloadHref: `/skills/#file=${encodeURIComponent(item.skillFile)}`, + shareHref: `/skill/?slug=${encodeURIComponent(item.id)}`, + tags: [item.category, item.hasAssets ? 'assets' : 'no-assets', `${item.files.length} files`].filter(Boolean), + features: [ + { title: 'Category', value: item.category || '(none)' }, + { title: 'Assets', value: item.hasAssets ? `${item.assetCount} bundled asset${item.assetCount === 1 ? '' : 's'}` : '(none)' }, + { title: 'Files', value: `${item.files.length} file${item.files.length === 1 ? '' : 's'}` }, + ], + specs: [ + { name: 'name', value: item.name }, + { name: 'skillFile', value: item.skillFile }, + { name: 'assets', value: String(item.assetCount) }, + { name: 'updated', value: item.lastUpdated || '(unknown)' }, + ], + metadata: [ + { name: 'type', value: 'skill' }, + { name: 'path', value: item.path }, + { name: 'status', value: item.hasAssets ? 'bundled' : 'single file' }, + ], + }; +} + +function adaptHook(item: HookItem): DetailData { + return { + slug: item.id, + title: item.title, + description: item.description, + label: item.hooks.length === 1 ? item.hooks[0] : `${item.hooks.length} events`, + accent: stableAccent(item.id), + terminalPath: item.readmeFile ?? item.path, + listingHref: LISTING_HREFS.hook, + listingLabel: 'hooks', + installTitle: 'Use this hook bundle', + installCommand: `cp -R ${item.path} .github/copilot/hooks/`, + sourcePath: `awesome-copilot/${item.path}`, + downloadHref: `/hooks/#file=${encodeURIComponent(item.readmeFile ?? item.path)}`, + shareHref: `/hook/?slug=${encodeURIComponent(item.id)}`, + tags: [...item.tags.slice(0, 6), ...item.hooks.slice(0, 2)], + features: [ + { title: 'Events', value: joinOrNone(item.hooks) }, + { title: 'Tags', value: joinOrNone(item.tags) }, + { title: 'Assets', value: item.assets.length ? `${item.assets.length} bundled asset${item.assets.length === 1 ? '' : 's'}` : '(none)' }, + ], + specs: [ + { name: 'name', value: item.title }, + { name: 'events', value: joinOrNone(item.hooks) }, + { name: 'assets', value: String(item.assets.length) }, + { name: 'updated', value: item.lastUpdated || '(unknown)' }, + ], + metadata: [ + { name: 'type', value: 'hook' }, + { name: 'path', value: item.path }, + { name: 'readme', value: item.readmeFile ?? '(none)' }, + ], + }; +} + +function adaptWorkflow(item: WorkflowItem): DetailData { + return { + slug: item.id, + title: item.title, + description: item.description, + label: item.triggers.length === 1 ? item.triggers[0] : `${item.triggers.length} triggers`, + accent: stableAccent(item.id), + terminalPath: item.path, + listingHref: LISTING_HREFS.workflow, + listingLabel: 'workflows', + installTitle: 'Use this workflow', + installCommand: `cp ${item.path} .github/workflows/${item.id}.md`, + sourcePath: `awesome-copilot/${item.path}`, + downloadHref: `/workflows/#file=${encodeURIComponent(item.path)}`, + shareHref: `/workflow/?slug=${encodeURIComponent(item.id)}`, + tags: item.triggers, + features: [ + { title: 'Triggers', value: joinOrNone(item.triggers) }, + { title: 'Source', value: item.path }, + { title: 'Updated', value: item.lastUpdated || '(unknown)' }, + ], + specs: [ + { name: 'name', value: item.title }, + { name: 'triggers', value: joinOrNone(item.triggers) }, + { name: 'path', value: item.path }, + { name: 'updated', value: item.lastUpdated || '(unknown)' }, + ], + metadata: [ + { name: 'type', value: 'workflow' }, + { name: 'path', value: item.path }, + { name: 'status', value: 'agentic workflow' }, + ], + }; +} + +function pluginAuthor(item: PluginItem): string { + const author = item.author as unknown; + if (typeof author === 'string') return author; + if (author && typeof author === 'object' && 'name' in author) return String((author as { name?: string }).name ?? ''); + return '(unknown)'; +} + +function adaptPlugin(item: PluginItem): DetailData { + const repository = item.repository || item.homepage || `https://github.com/${REPO_IDENTIFIER}/tree/main/${item.path}`; + return { + slug: item.id, + title: item.name, + description: item.description, + label: item.external ? 'external' : 'plugin', + accent: stableAccent(item.id), + terminalPath: item.path, + listingHref: LISTING_HREFS.plugin, + listingLabel: 'plugins', + installTitle: 'Install this plugin', + installCommand: item.external ? `copilot plugin install ${item.name}` : `copilot plugin install ${item.name}@awesome-copilot`, + sourcePath: repository, + downloadHref: repository, + shareHref: `/plugin/?slug=${encodeURIComponent(item.id)}`, + tags: item.tags.slice(0, 8), + features: [ + { title: 'Resources', value: `${item.itemCount} bundled resource${item.itemCount === 1 ? '' : 's'}` }, + { title: 'Source', value: item.external ? 'external' : 'awesome-copilot' }, + { title: 'Author', value: pluginAuthor(item) }, + ], + specs: [ + { name: 'name', value: item.name }, + { name: 'resources', value: String(item.itemCount) }, + { name: 'license', value: item.license ?? '(unknown)' }, + { name: 'repository', value: repository }, + ], + metadata: [ + { name: 'type', value: 'plugin' }, + { name: 'path', value: item.path }, + { name: 'status', value: item.external ? 'external' : 'curated' }, + ], + }; +} + +function adaptTool(item: ToolItem): DetailData { + const primaryLink = item.links.github || item.links.marketplace || item.links.vscode || item.links.pypi || item.links.blog || LISTING_HREFS.tool; + return { + slug: item.id, + title: item.name, + description: item.description, + label: item.featured ? 'featured' : item.category, + accent: stableAccent(item.id), + terminalPath: item.configuration?.type ?? item.category, + listingHref: LISTING_HREFS.tool, + listingLabel: 'tools', + installTitle: 'Configure this tool', + installCommand: item.configuration?.content?.trim() || 'See the project documentation for setup details.', + sourcePath: primaryLink, + downloadHref: primaryLink, + shareHref: `/tool/?slug=${encodeURIComponent(item.id)}`, + tags: [item.category, ...item.tags].slice(0, 8), + features: [ + { title: 'Category', value: item.category }, + { title: 'Requirements', value: joinOrNone(item.requirements) }, + { title: 'Features', value: joinOrNone(item.features) }, + ], + specs: [ + { name: 'name', value: item.name }, + { name: 'category', value: item.category }, + { name: 'featured', value: item.featured ? 'yes' : 'no' }, + { name: 'configuration', value: item.configuration?.type ?? '(none)' }, + ], + metadata: [ + { name: 'type', value: 'tool' }, + { name: 'source', value: primaryLink }, + { name: 'status', value: item.featured ? 'featured' : 'available' }, + ], + }; +} + +function adapt(kind: ResourceKind, item: unknown): DetailData { + if (kind === 'skill') return adaptSkill(item as SkillItem); + if (kind === 'hook') return adaptHook(item as HookItem); + if (kind === 'workflow') return adaptWorkflow(item as WorkflowItem); + if (kind === 'plugin') return adaptPlugin(item as PluginItem); + return adaptTool(item as ToolItem); +} + +function renderDetail(detail: DetailData, kind: ResourceKind): void { + document.title = `${detail.title} Β· ${kind[0].toUpperCase()}${kind.slice(1)} Β· Awesome Copilot`; + document.querySelector('meta[name="description"]')?.setAttribute('content', detail.description); + + setText('breadcrumb-current', detail.title); + setText('detail-terminal-line', `cat ./${detail.terminalPath}`); + setText('detail-title', detail.title); + setText('detail-description', detail.description); + setText('detail-overview', detail.description); + setText('detail-path', detail.terminalPath); + setText('source-owner', detail.sourcePath); + setText('install-title', detail.installTitle); + + const label = document.getElementById('detail-label'); + if (label) { + label.textContent = detail.label; + label.className = `detail-badge detail-badge--${detail.accent}`; + } + + for (const code of document.querySelectorAll('[data-install-command]')) code.textContent = detail.installCommand; + setHref('Back to listing', detail.listingHref); + setHref('View source listing', detail.listingHref); + setHref('Download resource', detail.downloadHref); + setHref('Share resource', detail.shareHref); + renderFeatureCards(detail.features); + renderDefinitionList('detail-specs', detail.specs); + renderDefinitionList('detail-metadata', detail.metadata); + renderTags(detail.tags); +} + +function renderError(kind: ResourceKind, message: string): void { + setText('breadcrumb-current', `${kind} not found`); + setText('detail-title', `${kind} not found`); + setText('detail-description', message); + setText('detail-overview', message); +} + +export async function initResourceDetail(kind: ResourceKind): Promise { + const slug = new URLSearchParams(window.location.search).get('slug'); + if (!slug) { + renderError(kind, `Missing ${kind} slug. Go back to the listing and choose a resource.`); + return; + } + + try { + const response = await fetch(`${import.meta.env.BASE_URL}${DATA_URLS[kind]}`); + if (!response.ok) throw new Error(`Failed to load ${kind} data (${response.status})`); + const data = await response.json() as { items: Array<{ id: string }> }; + const raw = data.items.find(item => item.id === slug); + if (!raw) { + renderError(kind, `No ${kind} found for slug "${slug}".`); + return; + } + renderDetail(adapt(kind, raw), kind); + } catch (error) { + renderError(kind, error instanceof Error ? error.message : `Failed to load ${kind} details.`); + } +} + +export function initCopyButtons(): void { + document.querySelectorAll('[data-copy-code]').forEach((button) => { + button.addEventListener('click', async () => { + const code = button.closest('.terminal-card')?.querySelector('code')?.textContent?.trim(); + if (!code) return; + const label = button.querySelector('span'); + const previousLabel = label?.textContent ?? 'Copy'; + try { + await navigator.clipboard.writeText(code); + button.dataset.copied = 'true'; + if (label) label.textContent = 'Copied'; + window.setTimeout(() => { + button.dataset.copied = 'false'; + if (label) label.textContent = previousLabel; + }, 1600); + } catch { + if (label) label.textContent = 'Failed'; + window.setTimeout(() => { + if (label) label.textContent = previousLabel; + }, 1600); + } + }); + }); +} From 63eaa4a2d40a27de25e65b3dc0233a47d683b32f Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Sat, 13 Jun 2026 01:46:56 -0500 Subject: [PATCH 12/26] fix(website): refine learning-hub layout and code block styling - Adjust main content padding (1.875rem 2.25rem) with max-width 60rem - Remove redundant padding from .lg\:sl-flex container - Reduce content-panel top padding to eliminate double-spacing - Separate code block vs inline code styles: - pre: green left-border accent, elevated bg, 1rem padding - inline code: subtle border, green text, compact padding - Widen right sidebar (flex: auto) with adjusted padding - Fix responsive main padding at 72rem breakpoint --- website/src/styles/starlight-overrides.css | 51 ++++++++++++++++------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/website/src/styles/starlight-overrides.css b/website/src/styles/starlight-overrides.css index eaa1fb1ad..400e2264f 100644 --- a/website/src/styles/starlight-overrides.css +++ b/website/src/styles/starlight-overrides.css @@ -224,14 +224,16 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .main- } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) main { - padding: 0 !important; + padding: 1.875rem 2.25rem 0 !important; + max-width: 60rem !important; + flex: 0 0 auto !important; } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .lg\:sl-flex { align-items: flex-start; gap: 1.75rem; min-height: calc(100vh - var(--site-header-height, 4.25rem)); - padding: 1.875rem clamp(1.25rem, 3vw, 2.25rem); + padding: 0; } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sidebar-pane { @@ -307,13 +309,13 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .conte html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:first-of-type > .sl-container { border-bottom: 0; border-radius: var(--radius-2) var(--radius-2) 0 0; - padding: clamp(1.5rem, 3vw, 2rem) clamp(1.5rem, 3vw, 2rem) 0; + padding: 0 clamp(1.5rem, 3vw, 2rem) 0; } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:nth-of-type(2) > .sl-container { border-top: 0; border-radius: 0 0 var(--radius-2) var(--radius-2); - padding: 1rem clamp(1.5rem, 3vw, 2rem) clamp(1.5rem, 3vw, 2rem); + padding: 0 clamp(1.5rem, 3vw, 2rem) clamp(1.5rem, 3vw, 2rem); } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .content-panel:first-of-type > .sl-container::before { @@ -362,9 +364,30 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-ma text-decoration-color: var(--color-primary); } -html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content :is(code, pre):not(:where(.not-content *)) { - border-color: var(--color-border-soft); - background: var(--color-bg); +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content pre:not(:where(.not-content *)) { + border: 1px solid var(--color-border); + border-left: 3px solid var(--color-green); + border-radius: var(--radius-1); + background: var(--color-bg-elevated); + padding: 1rem; + overflow-x: auto; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content pre code:not(:where(.not-content *)) { + border: none; + background: transparent; + padding: 0; + font-size: 0.85rem; + line-height: 1.6; +} + +html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content :not(pre) > code:not(:where(.not-content *)) { + border: 1px solid var(--color-border-soft); + border-radius: var(--radius-1); + background: var(--color-bg-elevated); + padding: 0.15rem 0.4rem; + font-size: 0.85rem; + color: var(--color-green); } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content blockquote:not(:where(.not-content *)), @@ -376,11 +399,11 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-ma } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar { - flex: 0 0 14.375rem; - width: 14.375rem; + flex: 1 1 auto; + width: auto; border-left: 0 !important; background: transparent; - padding-top: 0 !important; + padding: 1.875rem 0 0 !important; } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel { @@ -390,11 +413,11 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel > .sl-container { - width: 100%; + max-width: 100%; border: 1px solid var(--color-border); border-radius: var(--radius-2); background: var(--color-bg-elevated); - padding: 1.35rem; + padding: clamp(1.5rem, 3vw, 2rem) 0 1.35rem 1.35rem; } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar-panel h2 { @@ -449,8 +472,8 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .pagin } @media (max-width: 72rem) { - html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .lg\:sl-flex { - padding-inline: 1.25rem; + html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) main { + padding-inline: 1.25rem !important; } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .right-sidebar { From 283c540040b53163b4a26aba52afcd3720334796 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Tue, 23 Jun 2026 00:25:18 -0500 Subject: [PATCH 13/26] feat(website-ui): add new modals and resource detail entrypoints --- website/DESIGN.md | 799 ++++++++++++++++ website/src/components/InstallModal.astro | 164 ++++ website/src/components/ShareModal.astro | 275 ++++++ .../sections/ContributorsSection.astro | 198 ++++ website/src/components/ui/CopyButton.astro | 59 ++ website/src/components/ui/InlineAlert.astro | 81 ++ website/src/lib/frontmatter.ts | 10 + .../src/markdown/rehype-image-dimensions.mjs | 165 ++++ website/src/pages/agent/[id].astro | 458 +++++++++ website/src/pages/hook/[id].astro | 46 + website/src/pages/instruction/[id].astro | 464 +++++++++ website/src/pages/plugin/[id].astro | 44 + website/src/pages/skill/[id].astro | 894 ++++++++++++++++++ website/src/pages/tool/[id].astro | 23 + website/src/pages/workflow/[id].astro | 44 + website/src/scripts/custom-dropdown.ts | 165 ++++ website/src/scripts/share-modal.ts | 138 +++ 17 files changed, 4027 insertions(+) create mode 100644 website/DESIGN.md create mode 100644 website/src/components/InstallModal.astro create mode 100644 website/src/components/ShareModal.astro create mode 100644 website/src/components/sections/ContributorsSection.astro create mode 100644 website/src/components/ui/CopyButton.astro create mode 100644 website/src/components/ui/InlineAlert.astro create mode 100644 website/src/lib/frontmatter.ts create mode 100644 website/src/markdown/rehype-image-dimensions.mjs create mode 100644 website/src/pages/agent/[id].astro create mode 100644 website/src/pages/hook/[id].astro create mode 100644 website/src/pages/instruction/[id].astro create mode 100644 website/src/pages/plugin/[id].astro create mode 100644 website/src/pages/skill/[id].astro create mode 100644 website/src/pages/tool/[id].astro create mode 100644 website/src/pages/workflow/[id].astro create mode 100644 website/src/scripts/custom-dropdown.ts create mode 100644 website/src/scripts/share-modal.ts diff --git a/website/DESIGN.md b/website/DESIGN.md new file mode 100644 index 000000000..ae2062c45 --- /dev/null +++ b/website/DESIGN.md @@ -0,0 +1,799 @@ +--- +version: alpha +name: Copilot SDK Retro-Futuristic UI Design System +description: "A retro-futuristic, terminal-inspired, 8-bit/cyberpunk design system extracted from the supplied visual reference. Built for dark-mode developer tools, dashboards, SDK docs, agent interfaces, and data-heavy product UIs." +colors: + primary: "#A970FF" + secondary: "#2D8CFF" + success: "#90FFB1" + warning: "#FFC057" + error: "#FF507C" + surface: "#121416" + background: "#0A0D0D" + text: "#F5F7F7" + text-muted: "#A7ADB2" + text-subtle: "#6E767D" + border: "#2A2F31" + border-strong: "#4A5155" + surface-hover: "#171B1D" + surface-active: "#1D2326" + black: "#000000" + white: "#FFFFFF" +typography: + display-xl: + fontFamily: "Space Grotesk" + fontSize: "48px" + fontWeight: 800 + lineHeight: "56px" + letterSpacing: "-0.02em" + heading-lg: + fontFamily: "Space Grotesk" + fontSize: "32px" + fontWeight: 700 + lineHeight: "40px" + letterSpacing: "-0.01em" + heading-md: + fontFamily: "Space Grotesk" + fontSize: "20px" + fontWeight: 600 + lineHeight: "28px" + letterSpacing: "0em" + body: + fontFamily: "JetBrains Mono" + fontSize: "16px" + fontWeight: 400 + lineHeight: "24px" + letterSpacing: "0em" + body-sm: + fontFamily: "JetBrains Mono" + fontSize: "14px" + fontWeight: 400 + lineHeight: "20px" + letterSpacing: "0em" + label: + fontFamily: "JetBrains Mono" + fontSize: "12px" + fontWeight: 600 + lineHeight: "16px" + letterSpacing: "0.04em" + code: + fontFamily: "JetBrains Mono" + fontSize: "14px" + fontWeight: 400 + lineHeight: "20px" + letterSpacing: "0em" +spacing: + base: "4px" + "0": "0px" + "1": "4px" + "2": "12px" + "4": "16px" + "6": "24px" + "8": "32px" + "12": "48px" + "16": "64px" + "24": "96px" + container-max: "1280px" + container-margin: "48px" + mobile-padding: "16px" + tablet-padding: "24px" + desktop-padding: "32px" + button-padding-x: "16px" + button-padding-y: "12px" + input-padding-x: "12px" + input-padding-y: "16px" + card-padding-x: "16px" + card-padding-y: "20px" + list-item-padding-x: "16px" + list-item-padding-y: "16px" +rounded: + none: "0px" + sm: "4px" + md: "8px" + lg: "12px" + xl: "16px" + full: "9999px" +components: + border-default: + width: "1px" + color: "rgba(255, 255, 255, 0.06)" + style: "solid" + border-strong: + width: "1px" + color: "rgba(255, 255, 255, 0.12)" + style: "solid" + shadow-sm: + boxShadow: "0 2px 0 rgba(0, 0, 0, 0.40)" + shadow-md: + boxShadow: "0 4px 12px rgba(0, 0, 0, 0.60)" + shadow-lg: + boxShadow: "0 8px 24px rgba(0, 0, 0, 0.70)" + focus-ring: + borderColor: "{colors.primary}" + boxShadow: "0 0 0 1px {colors.primary}" + button-primary: + backgroundColor: "{colors.primary}" + textColor: "{colors.white}" + typography: "{typography.label}" + rounded: "{rounded.sm}" + padding: "12px 16px" + height: "32px" + button-primary-hover: + backgroundColor: "#B886FF" + textColor: "{colors.white}" + boxShadow: "0 0 0 1px rgba(169, 112, 255, 0.40)" + button-primary-active: + backgroundColor: "#7F45E8" + textColor: "{colors.white}" + button-primary-disabled: + backgroundColor: "#2B203F" + textColor: "#6D5B85" + button-secondary: + backgroundColor: "transparent" + textColor: "{colors.secondary}" + border: "1px solid {colors.secondary}" + typography: "{typography.label}" + rounded: "{rounded.sm}" + padding: "12px 16px" + button-secondary-hover: + backgroundColor: "rgba(45, 140, 255, 0.10)" + textColor: "#6DB2FF" + button-secondary-active: + backgroundColor: "rgba(45, 140, 255, 0.18)" + textColor: "{colors.secondary}" + button-ghost: + backgroundColor: "transparent" + textColor: "{colors.success}" + border: "1px solid rgba(144, 255, 177, 0.28)" + typography: "{typography.label}" + rounded: "{rounded.sm}" + button-danger: + backgroundColor: "rgba(255, 80, 124, 0.65)" + textColor: "{colors.white}" + border: "1px solid rgba(255, 80, 124, 0.80)" + typography: "{typography.label}" + rounded: "{rounded.sm}" + input-default: + backgroundColor: "{colors.background}" + textColor: "{colors.text}" + border: "1px solid rgba(255, 255, 255, 0.18)" + rounded: "{rounded.sm}" + padding: "12px 16px" + height: "36px" + typography: "{typography.body-sm}" + input-focused: + backgroundColor: "{colors.background}" + textColor: "{colors.text}" + border: "1px solid {colors.primary}" + boxShadow: "0 0 0 1px rgba(169, 112, 255, 0.24)" + input-disabled: + backgroundColor: "#0C0E0F" + textColor: "{colors.text-subtle}" + border: "1px solid rgba(255, 255, 255, 0.10)" + select-default: + backgroundColor: "{colors.background}" + textColor: "{colors.text}" + border: "1px solid rgba(255, 255, 255, 0.18)" + iconColor: "{colors.success}" + rounded: "{rounded.sm}" + height: "36px" + select-open: + backgroundColor: "{colors.background}" + textColor: "{colors.text}" + border: "1px solid {colors.success}" + rounded: "{rounded.sm}" + toggle-on: + backgroundColor: "{colors.success}" + thumbColor: "{colors.white}" + width: "36px" + height: "20px" + rounded: "{rounded.full}" + toggle-off: + backgroundColor: "{colors.surface}" + thumbColor: "{colors.text-muted}" + border: "1px solid rgba(255, 255, 255, 0.20)" + width: "36px" + height: "20px" + rounded: "{rounded.full}" + checkbox-selected: + backgroundColor: "{colors.success}" + checkColor: "{colors.background}" + border: "1px solid {colors.success}" + size: "16px" + rounded: "{rounded.sm}" + checkbox-unselected: + backgroundColor: "transparent" + border: "1px solid rgba(255, 255, 255, 0.22)" + size: "16px" + rounded: "{rounded.sm}" + badge-default: + backgroundColor: "{colors.primary}" + textColor: "{colors.white}" + typography: "{typography.label}" + rounded: "{rounded.sm}" + padding: "4px 8px" + badge-success: + backgroundColor: "{colors.success}" + textColor: "{colors.background}" + typography: "{typography.label}" + badge-warning: + backgroundColor: "{colors.warning}" + textColor: "{colors.background}" + typography: "{typography.label}" + badge-error: + backgroundColor: "{colors.error}" + textColor: "{colors.white}" + typography: "{typography.label}" + badge-outline: + backgroundColor: "transparent" + textColor: "{colors.primary}" + border: "1px solid {colors.primary}" + typography: "{typography.label}" + card-default: + backgroundColor: "{colors.surface}" + textColor: "{colors.text}" + border: "1px solid rgba(255, 255, 255, 0.12)" + rounded: "{rounded.md}" + padding: "16px" + boxShadow: "0 2px 0 rgba(0, 0, 0, 0.40)" + card-hover: + backgroundColor: "{colors.surface-hover}" + border: "1px solid rgba(144, 255, 177, 0.45)" + boxShadow: "0 0 16px rgba(144, 255, 177, 0.12)" + list-item-default: + backgroundColor: "{colors.surface}" + textColor: "{colors.text}" + border: "1px solid rgba(255, 255, 255, 0.08)" + rounded: "{rounded.sm}" + padding: "16px" + list-item-hover: + backgroundColor: "{colors.surface-hover}" + border: "1px solid rgba(255, 255, 255, 0.14)" + list-item-selected: + backgroundColor: "rgba(169, 112, 255, 0.10)" + border: "1px solid {colors.primary}" + tab-active: + backgroundColor: "{colors.primary}" + textColor: "{colors.white}" + border: "1px solid {colors.primary}" + rounded: "{rounded.sm} {rounded.sm} 0 0" + tab-inactive: + backgroundColor: "{colors.background}" + textColor: "{colors.text-muted}" + border: "1px solid rgba(255, 255, 255, 0.14)" + pagination-active: + backgroundColor: "{colors.primary}" + textColor: "{colors.white}" + rounded: "{rounded.sm}" + size: "32px" + pagination-inactive: + backgroundColor: "{colors.background}" + textColor: "{colors.text-muted}" + border: "1px solid rgba(255, 255, 255, 0.12)" + rounded: "{rounded.sm}" + size: "32px" + alert-success: + backgroundColor: "rgba(144, 255, 177, 0.06)" + textColor: "{colors.text}" + border: "1px solid {colors.success}" + iconColor: "{colors.success}" + alert-info: + backgroundColor: "rgba(45, 140, 255, 0.06)" + textColor: "{colors.text}" + border: "1px solid {colors.secondary}" + iconColor: "{colors.secondary}" + alert-warning: + backgroundColor: "rgba(255, 192, 87, 0.06)" + textColor: "{colors.text}" + border: "1px solid {colors.warning}" + iconColor: "{colors.warning}" + alert-error: + backgroundColor: "rgba(255, 80, 124, 0.06)" + textColor: "{colors.text}" + border: "1px solid {colors.error}" + iconColor: "{colors.error}" + skeleton: + backgroundColor: "#2A2F31" + highlightColor: "#3A4246" + rounded: "{rounded.sm}" + empty-state-cta: + backgroundColor: "{colors.primary}" + textColor: "{colors.white}" + rounded: "{rounded.sm}" + height: "40px" +--- + +# Copilot SDK Retro-Futuristic UI Design System + +## Overview + +This design system is a dark-mode, retro-futuristic interface language for developer-facing web products. It combines terminal UI structure, pixel-art details, cyberpunk neon accents, and dense technical documentation patterns. The visual personality should feel engineered, playful, precise, and slightly arcade-like without losing usability. + +The reference product identity is **GitHub Copilot SDK**: a developer platform that should feel fast, intelligent, hackable, and UI-native. The interface should look like a living technical control panel: black background, thin grid borders, monospace microcopy, purple primary actions, green success states, blue links, and pixel-inspired iconography. + +Use this system for landing pages, SDK documentation, dashboards, AI agent consoles, code tooling, internal developer portals, data visualization screens, and component-heavy web apps. + +Design principles: + +- **Terminal-first structure:** layouts are divided by thin 1px borders, panel grids, and compact information density. +- **Neon accents, controlled usage:** purple is the brand/action color, green is success/terminal signal, blue is secondary action/info, yellow is warning, and red is destructive/error. +- **8-bit personality:** pixel icons, blocky forms, low-radius corners, and subtle grid patterns create a retro-cyberpunk identity. +- **Functional density:** components may be compact, but labels, focus states, hit targets, and contrast must remain accessible. + +## Colors + +The system is built for **dark mode only**. The background is near-black, surfaces are slightly lifted dark panels, and all meaning is carried by high-contrast neon semantic colors. + +| Token | Value | Role | +|---|---:|---| +| `primary` | `#A970FF` | Main brand color, primary buttons, active tabs, selected states, progress indicators, hero highlights. | +| `secondary` | `#2D8CFF` | Secondary buttons, links, informational alerts, secondary chart series. | +| `success` | `#90FFB1` | Success states, positive feedback, terminal accents, selected checkboxes, on toggles. | +| `warning` | `#FFC057` | Warning states, caution badges, warning alerts, chart caution series. | +| `error` | `#FF507C` | Error states, destructive buttons, validation errors, destructive alerts. | +| `surface` | `#121416` | Cards, panels, inputs, elevated sections, component interiors. | +| `background` | `#0A0D0D` | Page background, modal backdrops, empty state background, terminal canvas. | +| `text` | `#F5F7F7` | Primary foreground text. | +| `text-muted` | `#A7ADB2` | Supporting text, descriptions, inactive labels. | +| `text-subtle` | `#6E767D` | Disabled text, placeholders, metadata. | +| `border` | `#2A2F31` | Default visible border approximation. Use alpha borders in CSS when supported. | +| `border-strong` | `#4A5155` | Stronger separators, active outlines, dense table borders. | + +Color usage rules: + +- Use `background` for the full page shell and large empty areas. +- Use `surface` for cards, tables, forms, alerts, panels, dropdowns, and list items. +- Use `primary` only for the most important action or currently selected navigation state. +- Use `success` sparingly for positive state, validation success, terminal signal, icons, and micro accents. +- Use `secondary` for links and informational feedback, never as the main call-to-action when a primary action exists. +- Use `warning` and `error` only when the UI is communicating risk, caution, validation failure, or destructive action. +- Prefer translucent backgrounds for semantic alerts: `rgba(token, 0.06)` to `rgba(token, 0.12)` with a 1px token-colored border. + +Chart colors should follow this sequence: + +1. `primary` / `#A970FF` +2. `secondary` / `#2D8CFF` +3. `success` / `#90FFB1` +4. `warning` / `#FFC057` +5. `error` / `#FF507C` +6. Deeper purple variant / `#6B35D9` + +## Typography + +The typography intentionally mixes a geometric display family with a technical monospace body family. + +- **Headings and UI display:** `Space Grotesk`, used for hero text, section titles, brand lockups, component headings, large labels, active navigation, and high-emphasis UI text. +- **Body and code:** `JetBrains Mono`, used for body copy, tables, labels, form text, captions, tokens, code snippets, metadata, and all dense technical content. + +Typography scale: + +| Token | Family | Size / Line height | Weight | Usage | +|---|---|---:|---:|---| +| `display-xl` | Space Grotesk | `48px / 56px` | `800` | Pixel-inspired hero titles, brand wordmarks, extra-large page titles. | +| `heading-lg` | Space Grotesk | `32px / 40px` | `700` | Large titles and major page sections. | +| `heading-md` | Space Grotesk | `20px / 28px` | `600` | Section headers, card titles, module headings. | +| `body` | JetBrains Mono | `16px / 24px` | `400` | Main body copy, readable paragraphs, documentation content. | +| `body-sm` | JetBrains Mono | `14px / 20px` | `400` | Supporting copy, table cells, form values, compact panels. | +| `label` | JetBrains Mono | `12px / 16px` | `600` | Labels, badges, button text, metadata, table headers. | +| `code` | JetBrains Mono | `14px / 20px` | `400` | Inline code, code blocks, terminal snippets. | + +Typography behavior: + +- Use uppercase labels for panel titles, metadata, and token names when the UI needs a technical, system-like rhythm. +- Keep headings tight but readable; avoid overly soft editorial typography. +- Use monospace for most interface copy to preserve the terminal/product-engineering feeling. +- Use `primary` or `success` for highlighted words, but never color entire paragraphs with neon colors. + +## Layout + +The layout follows a **panel-grid terminal system**. Large screens should feel like a design console: clearly divided sections, compact cards, data tables, chart panels, and component groups separated by thin borders. + +Spacing system: + +| Token | Value | Usage | +|---|---:|---| +| `spacing.0` | `0px` | Reset, flush edges. | +| `spacing.1` | `4px` | Micro gaps, icon nudges, dense inline alignment. | +| `spacing.2` | `12px` | Compact internal spacing, small button gaps. | +| `spacing.4` | `16px` | Default component padding, card interior, list item padding. | +| `spacing.6` | `24px` | Panel grouping, tablet container padding. | +| `spacing.8` | `32px` | Desktop container padding, section spacing. | +| `spacing.12` | `48px` | Outer margin, major section rhythm. | +| `spacing.16` | `64px` | Large visual separation. | +| `spacing.24` | `96px` | Hero spacing and large layout breaks. | + +Container strategy: + +- Max content width: `1280px`. +- Desktop side margin: `48px`. +- Desktop inner container padding: `32px`. +- Tablet inner container padding: `24px`. +- Mobile inner container padding: `16px`. +- Maintain a strict 4px base rhythm. Prefer values from the spacing scale instead of arbitrary spacing. + +Component padding recommendations: + +| Component | Padding | +|---|---:| +| Buttons | `12px 16px` | +| Inputs | `12px 16px` | +| Cards | `16px 20px` | +| List items | `16px 16px` | +| Alerts | `12px 16px` | +| Badges | `4px 8px` | + +Layout behavior: + +- Use bordered panels instead of floating glassmorphism. +- Prefer grid layouts over large empty whitespace. +- Keep data-heavy screens compact but organized. +- Use visible section numbers or terminal-like labels when useful. +- Avoid rounded, soft SaaS layouts that dilute the retro-console aesthetic. + +## Elevation & Depth + +Depth is subtle and should feel like dark terminal layers, not material design elevation. Use low-spread shadows, thin borders, and slight surface shifts to separate content. + +Shadow tokens: + +| Token | Value | Usage | +|---|---|---| +| `shadow-sm` | `0 2px 0 rgba(0, 0, 0, 0.40)` | Small controls, default cards, buttons. | +| `shadow-md` | `0 4px 12px rgba(0, 0, 0, 0.60)` | Dropdowns, popovers, hover cards. | +| `shadow-lg` | `0 8px 24px rgba(0, 0, 0, 0.70)` | Modals, command palettes, overlays. | + +Border system: + +| Token | Value | Usage | +|---|---|---| +| `border-default` | `1px solid rgba(255, 255, 255, 0.06)` | Large panel boundaries and subtle section divisions. | +| `border-strong` | `1px solid rgba(255, 255, 255, 0.12)` | Cards, inputs, tables, active panels. | +| `focus-ring` | `1px solid #A970FF` + soft purple glow | Keyboard focus and focused form controls. | + +Elevation rules: + +- Do not use bright outer glows on every card. Reserve glow for hover, focus, selected, or live/active states. +- Use `surface-hover` only when the component is interactive. +- Dropdowns and overlays may use `shadow-md` or `shadow-lg`, but must keep a visible border. +- Prefer border contrast over heavy shadows. + +## Shapes + +The shape language is squared, engineered, and pixel-aware. Corners should be low-radius, never pill-shaped except for toggles and circular progress indicators. + +Radius tokens: + +| Token | Value | Usage | +|---|---:|---| +| `none` | `0px` | Sharp dividers, grid cells, terminal containers. | +| `sm` | `4px` | Buttons, inputs, badges, tabs, pagination. | +| `md` | `8px` | Cards, dropdowns, list items, alert containers. | +| `lg` | `12px` | Larger panels and grouped modules. | +| `xl` | `16px` | Rare: large containers or empty states. | +| `full` | `9999px` | Toggles, circular indicators, avatars only. | + +Shape rules: + +- Default interactive radius: `4px`. +- Default card radius: `8px`. +- Do not mix pill buttons with pixel icons unless the component is a toggle. +- Icons may use square/pixel silhouettes even when placed inside a `4px` or `8px` container. + +## Components + +### Buttons + +Buttons are compact, high-contrast, and stateful. Use a right chevron or small icon only when the action moves the user forward. + +**Primary button** + +- Background: `primary` / `#A970FF`. +- Text: `white`. +- Radius: `sm` / `4px`. +- Padding: `12px 16px`. +- Font: `label`. +- Hover: slightly brighter purple `#B886FF` with a subtle purple outline/glow. +- Active: deeper purple `#7F45E8`. +- Disabled: muted dark purple background with low-contrast text. +- Use for the single main action on a screen, such as **Create project**. + +**Secondary button** + +- Background: transparent. +- Text and border: `secondary` / `#2D8CFF`. +- Hover: translucent blue fill. +- Use for secondary workflows, links, alternate actions, and utility actions. + +**Ghost button** + +- Background: transparent. +- Text: `success` / `#90FFB1`. +- Border: faint success border. +- Use for low-emphasis terminal-like actions such as **Learn more**. + +**Danger button** + +- Background: translucent `error`. +- Border: `error`. +- Text: `white`. +- Use only for destructive actions such as **Delete**. + +### Inputs + +Inputs should look like terminal command fields. + +- Default background: `background`. +- Border: `border-strong`. +- Radius: `sm`. +- Height: about `36px`. +- Padding: `12px 16px`. +- Placeholder: `text-subtle`. +- Focus state: purple border with a soft purple focus ring. +- Disabled state: darker background, muted placeholder, reduced border contrast. +- With icon: place icon left at `16px`, use muted text color unless active. + +Validation: + +- Success validation uses a `success` border or inline icon. +- Error validation uses an `error` border and a short helper message in `error` color. +- Never rely on color alone; include text or icon feedback. + +### Select + +Select components match inputs but add a green terminal-like chevron. + +- Default background: `background`. +- Border: `border-strong`. +- Text: `text`. +- Chevron: `success`. +- Focus: `primary` border. +- Open state: dropdown uses `surface`, visible border, and `shadow-md`. +- Options should be compact with `body-sm` typography and a `surface-hover` hover state. + +### Toggles and Checkboxes + +**Toggle** + +- On: track uses `success`, thumb uses `white`. +- Off: track uses `surface`, thumb uses `text-muted`, border uses `border-strong`. +- Shape: `full` radius. +- Keep the text label close to the control and indicate `ON` / `OFF` where helpful. + +**Checkbox** + +- Selected: fill `success`, checkmark `background`. +- Unselected: transparent fill, `border-strong` border. +- Size: `16px`. +- Radius: `sm`. +- Focus: purple focus ring. + +### Badges + +Badges are compact status indicators with monospace labels. + +| Variant | Background | Text | Usage | +|---|---|---|---| +| Default | `primary` | `white` | Generic status, selected metadata. | +| Success | `success` | `background` | Generated, active, completed, healthy. | +| Warning | `warning` | `background` | Warning, pending, needs review. | +| Error | `error` | `white` | Error, failed, destructive status. | +| Outline | transparent | `primary` | Neutral/secondary categorization. | + +Badge rules: + +- Use `label` typography. +- Use `4px 8px` padding. +- Keep text short: one or two words. +- Avoid placing more than three badges in a single card header. + +### Cards + +Cards are dark, bordered, compact containers. They may include pixel-art icons, short descriptions, and a ghost CTA. + +Default card: + +- Background: `surface`. +- Border: `1px solid rgba(255, 255, 255, 0.12)`. +- Radius: `md`. +- Padding: `16px`. +- Shadow: `shadow-sm`. + +Hover card: + +- Background: `surface-hover`. +- Border: faint `success` border. +- Shadow: subtle green glow. +- CTA: success text such as **Learn more β†’**. + +Card content hierarchy: + +1. Pixel icon or compact visual mark. +2. Title using `heading-md` or `label` depending on density. +3. Supporting copy using `body-sm`. +4. Optional ghost link in `success`. + +### List Items + +List items are compact, clickable rows with optional icon tile and trailing chevron. + +- Default background: `surface`. +- Hover: `surface-hover`. +- Selected: translucent `primary` background with `primary` border. +- Leading icon tile: small purple square with pixel-style icon. +- Title: `label` or `body-sm` with strong weight. +- Supporting text: `body-sm` or smaller muted text. +- Trailing indicator: chevron in `text-muted` or `text` on hover. + +### Tabs + +Tabs are rectangular and grid-aligned. + +- Active tab: `primary` background, `white` text. +- Inactive tab: `background` or `surface`, muted text, subtle border. +- Radius: `sm` on top corners. +- Tab panels: bordered `surface` panel with `16px` internal padding. +- Avoid animated pill tabs; this system should feel like a terminal panel switcher. + +### Pagination + +Pagination buttons use the same compact square geometry as controls. + +- Size: `32px` square. +- Active page: `primary` background, `white` text. +- Inactive page: `background` with `border-default`. +- Disabled arrows: muted text and reduced opacity. +- Use ellipsis for skipped ranges. + +### Progress + +Progress should communicate technical status, not decoration. + +Linear progress: + +- Track: dark `surface-active`. +- Fill: `primary`. +- Label: percentage in `body-sm`. +- Height: about `4px` to `6px`. + +Circular progress: + +- Track: dark surface ring. +- Fill: `primary`. +- Center label: percentage using `body` or `heading-md`. +- Use for compact status cards or dashboard widgets. + +### Data Visualization + +Charts should look like terminal analytics panels. + +General rules: + +- Background: `background` or `surface`. +- Grid lines: very subtle `rgba(255, 255, 255, 0.06)`. +- Axis labels: `text-muted`, `label` or `body-sm` typography. +- Chart labels and legends must be readable and not rely only on color. +- Use the chart color sequence from the Colors section. + +Bar charts: + +- Use `primary` bars by default. +- Bars may have a pixel/block feel rather than rounded pill bars. +- Keep value labels compact. + +Donut charts: + +- Use `primary`, `success`, `warning`, and `error` for semantic slices. +- Center label may show total value, e.g. `Total 128`. +- Legend should include percentages. + +Data tables: + +- Header row uses `label` typography, uppercase labels, muted text. +- Rows use `body-sm`. +- Borders are thin and low-contrast. +- Status values use semantic colors: Active = `success`, Warning = `warning`, Error = `error`. +- Pagination appears below the table with compact square buttons. + +### Alerts and Feedback + +Alerts are bordered, compact, and semantic. Use an icon at the left, title on top, and one short supporting line. + +| Variant | Border / Icon | Background | Usage | +|---|---|---|---| +| Success | `success` | `rgba(144, 255, 177, 0.06)` | Completion, saved state, healthy operation. | +| Info | `secondary` | `rgba(45, 140, 255, 0.06)` | Neutral information or guidance. | +| Warning | `warning` | `rgba(255, 192, 87, 0.06)` | Needs review, caution, non-blocking risk. | +| Error | `error` | `rgba(255, 80, 124, 0.06)` | Failed action, validation block, destructive consequence. | + +Alert copy: + +- Title should be one word or short phrase: `SUCCESS`, `INFO`, `WARNING`, `ERROR`. +- Description should be one sentence. +- Do not stack more than three alerts unless they are in a log/feed interface. + +### Loading and Empty States + +Loading states use dark skeleton blocks with a subtle highlight. + +Skeleton loading: + +- Background: `#2A2F31`. +- Highlight: `#3A4246`. +- Radius: `sm`. +- Use blocky rectangles, not soft shimmer-heavy placeholders. +- Match the structure of the final content: avatar block, title line, metadata lines, table rows. + +Empty states: + +- Use a pixel mascot or pixel icon when possible. +- Title: concise and direct, e.g. **No projects found**. +- Body: one sentence explaining how to start. +- CTA: primary button, e.g. **Create project β†’**. +- Empty state should sit inside a bordered panel, not on a pure blank page. + +### Iconography + +Iconography is pixel-inspired and should feel like a compact terminal icon set. + +Rules: + +- Style: pixel / 1.5px stroke. +- Default sizes: `16px`, `20px`, `24px`, `32px`. +- Use hard edges, simple silhouettes, and minimal detail. +- Default icon color: `primary` for brand/action icons and `success` for terminal/status icons. +- Warning/error icons must use their semantic token. +- Avoid realistic, filled, soft, or skeuomorphic icons. +- Icons should remain legible at `16px`. + +Recommended icon categories: + +- Terminal / command window. +- Rocket / launch. +- Lightning / automation. +- Cube / package. +- Database / storage. +- Code brackets. +- User / team. +- Gear / settings. +- Check / success. +- Plus / create. +- Bell / notification. +- Info / warning / error. +- Trash / delete. +- Search / inspect. +- Menu / list. + +## Do's and Don'ts + +Do: + +- Do use a near-black background with dark panels and visible 1px borders. +- Do keep primary actions purple and reserve `primary` for high-emphasis interactions. +- Do use green for terminal-like success, selected checkboxes, on toggles, and positive feedback. +- Do use monospace typography for dense UI, labels, tables, and code-oriented content. +- Do keep corners compact: `4px` for controls and `8px` for cards. +- Do use pixel-style icons and small mascot illustrations to reinforce the retro SDK identity. +- Do preserve keyboard focus states with visible purple outlines. +- Do use chart legends and labels so color is not the only source of meaning. + +Don't: + +- Don't use light mode unless a full separate palette is intentionally designed. +- Don't use large soft shadows, glassmorphism, glossy gradients, or rounded SaaS pill components. +- Don't overuse neon colors on large surfaces; use them as accents and state signals. +- Don't place multiple competing primary buttons in the same panel. +- Don't use decorative icons that become illegible at `16px`. +- Don't rely on color alone for errors, warnings, selected states, or chart meaning. +- Don't mix serif/editorial typography with this system. +- Don't use arbitrary spacing values outside the 4px-based scale. + +Accessibility guardrails: + +- Maintain WCAG AA contrast for normal text and interactive controls. +- Every interactive element must have a visible focus state. +- All flows must be keyboard navigable. +- Semantic alerts must include text plus icon, not color alone. +- Inputs must have associated labels or accessible names. +- Touch targets should be at least `44px Γ— 44px` on mobile, even when the visual control is smaller. +- Use `aria-current` for active pagination/tabs and `aria-selected` where appropriate. +- Use `aria-live` politely for async feedback such as generated/saved/completed messages. diff --git a/website/src/components/InstallModal.astro b/website/src/components/InstallModal.astro new file mode 100644 index 000000000..0ee526d4b --- /dev/null +++ b/website/src/components/InstallModal.astro @@ -0,0 +1,164 @@ +--- +export interface Props { + resourceType: string; +} + +const { resourceType } = Astro.props; +const label = resourceType; +--- + + +
+
+

Install in VS Code

+ +
+

Choose your VS Code edition to install this {label}.

+ +
+
+ + + + diff --git a/website/src/components/ShareModal.astro b/website/src/components/ShareModal.astro new file mode 100644 index 000000000..f29451279 --- /dev/null +++ b/website/src/components/ShareModal.astro @@ -0,0 +1,275 @@ + + + diff --git a/website/src/components/sections/ContributorsSection.astro b/website/src/components/sections/ContributorsSection.astro new file mode 100644 index 000000000..5a778c3cb --- /dev/null +++ b/website/src/components/sections/ContributorsSection.astro @@ -0,0 +1,198 @@ +--- +/** + * Contributors section β€” renders contributors as 8bit CLI cards. + * Reads `.all-contributorsrc` at build time and shows a preview of + * contributors, linking to the full /contributors/ page. + */ +import fs from 'node:fs'; +import BitButton from '../ui/8BitButton.astro'; +import Container from '../ui/Container.astro'; +import Icon from '../ui/Icon.astro'; + +interface AllContributor { + login: string; + name: string; + avatar_url: string; + profile: string; + contributions: string[]; +} + +interface AllContributorsRc { + projectName: string; + contributors: AllContributor[]; +} + +const rcPath = new URL('../../../../.all-contributorsrc', import.meta.url); +const rc = JSON.parse(fs.readFileSync(rcPath, 'utf-8')) as AllContributorsRc; + +const PREVIEW_COUNT = 24; +const contributors = rc.contributors.slice(0, PREVIEW_COUNT); +const total = rc.contributors.length; +--- +
+ +
+

$ git shortlog -sne | head -n {PREVIEW_COUNT}

+

Contributors

+

{total} humans shipping agents, skills, plugins, and instructions to the console.

+
+ + + +
+ View all {total} +
+
+
+ diff --git a/website/src/components/ui/CopyButton.astro b/website/src/components/ui/CopyButton.astro new file mode 100644 index 000000000..1f9effb01 --- /dev/null +++ b/website/src/components/ui/CopyButton.astro @@ -0,0 +1,59 @@ +--- +import Icon from './Icon.astro'; + +interface Props { + ariaLabel: string; + label?: string; + iconSize?: number; + class?: string; +} + +const { ariaLabel, label = 'Copy command', iconSize = 14, class: className } = Astro.props; +--- + + + + diff --git a/website/src/components/ui/InlineAlert.astro b/website/src/components/ui/InlineAlert.astro new file mode 100644 index 000000000..c8e0a22a3 --- /dev/null +++ b/website/src/components/ui/InlineAlert.astro @@ -0,0 +1,81 @@ +--- +import Icon from './Icon.astro'; + +export type AlertType = 'success' | 'warning' | 'error'; + +export interface Props { + type: AlertType; + title: string; +} + +const { type, title } = Astro.props; + +const iconMap: Record = { + success: 'task_alt', + warning: 'warning', + error: 'error', +}; + +const iconName = iconMap[type]; +--- + +
+
+ + diff --git a/website/src/lib/frontmatter.ts b/website/src/lib/frontmatter.ts new file mode 100644 index 000000000..99b4154bf --- /dev/null +++ b/website/src/lib/frontmatter.ts @@ -0,0 +1,10 @@ +export interface ParsedFrontmatter { + frontmatter: string | null; + body: string; +} + +export function parseFrontmatter(content: string): ParsedFrontmatter { + const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/); + if (!match) return { frontmatter: null, body: content }; + return { frontmatter: match[1].trim(), body: match[2] }; +} diff --git a/website/src/markdown/rehype-image-dimensions.mjs b/website/src/markdown/rehype-image-dimensions.mjs new file mode 100644 index 000000000..b93bb359a --- /dev/null +++ b/website/src/markdown/rehype-image-dimensions.mjs @@ -0,0 +1,165 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const dimensionsCache = new Map(); + +export function rehypeImageDimensions({ publicDir } = {}) { + const publicPath = fileURLToPath(publicDir ?? new URL("../../public/", import.meta.url)); + + return function addImageDimensions(tree) { + visitElements(tree, (node) => { + if (node.tagName !== "img") return; + + const properties = node.properties ?? {}; + const src = String(properties.src ?? ""); + if (!src.startsWith("/images/")) return; + + const dimensions = getImageDimensions(path.join(publicPath, src)); + if (!dimensions) return; + + const width = parseLength(properties.width); + const height = parseLength(properties.height); + + if (!width && !height) { + properties.width = dimensions.width; + properties.height = dimensions.height; + } else if (width && !height) { + properties.height = Math.round((dimensions.height * width) / dimensions.width); + } else if (!width && height) { + properties.width = Math.round((dimensions.width * height) / dimensions.height); + } + + node.properties = properties; + }); + }; +} + +function visitElements(node, visitor) { + if (!node || typeof node !== "object") return; + if (node.type === "element") visitor(node); + + if (!Array.isArray(node.children)) return; + for (const child of node.children) visitElements(child, visitor); +} + +function parseLength(value) { + if (typeof value === "number") return value; + if (typeof value !== "string") return undefined; + + const trimmed = value.trim(); + if (!/^\d+(\.\d+)?$/.test(trimmed)) return undefined; + + return Number(trimmed); +} + +function getImageDimensions(filePath) { + if (dimensionsCache.has(filePath)) return dimensionsCache.get(filePath); + + let dimensions; + try { + const buffer = readFileSync(filePath); + dimensions = readDimensions(buffer); + } catch { + dimensions = undefined; + } + + dimensionsCache.set(filePath, dimensions); + return dimensions; +} + +function readDimensions(buffer) { + if (isPng(buffer)) { + return { + width: buffer.readUInt32BE(16), + height: buffer.readUInt32BE(20), + }; + } + + if (isGif(buffer)) { + return { + width: buffer.readUInt16LE(6), + height: buffer.readUInt16LE(8), + }; + } + + if (isJpeg(buffer)) return readJpegDimensions(buffer); + if (isWebp(buffer)) return readWebpDimensions(buffer); + + return undefined; +} + +function isPng(buffer) { + return buffer.length > 24 && buffer.toString("ascii", 1, 4) === "PNG"; +} + +function isGif(buffer) { + return buffer.length > 10 && buffer.toString("ascii", 0, 3) === "GIF"; +} + +function isJpeg(buffer) { + return buffer.length > 4 && buffer[0] === 0xff && buffer[1] === 0xd8; +} + +function readJpegDimensions(buffer) { + let offset = 2; + + while (offset < buffer.length) { + if (buffer[offset] !== 0xff) return undefined; + + const marker = buffer[offset + 1]; + const size = buffer.readUInt16BE(offset + 2); + if (size < 2) return undefined; + + if (marker >= 0xc0 && marker <= 0xc3) { + return { + height: buffer.readUInt16BE(offset + 5), + width: buffer.readUInt16BE(offset + 7), + }; + } + + offset += 2 + size; + } + + return undefined; +} + +function isWebp(buffer) { + return ( + buffer.length > 30 && + buffer.toString("ascii", 0, 4) === "RIFF" && + buffer.toString("ascii", 8, 12) === "WEBP" + ); +} + +function readWebpDimensions(buffer) { + const format = buffer.toString("ascii", 12, 16); + + if (format === "VP8X") { + return { + width: readUInt24LE(buffer, 24) + 1, + height: readUInt24LE(buffer, 27) + 1, + }; + } + + if (format === "VP8 " && buffer.length > 29) { + return { + width: buffer.readUInt16LE(26) & 0x3fff, + height: buffer.readUInt16LE(28) & 0x3fff, + }; + } + + if (format === "VP8L" && buffer.length > 25) { + const bits = buffer.readUInt32LE(21); + return { + width: (bits & 0x3fff) + 1, + height: ((bits >> 14) & 0x3fff) + 1, + }; + } + + return undefined; +} + +function readUInt24LE(buffer, offset) { + return buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16); +} diff --git a/website/src/pages/agent/[id].astro b/website/src/pages/agent/[id].astro new file mode 100644 index 000000000..d411b77d3 --- /dev/null +++ b/website/src/pages/agent/[id].astro @@ -0,0 +1,458 @@ +--- +import fs from 'node:fs'; +import path from 'node:path'; +import { marked } from 'marked'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import Button from '../../components/ui/Button.astro'; +import Container from '../../components/ui/Container.astro'; +import Icon from '../../components/ui/Icon.astro'; +import InstallModal from '../../components/InstallModal.astro'; +import CopyButton from '../../components/ui/CopyButton.astro'; +import agentsData from '../../../public/data/agents.json'; +import type { AgentItem, AgentsData } from '../../lib/upstream-types'; +import { parseFrontmatter } from '../../lib/frontmatter'; + +export function getStaticPaths() { + return (agentsData as AgentsData).items.map((item: AgentItem) => ({ + params: { id: item.id }, + })); +} + +const { id } = Astro.params; +const item = (agentsData as AgentsData).items.find((i: AgentItem) => i.id === id); +const repoRoot = path.resolve(process.cwd(), '..'); + let markdownHtml = ''; + let frontmatterText: string | null = null; + if (item) { + const mdPath = path.resolve(repoRoot, item.path); + try { + const md = fs.readFileSync(mdPath, 'utf-8'); + const parsed = parseFrontmatter(md); + frontmatterText = parsed.frontmatter; + markdownHtml = marked.parse(parsed.body) as string; + } catch { + markdownHtml = ''; + } + } +--- + + +
+ + + +
+
+
+

cat ./agents/loading.agent.md

+
+ loading +

Loading agent

+

Fetching agent details on demand.

+
+
+ + +
+
+ +
+

At a glance

+

Execution profile

+

Loading agent execution profile.

+
+

Model

loading

+

Tools

loading

+

Handoffs

loading

+
+
+ + {markdownHtml ? ( +
+

{item ? item.path : 'agents/loading.agent.md'}

readonly
+

Documentation

+
+
+ {frontmatterText ?
{`---\n${frontmatterText}\n---`}
: null} +
+
+
+
+ ) : null} + +
+

Usage

+

Install and run from your workspace

+
+
npx
+
npx awesome-copilot add agents/loading.agent.md
+
+

Use this command from your project root, then open the agent from your Copilot setup.

+
+ +
+

agents/loading.agent.md

readonly
+

Agent file preview

+
+
name
loading
+
model
loading
+
tools
loading
+
handoffs
loading
+
+
+
+ + +
+
+
+ +
+ + + + diff --git a/website/src/pages/hook/[id].astro b/website/src/pages/hook/[id].astro new file mode 100644 index 000000000..55f635a02 --- /dev/null +++ b/website/src/pages/hook/[id].astro @@ -0,0 +1,46 @@ +--- +import fs from 'node:fs'; +import path from 'node:path'; +import { marked } from 'marked'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../../components/ResourceDetailPage.astro'; +import hooksData from '../../../public/data/hooks.json'; +import type { HookItem, HooksData } from '../../lib/upstream-types'; +import { parseFrontmatter } from '../../lib/frontmatter'; + +export function getStaticPaths() { + return (hooksData as HooksData).items.map((item: HookItem) => ({ + params: { id: item.id }, + })); +} + +const { id } = Astro.params; +const item = (hooksData as HooksData).items.find((i: HookItem) => i.id === id); +const repoRoot = path.resolve(process.cwd(), '..'); + let markdownHtml = ''; + let frontmatterText: string | null = null; + if (item) { + const mdPath = item.readmeFile + ? path.resolve(repoRoot, item.readmeFile) + : path.resolve(repoRoot, item.path, 'README.md'); + try { + const md = fs.readFileSync(mdPath, 'utf-8'); + const parsed = parseFrontmatter(md); + frontmatterText = parsed.frontmatter; + markdownHtml = marked.parse(parsed.body) as string; + } catch { + markdownHtml = ''; + } + } +--- + + + + + + diff --git a/website/src/pages/instruction/[id].astro b/website/src/pages/instruction/[id].astro new file mode 100644 index 000000000..2775eb197 --- /dev/null +++ b/website/src/pages/instruction/[id].astro @@ -0,0 +1,464 @@ +--- +import fs from 'node:fs'; +import path from 'node:path'; +import { marked } from 'marked'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import Button from '../../components/ui/Button.astro'; +import Container from '../../components/ui/Container.astro'; +import Icon from '../../components/ui/Icon.astro'; +import InstallModal from '../../components/InstallModal.astro'; +import CopyButton from '../../components/ui/CopyButton.astro'; +import instructionsData from '../../../public/data/instructions.json'; +import type { InstructionItem, InstructionsData } from '../../lib/upstream-types'; +import { parseFrontmatter } from '../../lib/frontmatter'; + +export function getStaticPaths() { + return (instructionsData as InstructionsData).items.map((item: InstructionItem) => ({ + params: { id: item.id }, + })); +} + +const { id } = Astro.params; +const item = (instructionsData as InstructionsData).items.find((i: InstructionItem) => i.id === id); +const repoRoot = path.resolve(process.cwd(), '..'); + let markdownHtml = ''; + let frontmatterText: string | null = null; + if (item) { + const mdPath = path.resolve(repoRoot, item.path); + try { + const md = fs.readFileSync(mdPath, 'utf-8'); + const parsed = parseFrontmatter(md); + frontmatterText = parsed.frontmatter; + markdownHtml = marked.parse(parsed.body) as string; + } catch { + markdownHtml = ''; + } + } +--- + + +
+ + + +
+
+
+

cat ./instructions/loading.instructions.md

+
+ loading +

Loading instruction

+

Fetching instruction details on demand.

+
+
+ + +
+
+ +
+

At a glance

+

Scope and coverage

+

Loading instruction scope.

+
+

Apply to

loading

+

Extensions

loading

+

Patterns

loading

+
+
+ + {markdownHtml ? ( +
+

{item ? item.path : 'instructions/loading.instructions.md'}

readonly
+

Documentation

+
+
+ {frontmatterText ?
{`---\n${frontmatterText}\n---`}
: null} +
+
+
+
+ ) : null} + +
+

Usage

+

Install and run from your workspace

+
+
npx
+
npx awesome-copilot add instructions/loading.instructions.md
+
+

Use this command from your project root, then apply the instruction from your Copilot setup.

+
+ +
+

instructions/loading.instructions.md

readonly
+

Instruction file metadata

+
+
name
loading
+
applyTo
loading
+
extensions
loading
+
updated
loading
+
+
+
+ + +
+
+
+ +
+ + + + diff --git a/website/src/pages/plugin/[id].astro b/website/src/pages/plugin/[id].astro new file mode 100644 index 000000000..912e39ea0 --- /dev/null +++ b/website/src/pages/plugin/[id].astro @@ -0,0 +1,44 @@ +--- +import fs from 'node:fs'; +import path from 'node:path'; +import { marked } from 'marked'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../../components/ResourceDetailPage.astro'; +import pluginsData from '../../../public/data/plugins.json'; +import type { PluginItem, PluginsData } from '../../lib/upstream-types'; +import { parseFrontmatter } from '../../lib/frontmatter'; + +export function getStaticPaths() { + return (pluginsData as PluginsData).items.map((item: PluginItem) => ({ + params: { id: item.id }, + })); +} + +const { id } = Astro.params; +const item = (pluginsData as PluginsData).items.find((i: PluginItem) => i.id === id); +const repoRoot = path.resolve(process.cwd(), '..'); + let markdownHtml = ''; + let frontmatterText: string | null = null; + if (item) { + const mdPath = path.resolve(repoRoot, item.path, 'README.md'); + try { + const md = fs.readFileSync(mdPath, 'utf-8'); + const parsed = parseFrontmatter(md); + frontmatterText = parsed.frontmatter; + markdownHtml = marked.parse(parsed.body) as string; + } catch { + markdownHtml = ''; + } + } +--- + + + + + + diff --git a/website/src/pages/skill/[id].astro b/website/src/pages/skill/[id].astro new file mode 100644 index 000000000..05b0eb6ab --- /dev/null +++ b/website/src/pages/skill/[id].astro @@ -0,0 +1,894 @@ +--- +import fs from 'node:fs'; +import path from 'node:path'; +import { marked } from 'marked'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import Badge from '../../components/ui/Badge.astro'; +import Button from '../../components/ui/Button.astro'; +import Container from '../../components/ui/Container.astro'; +import CopyButton from '../../components/ui/CopyButton.astro'; +import Icon from '../../components/ui/Icon.astro'; +import InstallModal from '../../components/InstallModal.astro'; +import skillsData from '../../../public/data/skills.json'; +import type { SkillItem } from '../../lib/upstream-types'; + +const repoRoot = path.resolve(process.cwd(), '..'); + +export function getStaticPaths() { + return skillsData.items.map((skill: SkillItem) => ({ + params: { id: skill.id }, + props: { skill } + })); +} + +function parseFrontmatter(content: string): { frontmatter: string | null; body: string } { + const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/); + if (!match) return { frontmatter: null, body: content }; + return { frontmatter: match[1].trim(), body: match[2] }; +} + +function splitSections(body: string): { title: string; body: string }[] { + const sections: { title: string; body: string }[] = []; + const lines = body.split('\n'); + let current: { title: string; body: string } | null = null; + + for (const line of lines) { + const headingMatch = line.match(/^##\s+(.+)/); + if (headingMatch) { + if (current) sections.push({ title: current.title, body: current.body.trimEnd() }); + current = { title: headingMatch[1], body: '' }; + } else if (current) { + current.body += line + '\n'; + } + } + if (current) sections.push({ title: current.title, body: current.body.trimEnd() }); + + return sections; +} + +function readFileContents(skillPath: string, fileName: string): string | null { + try { + return fs.readFileSync(path.resolve(repoRoot, skillPath, fileName), 'utf-8'); + } catch { + return null; + } +} + +const { skill } = Astro.props; +const skillPath = skill.path; +const skillFilePath = skill.skillFile; +const installCommand = `npx awesome-copilot add ${skillPath}`; +const sourcePath = `awesome-copilot/${skillPath}`; + +function getFileType(filePath: string): string { + if (filePath.endsWith('SKILL.md')) return 'skill'; + if (filePath.includes('/scripts/')) return 'script'; + if (filePath.includes('/templates/')) return 'template'; + if (filePath.includes('/references/')) return 'reference'; + if (filePath.includes('/assets/')) return 'asset'; + if (filePath.includes('/evals/')) return 'eval'; + return 'file'; +} + +function getFileDescription(filePath: string, type: string): string { + const name = filePath.split('/').pop() ?? filePath; + if (type === 'skill') return 'Main skill definition'; + if (type === 'script') return `Executable script: ${name}`; + if (type === 'template') return `Template file: ${name}`; + if (type === 'reference') return `Reference: ${name}`; + if (type === 'asset') return `Bundled asset: ${name}`; + if (type === 'eval') return `Evaluation: ${name}`; + return name; +} + +const resourceFiles = skill.files.map((file) => ({ + path: file.name, + type: getFileType(file.path), + description: getFileDescription(file.path, getFileType(file.path)), +})); + +const fileExplorerFolders = ['script', 'template', 'reference', 'asset', 'eval'].map((type) => ({ + type, + name: `${type === 'eval' ? 'evals' : `${type}s`}`, + files: resourceFiles.filter((file) => file.type === type), +})).filter((folder) => folder.files.length > 0); + +const skillFile = resourceFiles.find((file) => file.type === 'skill'); + +const rawSkillMd = readFileContents(skillPath, 'SKILL.md'); +const parsedSkill = rawSkillMd ? parseFrontmatter(rawSkillMd) : null; +const parsedContent = parsedSkill?.body.trim() ?? ''; +const skillBody = parsedContent || skill.description; +const skillBodyHtml = parsedContent ? marked.parse(parsedContent) : `

${skill.description}

`; +const skillSections = parsedContent ? splitSections(parsedContent) : []; + +const previewFiles = [ + { + path: 'SKILL.md', + title: 'SKILL.md preview', + language: 'markdown', + description: skillFile?.description ?? 'Main skill definition', + code: rawSkillMd || `---\nname: ${skill.id}\ndescription: ${skill.description}\n---\n\n${skill.description}`, + frontmatter: parsedSkill?.frontmatter ?? `name: ${skill.id}\ndescription: ${skill.description}`, + html: skillBodyHtml, + sections: skillSections.map((s) => ({ title: s.title, html: marked.parse(s.body) as string })), + }, + ...resourceFiles + .filter((file) => file.type !== 'skill') + .map((file) => { + const content = readFileContents(skillPath, file.path); + const isMarkdown = file.path.endsWith('.md'); + const html = content && isMarkdown ? marked.parse(content) : null; + return { + path: file.path, + title: `${file.path} preview`, + language: file.path.endsWith('.json') ? 'json' : file.path.endsWith('.ts') ? 'typescript' : file.path.endsWith('.py') ? 'python' : 'markdown', + description: file.description, + code: content || `# ${file.path}\n\nFile content not available.`, + frontmatter: null, + html, + sections: [] as { title: string; html: string }[], + }; + }) +]; +--- + +
+ + + +
+
+
+

cat ./{skillFilePath}

+
+ {skill.hasAssets ? `${skill.assetCount} assets` : 'skill'} +

{skill.title}

+

{skill.description}

+
+
+ + +
+
+ +
+
+

File Explorer

+ {skill.files.length} files +
+
+ {fileExplorerFolders.map((folder) => ( +
+ + +
+ ))} + +
+
+ +
+
+

{skillFilePath}

+
+ readonly + +
+
+

SKILL.md preview

+ {previewFiles.map((file, index) => ( +
+ +
+

Usage

+

Install the complete skill directory

+
+
+ npx +
+ +
+
+
{installCommand}
+
+

Use this from your project root. The installer should preserve the whole skill directory, not just the markdown file.

+
+
+ + +
+
+
+ +
+ + + + + diff --git a/website/src/pages/tool/[id].astro b/website/src/pages/tool/[id].astro new file mode 100644 index 000000000..13f2890fb --- /dev/null +++ b/website/src/pages/tool/[id].astro @@ -0,0 +1,23 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../../components/ResourceDetailPage.astro'; +import toolsData from '../../../public/data/tools.json'; +import type { ToolItem, ToolsData } from '../../lib/upstream-types'; + +export function getStaticPaths() { + return (toolsData as ToolsData).items.map((item: ToolItem) => ({ + params: { id: item.id }, + })); +} +--- + + + + + + diff --git a/website/src/pages/workflow/[id].astro b/website/src/pages/workflow/[id].astro new file mode 100644 index 000000000..d9c93a339 --- /dev/null +++ b/website/src/pages/workflow/[id].astro @@ -0,0 +1,44 @@ +--- +import fs from 'node:fs'; +import path from 'node:path'; +import { marked } from 'marked'; +import BaseLayout from '../../layouts/BaseLayout.astro'; +import ResourceDetailPage from '../../components/ResourceDetailPage.astro'; +import workflowsData from '../../../public/data/workflows.json'; +import type { WorkflowItem, WorkflowsData } from '../../lib/upstream-types'; +import { parseFrontmatter } from '../../lib/frontmatter'; + +export function getStaticPaths() { + return (workflowsData as WorkflowsData).items.map((item: WorkflowItem) => ({ + params: { id: item.id }, + })); +} + +const { id } = Astro.params; +const item = (workflowsData as WorkflowsData).items.find((i: WorkflowItem) => i.id === id); +const repoRoot = path.resolve(process.cwd(), '..'); + let markdownHtml = ''; + let frontmatterText: string | null = null; + if (item) { + const mdPath = path.resolve(repoRoot, item.path); + try { + const md = fs.readFileSync(mdPath, 'utf-8'); + const parsed = parseFrontmatter(md); + frontmatterText = parsed.frontmatter; + markdownHtml = marked.parse(parsed.body) as string; + } catch { + markdownHtml = ''; + } + } +--- + + + + + + diff --git a/website/src/scripts/custom-dropdown.ts b/website/src/scripts/custom-dropdown.ts new file mode 100644 index 000000000..22178667f --- /dev/null +++ b/website/src/scripts/custom-dropdown.ts @@ -0,0 +1,165 @@ +/** + * Lightweight custom dropdown for select elements. + * Supports both single-select and multi-select controls. + */ + +type DropdownController = { + container: HTMLElement; + menu: HTMLElement; + select: HTMLSelectElement; + close: () => void; + open: () => void; +}; + +const dropdowns: DropdownController[] = []; +let globalListenersReady = false; + +export function initCustomDropdowns() { + document.querySelectorAll('.console-select-control select').forEach(select => { + if (select.dataset.customDropdownReady === 'true') return; + + const container = select.closest('.console-select-control'); + if (!container) return; + + select.dataset.customDropdownReady = 'true'; + select.classList.add('native-select-hidden'); + container.classList.add('custom-dropdown-control'); + container.setAttribute('aria-haspopup', 'listbox'); + container.setAttribute('aria-expanded', 'false'); + if (!container.hasAttribute('tabindex')) container.tabIndex = 0; + + const menu = document.createElement('div'); + menu.className = 'custom-dropdown-menu'; + menu.setAttribute('role', 'listbox'); + if (select.multiple) menu.setAttribute('aria-multiselectable', 'true'); + menu.hidden = true; + + Array.from(select.options).forEach(option => { + const item = document.createElement('button'); + item.type = 'button'; + item.className = 'custom-dropdown-item'; + if (select.multiple) item.classList.add('custom-dropdown-item--multi'); + item.textContent = option.text; + item.dataset.value = option.value; + item.setAttribute('role', 'option'); + if (option.selected) item.classList.add('is-selected'); + + item.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + + if (select.multiple) { + if (!option.value) { + Array.from(select.options).forEach(selectOption => { + selectOption.selected = false; + }); + } else { + option.selected = !option.selected; + const emptyOption = Array.from(select.options).find(selectOption => !selectOption.value); + if (emptyOption) emptyOption.selected = false; + } + + syncSelectedItems(menu, getSelectedValues(select)); + } else { + select.value = option.value; + syncSelectedItems(menu, [select.value]); + closeDropdown(menu, container); + } + + select.dispatchEvent(new Event('change', { bubbles: true })); + }); + + menu.appendChild(item); + }); + + document.body.appendChild(menu); + + const controller: DropdownController = { + container, + menu, + select, + close: () => closeDropdown(menu, container), + open: () => openDropdown(menu, container, select), + }; + dropdowns.push(controller); + + container.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + + if (menu.hidden) { + closeAllDropdowns(); + controller.open(); + } else { + controller.close(); + } + }); + + container.addEventListener('keydown', (event) => { + if (event.key === 'Escape') { + controller.close(); + return; + } + + if (event.key !== 'Enter' && event.key !== ' ' && event.key !== 'ArrowDown') return; + event.preventDefault(); + closeAllDropdowns(); + controller.open(); + }); + }); + + if (!globalListenersReady) { + document.addEventListener('click', (event) => { + const target = event.target as Node; + if (dropdowns.some(({ container, menu }) => container.contains(target) || menu.contains(target))) return; + closeAllDropdowns(); + }); + + window.addEventListener('resize', closeAllDropdowns); + window.addEventListener('scroll', (event) => { + const target = event.target; + if (target instanceof Node && dropdowns.some(({ menu }) => menu.contains(target))) return; + closeAllDropdowns(); + }, true); + globalListenersReady = true; + } +} + +function openDropdown(menu: HTMLElement, container: HTMLElement, select: HTMLSelectElement): void { + syncSelectedItems(menu, getSelectedValues(select)); + positionMenu(menu, container); + menu.hidden = false; + container.classList.add('custom-dropdown-control--open'); + container.setAttribute('aria-expanded', 'true'); +} + +function closeDropdown(menu: HTMLElement, container: HTMLElement): void { + menu.hidden = true; + container.classList.remove('custom-dropdown-control--open'); + container.setAttribute('aria-expanded', 'false'); +} + +function closeAllDropdowns(): void { + dropdowns.forEach(({ close }) => close()); +} + +function positionMenu(menu: HTMLElement, container: HTMLElement): void { + const rect = container.getBoundingClientRect(); + menu.style.left = `${rect.left}px`; + menu.style.top = `${rect.bottom + 4}px`; + menu.style.minWidth = `${rect.width}px`; + menu.style.maxWidth = `${Math.max(rect.width, 320)}px`; +} + +function syncSelectedItems(menu: HTMLElement, values: string[]): void { + menu.querySelectorAll('.custom-dropdown-item').forEach(item => { + const itemValue = item.dataset.value ?? ''; + const selected = values.length === 0 ? itemValue === '' : values.includes(itemValue); + item.classList.toggle('is-selected', selected); + item.setAttribute('aria-selected', selected ? 'true' : 'false'); + }); +} + +function getSelectedValues(select: HTMLSelectElement): string[] { + return Array.from(select.selectedOptions).map(option => option.value).filter(Boolean); +} diff --git a/website/src/scripts/share-modal.ts b/website/src/scripts/share-modal.ts new file mode 100644 index 000000000..e5a34ab07 --- /dev/null +++ b/website/src/scripts/share-modal.ts @@ -0,0 +1,138 @@ +import { SITE } from "../config/site"; +import { copyToClipboard, truncate } from "./utils"; + +export interface ShareModalData { + title: string; + description: string; + url: string; + badge?: string; +} + +let currentTrigger: HTMLElement | null = null; +let listenersReady = false; + +function normalizeShareUrl(url: string): string { + if (URL.canParse(url)) return url; + return new URL(url, SITE.url).toString(); +} + +function buildShareText(title: string): string { + return `${title} Β· ${SITE.title}`; +} + +function buildShareLinks(url: string, title: string): Record { + const shareUrl = encodeURIComponent(url); + const shareText = encodeURIComponent(buildShareText(title)); + + return { + x: `https://twitter.com/intent/tweet?text=${shareText}&url=${shareUrl}`, + whatsapp: `https://wa.me/?text=${shareText}%20${shareUrl}`, + linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${shareUrl}`, + }; +} + +function ensureShareModalListeners(dialog: HTMLDialogElement): void { + if (listenersReady) return; + listenersReady = true; + + const closeBtn = document.getElementById("share-modal-close"); + const copyBtn = document.getElementById( + "share-modal-copy" + ) as HTMLButtonElement | null; + + closeBtn?.addEventListener("click", () => { + dialog.close(); + }); + + dialog.addEventListener("click", (event) => { + if (event.target === dialog) dialog.close(); + }); + + dialog.addEventListener("close", () => { + const trigger = currentTrigger; + currentTrigger = null; + if (trigger) requestAnimationFrame(() => trigger.focus()); + }); + + document.addEventListener("keydown", (event) => { + if (event.key === "Escape" && dialog.open) dialog.close(); + }); + + copyBtn?.addEventListener("click", async () => { + const shareUrl = copyBtn.dataset.shareUrl; + if (!shareUrl) return; + + const originalLabel = copyBtn.querySelector("span")?.textContent ?? "Copy link"; + const success = await copyToClipboard(shareUrl); + const label = copyBtn.querySelector("span"); + if (label) label.textContent = success ? "Copied" : "Failed"; + + window.setTimeout(() => { + if (label) label.textContent = originalLabel; + }, 1600); + }); +} + +export function openShareModal( + payload: ShareModalData, + trigger?: HTMLElement +): boolean { + const dialog = document.getElementById("share-modal") as HTMLDialogElement | null; + const title = document.getElementById("share-modal-title"); + const description = document.getElementById("share-modal-desc"); + const previewBadge = document.getElementById("share-preview-badge"); + const previewTitle = document.getElementById("share-preview-title"); + const previewDescription = document.getElementById("share-preview-description"); + const previewUrl = document.getElementById("share-preview-url"); + const copyBtn = document.getElementById( + "share-modal-copy" + ) as HTMLButtonElement | null; + const xLink = document.getElementById("share-modal-x") as HTMLAnchorElement | null; + const whatsappLink = document.getElementById( + "share-modal-whatsapp" + ) as HTMLAnchorElement | null; + const linkedinLink = document.getElementById( + "share-modal-linkedin" + ) as HTMLAnchorElement | null; + + if ( + !dialog || + !title || + !description || + !previewBadge || + !previewTitle || + !previewDescription || + !previewUrl || + !copyBtn || + !xLink || + !whatsappLink || + !linkedinLink + ) { + return false; + } + + const shareUrl = normalizeShareUrl(payload.url); + const links = buildShareLinks(shareUrl, payload.title); + + currentTrigger = trigger ?? (document.activeElement as HTMLElement | null); + ensureShareModalListeners(dialog); + + title.textContent = `Share ${payload.title}`; + description.textContent = "Share this page on your preferred network or copy the canonical link."; + previewBadge.textContent = (payload.badge ?? "Resource").toUpperCase(); + previewTitle.textContent = payload.title; + previewDescription.textContent = truncate(payload.description, 180); + previewUrl.textContent = shareUrl.replace(/^https?:\/\//, ""); + + xLink.href = links.x; + whatsappLink.href = links.whatsapp; + linkedinLink.href = links.linkedin; + copyBtn.dataset.shareUrl = shareUrl; + + dialog.showModal(); + requestAnimationFrame(() => { + (document.getElementById("share-modal-close") as HTMLButtonElement | null)?.focus(); + }); + + return true; +} From 4051cf848698cf220eae45fdaa1e9a4a6b52ed24 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Tue, 23 Jun 2026 00:25:29 -0500 Subject: [PATCH 14/26] refactor(website-pages): reorganize routes and update adapters/ui wiring --- website/astro.config.mjs | 61 +- website/package-lock.json | 2924 +++++++++-------- website/package.json | 4 +- website/src/components/BackToTop.astro | 17 +- website/src/components/Head.astro | 2 + website/src/components/Modal.astro | 10 +- .../src/components/ResourceDetailPage.astro | 138 +- website/src/components/ThemeToggle.astro | 26 +- .../src/components/layout/Navigation.astro | 47 +- .../components/resource/ResourceCard.astro | 3 +- .../components/resource/ResourceModal.astro | 18 +- .../src/components/sections/CTASection.astro | 88 +- .../src/components/sections/HeroSection.astro | 380 ++- website/src/components/ui/8BitButton.astro | 25 +- website/src/components/ui/Button.astro | 4 + website/src/components/ui/Icon.astro | 2 + .../src/components/ui/RetroResourceCard.astro | 12 +- .../cli-for-beginners/00-quick-start.md | 12 +- .../01-setup-and-first-steps.md | 14 +- .../02-context-and-conversations.md | 60 +- .../03-development-workflows.md | 62 +- .../04-agents-and-custom-instructions.md | 24 +- .../cli-for-beginners/05-skills.md | 32 +- .../cli-for-beginners/06-mcp-servers.md | 48 +- .../07-putting-it-all-together.md | 12 +- .../learning-hub/cli-for-beginners/index.md | 1 - website/src/layouts/BaseLayout.astro | 27 + website/src/lib/adapters/agents.ts | 4 +- website/src/lib/adapters/hooks.ts | 5 +- website/src/lib/adapters/instructions.ts | 5 +- website/src/lib/adapters/manifest.ts | 9 +- website/src/lib/adapters/plugins.ts | 5 +- website/src/lib/adapters/skills.ts | 5 +- website/src/lib/adapters/tools.ts | 4 +- website/src/lib/adapters/workflows.ts | 5 +- website/src/lib/types.ts | 4 +- website/src/pages/agent.astro | 283 -- website/src/pages/agents.astro | 155 +- website/src/pages/hook.astro | 15 - website/src/pages/hooks.astro | 54 +- website/src/pages/index.astro | 3 + website/src/pages/instruction.astro | 290 -- website/src/pages/instructions.astro | 90 +- .../pages/learning-hub/cookbook/index.astro | 19 +- website/src/pages/plugin.astro | 15 - website/src/pages/plugins.astro | 406 ++- website/src/pages/skill.astro | 15 - website/src/pages/skills.astro | 65 +- website/src/pages/tool.astro | 15 - website/src/pages/tools.astro | 57 +- website/src/pages/workflow.astro | 15 - website/src/pages/workflows.astro | 56 +- website/src/scripts/modal.ts | 14 +- website/src/scripts/pages/agents-render.ts | 102 +- .../src/scripts/pages/extensions-render.ts | 171 +- website/src/scripts/pages/hooks-render.ts | 10 +- website/src/scripts/pages/hooks.ts | 263 +- .../src/scripts/pages/instructions-render.ts | 53 +- website/src/scripts/pages/plugins-render.ts | 4 +- website/src/scripts/pages/samples-render.ts | 12 +- website/src/scripts/pages/skills-render.ts | 16 +- website/src/scripts/pages/skills.ts | 248 +- website/src/scripts/pages/tools-render.ts | 22 +- website/src/scripts/pages/tools.ts | 2 +- website/src/scripts/pages/workflows-render.ts | 2 +- website/src/scripts/resource-catalog.ts | 76 +- website/src/scripts/resource-detail.ts | 153 +- website/src/scripts/utils.ts | 79 +- website/src/styles/global.css | 211 +- website/src/styles/starlight-overrides.css | 332 +- 70 files changed, 4146 insertions(+), 3276 deletions(-) delete mode 100644 website/src/pages/agent.astro delete mode 100644 website/src/pages/hook.astro delete mode 100644 website/src/pages/instruction.astro delete mode 100644 website/src/pages/plugin.astro delete mode 100644 website/src/pages/skill.astro delete mode 100644 website/src/pages/tool.astro delete mode 100644 website/src/pages/workflow.astro diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 1356a8e24..11e70de2a 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -2,23 +2,21 @@ import sitemap from "@astrojs/sitemap"; import starlight from "@astrojs/starlight"; import { defineConfig } from "astro/config"; import pagefindResources from "./src/integrations/pagefind-resources"; +import { rehypeImageDimensions } from "./src/markdown/rehype-image-dimensions.mjs"; const site = "https://awesome-copilot.github.com/"; const siteDescription = "Community-contributed agents, instructions, and skills to enhance your GitHub Copilot experience"; -// Social preview image used for all Open Graph / Twitter cards (e.g. LinkedIn, which is -// Open Graph-driven). socialImageWidth/Height MUST match the actual pixels of social-image.png. -// If a page ever overrides og:image, also override og:image:width/height and twitter:image -// (Head.astro derives og:image:secure_url from twitter:image first). const socialImageUrl = new URL("/images/social-image.png", site).toString(); -const socialImageWidth = "2400"; -const socialImageHeight = "1260"; // https://astro.build/config export default defineConfig({ site, base: "/", output: "static", + markdown: { + rehypePlugins: [[rehypeImageDimensions, { publicDir: new URL("./public/", import.meta.url) }]], + }, integrations: [ starlight({ title: "Awesome GitHub Copilot", @@ -33,20 +31,6 @@ export default defineConfig({ content: socialImageUrl, }, }, - { - tag: "meta", - attrs: { - property: "og:image:width", - content: socialImageWidth, - }, - }, - { - tag: "meta", - attrs: { - property: "og:image:height", - content: socialImageHeight, - }, - }, { tag: "meta", attrs: { @@ -62,20 +46,30 @@ export default defineConfig({ }, }, ], - customCss: [ - "./src/styles/starlight-overrides.css", - "./src/styles/global.css", - ], + customCss: ["./src/styles/global.css"], editLink: { baseUrl: "https://github.com/github/awesome-copilot/edit/staged/website/", }, sidebar: [ + { + label: "Browse Resources", + items: [ + { label: "Home", link: "/" }, + { label: "Agents", link: "/agents/" }, + { label: "Instructions", link: "/instructions/" }, + { label: "Skills", link: "/skills/" }, + { label: "Hooks", link: "/hooks/" }, + { label: "Workflows", link: "/workflows/" }, + { label: "Canvas Extensions", link: "/extensions/" }, + { label: "Plugins", link: "/plugins/" }, + { label: "Tools", link: "/tools/" }, + { label: "Contributors", link: "/contributors/" }, + ], + }, { label: "Fundamentals", items: [ - "learning-hub/github-copilot-app", - "learning-hub/working-with-canvas-extensions", "learning-hub/what-are-agents-skills-instructions", "learning-hub/agents-and-subagents", "learning-hub/understanding-copilot-context", @@ -121,21 +115,6 @@ export default defineConfig({ }, ], }, - { - label: "Browse Resources", - items: [ - { label: "Home", link: "/" }, - { label: "Agents", link: "/agents/" }, - { label: "Instructions", link: "/instructions/" }, - { label: "Skills", link: "/skills/" }, - { label: "Hooks", link: "/hooks/" }, - { label: "Workflows", link: "/workflows/" }, - { label: "Canvas Extensions", link: "/extensions/" }, - { label: "Plugins", link: "/plugins/" }, - { label: "Tools", link: "/tools/" }, - { label: "Contributors", link: "/contributors/" }, - ], - }, ], disable404Route: true, // Search is rendered by our custom Search component and indexed by diff --git a/website/package-lock.json b/website/package-lock.json index 0890879b4..f7a7ce6ac 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -9,229 +9,139 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@astrojs/check": "^0.9.9", "@astrojs/sitemap": "^3.7.2", - "@astrojs/starlight": "^0.41.1", - "astro": "^7.0.3", + "@astrojs/starlight": "^0.38.4", + "astro": "^6.1.9", "choices.js": "^11.2.2", "front-matter": "^4.0.2", "gray-matter": "^4.0.3", "jszip": "^3.10.1", "marked": "^18.0.2", - "shiki": "^4.0.2" + "shiki": "^4.0.2", + "typescript": "^5.9.3" } }, - "node_modules/@astrojs/compiler-binding": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.2.3.tgz", - "integrity": "sha512-Xz3iBNse+hXXD25IXxsuXEt2ai8klAWE15CRm/EQBc9+aE3jXaF07DZx+iakk3HC6NHvWlEPzLPyxsLgPzOJsw==", + "node_modules/@astrojs/check": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.9.tgz", + "integrity": "sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==", "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" + "dependencies": { + "@astrojs/language-server": "^2.16.7", + "chokidar": "^4.0.3", + "kleur": "^4.1.5", + "yargs": "^17.7.2" }, - "optionalDependencies": { - "@astrojs/compiler-binding-darwin-arm64": "0.2.3", - "@astrojs/compiler-binding-darwin-x64": "0.2.3", - "@astrojs/compiler-binding-linux-arm64-gnu": "0.2.3", - "@astrojs/compiler-binding-linux-arm64-musl": "0.2.3", - "@astrojs/compiler-binding-linux-x64-gnu": "0.2.3", - "@astrojs/compiler-binding-linux-x64-musl": "0.2.3", - "@astrojs/compiler-binding-wasm32-wasi": "0.2.3", - "@astrojs/compiler-binding-win32-arm64-msvc": "0.2.3", - "@astrojs/compiler-binding-win32-x64-msvc": "0.2.3" - } - }, - "node_modules/@astrojs/compiler-binding-darwin-arm64": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.2.3.tgz", - "integrity": "sha512-sJIHeL1ONXEBLob8ZaXfmX6iCftUno08G/cMXj2FJnL0xNbHuELcEq1mjxHVFHNgUYu4P7xJNm2mpc0zUEPoKw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-darwin-x64": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.2.3.tgz", - "integrity": "sha512-P0NYu6aaIeLCqFfszxxBHL0a5WRaYigNVbDoO654Gi5Q2au5duDb5xZBv5EqUg4qnQVC173FXNvGZu1M7nk+/w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.2.3.tgz", - "integrity": "sha512-PqVN5AqhuDqfx3ejaerwrC8codpV9jnyKV+IOel027qsJ1anFUJLdjUlY8VVys0xgd8lmqveX11OkcaQj/otTg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.2.3.tgz", - "integrity": "sha512-O3e2CbN4yTsRguWYNnRd0p5YQ0H3fb7KpcR0W4R319q/gq5B1pJ7eqNbiO3b8g2AuiEcRTiUz5jeGT9j69cxOQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.2.3.tgz", - "integrity": "sha512-hbLBjXVp+96psMe7/7uqyrquGiULXANrq6REVxxPK/I5VzebZ7LHmSfykmByUbLyR1u+K6CTBKgvdQsK2L+2Xw==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-x64-musl": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.2.3.tgz", - "integrity": "sha512-vIiEvOwrJfHZMaTmqUCrFTIwMYL0+PD3Rvy7kFDQgERyx3zhaw8CPa01MCCqa+/sj344BGrXKZ6ti37SgNLMhw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" + "bin": { + "astro-check": "bin/astro-check.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" } }, - "node_modules/@astrojs/compiler-binding-wasm32-wasi": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.2.3.tgz", - "integrity": "sha512-p9S2X8z/mUR2SMzAVJRFMCt8YaalKR+pjl2DgpdjzCQc6ww4bo8kiy54tgKqxZeNF5c+/2tCDTQIxVSm9V1FsA==", - "cpu": [ - "wasm32" - ], + "node_modules/@astrojs/check/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "license": "MIT", - "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.6" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=14.0.0" + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.2.3.tgz", - "integrity": "sha512-vcCG6JttIb5vbSmcxO2O398hpVj7lQ349iS7cjgYP6ZuLVEnw+9qPAr2MM2kJkU5wEGZqJ2gyi/M7UJoPwH1iQ==", - "cpu": [ - "arm64" - ], + "node_modules/@astrojs/check/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.2.3.tgz", - "integrity": "sha512-hKssjNvC36e00Inb1GW1JsVyCFSCGnIjKem4S8q0VIW6cpWAUpvYB4qQU2HIDGD6SDX0ork4F5sWkNWkp2hrGQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "node_modules/@astrojs/compiler": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-3.0.1.tgz", + "integrity": "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA==", + "license": "MIT" }, - "node_modules/@astrojs/compiler-rs": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.2.3.tgz", - "integrity": "sha512-JRAtRcPxS4JeAZEIQFQ6GecBs/Wyp4m6/E8vBNxSgVfo1AtRVLUqRCl5oCGOZ0X/BSBB3Vef/7IlzyiGKi2ORA==", + "node_modules/@astrojs/internal-helpers": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.9.0.tgz", + "integrity": "sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg==", "license": "MIT", "dependencies": { - "@astrojs/compiler-binding": "0.2.3" + "picomatch": "^4.0.4" } }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.0.tgz", - "integrity": "sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.4", - "@types/mdast": "^4.0.4", - "js-yaml": "^4.1.1", - "picomatch": "^4.0.4", - "retext-smartypants": "^6.2.0", - "shiki": "^4.0.2", - "smol-toml": "^1.6.0", - "unified": "^11.0.5" + "node_modules/@astrojs/language-server": { + "version": "2.16.10", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.10.tgz", + "integrity": "sha512-87VQ/5GSdHlRnUA+hGuerYyIGAj+9RbZmATyuKLEUePinUXhQ5YkRnRrHhOD9sSi5JOErLjrLkHnfZFEvGrV8w==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.13.1", + "@astrojs/yaml2ts": "^0.2.4", + "@jridgewell/sourcemap-codec": "^1.5.5", + "@volar/kit": "~2.4.28", + "@volar/language-core": "~2.4.28", + "@volar/language-server": "~2.4.28", + "@volar/language-service": "~2.4.28", + "muggle-string": "^0.4.1", + "tinyglobby": "^0.2.16", + "volar-service-css": "0.0.70", + "volar-service-emmet": "0.0.70", + "volar-service-html": "0.0.70", + "volar-service-prettier": "0.0.70", + "volar-service-typescript": "0.0.70", + "volar-service-typescript-twoslash-queries": "0.0.70", + "volar-service-yaml": "0.0.70", + "vscode-html-languageservice": "^5.6.2", + "vscode-uri": "^3.1.0" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } } }, + "node_modules/@astrojs/language-server/node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "license": "MIT" + }, "node_modules/@astrojs/markdown-remark": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.2.0.tgz", - "integrity": "sha512-+YxmVQu1Bd+MFfSzjq1rOJvD9+nIOJzz5YIIhdIH01RrxRkKbyKoEgyIqP3yv51MhzMDgd79QaPv+kCVPT8vHw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.1.1.tgz", + "integrity": "sha512-C6e9BnLGlbdv6bV8MYGeHpHxsUHrCrB4OuRLqi5LI7oiBVcBcqfUN06zpwFQdHgV48QCCrMmLpyqBr7VqC+swA==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.10.0", - "@astrojs/prism": "4.0.2", + "@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", @@ -239,6 +149,9 @@ "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.0", + "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", @@ -246,26 +159,13 @@ "vfile": "^6.0.3" } }, - "node_modules/@astrojs/markdown-satteri": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.2.tgz", - "integrity": "sha512-feXuUPy41gVfeM7EHT1ciUim8ozGr+YHXab9uUBc1Hk8y60DQosO8ldL+AoPXnCAoGj1OChwHfvXmmJ6XVnY9A==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.10.0", - "@astrojs/prism": "4.0.2", - "github-slugger": "^2.0.0", - "satteri": "^0.9.1" - } - }, "node_modules/@astrojs/mdx": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-7.0.0.tgz", - "integrity": "sha512-LKwNA8nnLtEM0auoP6OfH/UnlKe1Ub59qZjbcYkZjPBGw6PkJewWkA/1qwLpECvV6gMDd6TR6eqV9p/VYZrcrQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-5.0.4.tgz", + "integrity": "sha512-tSbuuYueNODiFAFaME7pjHY5lOLoxBYJi1cKd6scw9+a4ZO7C7UGdafEoVAQvOV2eO8a6RaHSAJYGVPL1w8BPA==", "license": "MIT", "dependencies": { - "@astrojs/internal-helpers": "0.10.0", - "@astrojs/markdown-remark": "7.2.0", + "@astrojs/markdown-remark": "7.1.1", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", @@ -283,19 +183,13 @@ "node": ">=22.12.0" }, "peerDependencies": { - "@astrojs/markdown-satteri": "^0.3.1-alpha.0", - "astro": "^7.0.0-alpha.0" - }, - "peerDependenciesMeta": { - "@astrojs/markdown-satteri": { - "optional": true - } + "astro": "^6.0.0" } }, "node_modules/@astrojs/prism": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", - "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.1.tgz", + "integrity": "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ==", "license": "MIT", "dependencies": { "prismjs": "^1.30.0" @@ -316,58 +210,52 @@ } }, "node_modules/@astrojs/starlight": { - "version": "0.41.1", - "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.41.1.tgz", - "integrity": "sha512-avf2OmrVg6GdVU18juebjjIIuLa+uS3syHuJ/3yDaEFP/8it+YvcxRrYDSf7K6rC4v770UxIddba2hAqQyTeYA==", + "version": "0.38.4", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.38.4.tgz", + "integrity": "sha512-TGFIr2aVC+gcZCPQzJOO4ZnA/yL3jRnsUDcKlVdEhxhxaOQnWr9lZ9MRScg9zU6uh3HVeZAmmjkLCdTlHdcaZA==", "license": "MIT", "dependencies": { - "@astrojs/markdown-satteri": "^0.3.2", - "@astrojs/mdx": "^7.0.0", - "@astrojs/sitemap": "^3.7.2", + "@astrojs/markdown-remark": "^7.0.0", + "@astrojs/mdx": "^5.0.0", + "@astrojs/sitemap": "^3.7.1", "@pagefind/default-ui": "^1.3.0", "@types/hast": "^3.0.4", "@types/js-yaml": "^4.0.9", "@types/mdast": "^4.0.4", - "astro-expressive-code": "^0.44.0", + "astro-expressive-code": "^0.41.6", "bcp-47": "^2.1.0", - "hast-util-from-html": "^2.0.3", - "hast-util-select": "^6.0.4", - "hast-util-to-string": "^3.0.1", - "hastscript": "^9.0.1", - "i18next": "^26.0.7", - "js-yaml": "^4.1.1", + "hast-util-from-html": "^2.0.1", + "hast-util-select": "^6.0.2", + "hast-util-to-string": "^3.0.0", + "hastscript": "^9.0.0", + "i18next": "^23.11.5", + "js-yaml": "^4.1.0", "klona": "^2.0.6", - "magic-string": "^0.30.21", - "mdast-util-directive": "^3.1.0", - "mdast-util-to-markdown": "^2.1.2", + "magic-string": "^0.30.17", + "mdast-util-directive": "^3.0.0", + "mdast-util-to-markdown": "^2.1.0", "mdast-util-to-string": "^4.0.0", - "pagefind": "^1.5.2", - "rehype": "^13.0.2", - "rehype-format": "^5.0.1", - "remark-directive": "^4.0.0", - "satteri": "^0.9.1", + "pagefind": "^1.3.0", + "rehype": "^13.0.1", + "rehype-format": "^5.0.0", + "remark-directive": "^3.0.0", "ultrahtml": "^1.6.0", "unified": "^11.0.5", - "unist-util-visit": "^5.1.0", - "vfile": "^6.0.3" + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.2" }, "peerDependencies": { - "@astrojs/markdown-remark": "^7.2.0", - "astro": "^7.0.2" - }, - "peerDependenciesMeta": { - "@astrojs/markdown-remark": { - "optional": true - } + "astro": "^6.0.0" } }, "node_modules/@astrojs/telemetry": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.2.tgz", - "integrity": "sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.1.tgz", + "integrity": "sha512-7fcIxXS9J4ls5tr8b3ww9rbAIz2+HrhNJYZdkAhhB4za/I5IZ/60g+Bs8q7zwG0tOIZfNB4JWhVJ1Qkl/OrNCw==", "license": "MIT", "dependencies": { "ci-info": "^4.4.0", + "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^4.0.0", "is-wsl": "^3.1.1", @@ -377,31 +265,40 @@ "node": "18.20.8 || ^20.3.0 || >=22.0.0" } }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", + "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==", + "license": "MIT", + "dependencies": { + "yaml": "^2.8.3" + } + }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -410,182 +307,60 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@bruits/satteri-darwin-arm64": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.4.tgz", - "integrity": "sha512-W3MSUkr2mZRR8Stoe+lqNAyzQzRuFMU8WffV9IvFSxTok0LGWR0ZZQPLELU4QTRiUbhL2Y4VUP9vV7pj8rHjgg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@bruits/satteri-darwin-x64": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.4.tgz", - "integrity": "sha512-DXOuuaE1lsv7mpk2mOvGrzqoEWEvOIZEO/fXVa7zfM23Iob+CBjBkRAMwpHA4pmZ3j6Gj7WJzPKw0kQ7w741AQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@bruits/satteri-linux-arm64-gnu": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.4.tgz", - "integrity": "sha512-gJxU9rGGoqIznSEgEzpjxkry24jeHuMpoo1tCIAhHYh7WaD3j5F8zt3jmHxEaN1Uwa+K5+wFgIR2uIGOnMzEmw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-linux-arm64-musl": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.4.tgz", - "integrity": "sha512-Wjzu9hmmAbfmDkBfPI1VdZygJtYz9uYZQnkEyrXi6S2JFi+2pXQ1A5irj38bqm0IZmWcTbk0cVG4NZnPdtVNJA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-linux-x64-gnu": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.4.tgz", - "integrity": "sha512-MR1Q+wMx65FQlbSV7cRqWW87Knp0zkoaIV55Dt+xZl028wJABXEPEEmG3670SLq7lVZvcGIDwCgSg2kCYxvRwA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-linux-x64-musl": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.4.tgz", - "integrity": "sha512-T4gxhXve3zyNAZesrXAd/rDZOGRkbfFIUFld4TGsw6BsjoIteCcDji6IMqeXyaWEVSykY2X8Eid2hr6aXGYAaw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-wasm32-wasi": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.4.tgz", - "integrity": "sha512-/CEG8LUlpaBEnhFnYVn0UnlHFLs51UhrkJBUPDUXLzkadzAcnR88iRA/nOl7Zwhjb4WhfBV4p3P5qeOJMtH0iA==", - "cpu": [ - "wasm32" - ], - "optional": true, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.9.0" } }, - "node_modules/@bruits/satteri-win32-arm64-msvc": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.4.tgz", - "integrity": "sha512-E1ZPQbgCtFKiU7pFYVndynvY7ne4coeVDUgnVThErSFlJ2ceQCBZrfRTD1lzrIDy63Bbqo+g/cZY9duw+JYjIw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@bruits/satteri-win32-x64-msvc": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.4.tgz", - "integrity": "sha512-5I7SiarsNdAUuhJb50CXJPTwr/ECVrBoU+fymoLjChK5fW//+srhY4lstcNTzgFRtQSYfVtm4OQZz16CVMeTeA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@capsizecss/unpack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", - "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz", + "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==", "license": "MIT", "dependencies": { - "fontkitten": "^1.0.3" + "fontkitten": "^1.0.0" }, "engines": { "node": ">=18" } }, "node_modules/@clack/core": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.2.tgz", - "integrity": "sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.2.0.tgz", + "integrity": "sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg==", "license": "MIT", "dependencies": { - "fast-wrap-ansi": "^0.2.0", + "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" } }, "node_modules/@clack/prompts": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.6.0.tgz", - "integrity": "sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.2.0.tgz", + "integrity": "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w==", "license": "MIT", "dependencies": { - "@clack/core": "1.4.2", - "fast-string-width": "^3.0.2", - "fast-wrap-ansi": "^0.2.0", + "@clack/core": "1.2.0", + "fast-string-width": "^1.1.0", + "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" } }, "node_modules/@ctrl/tinycolor": { @@ -597,31 +372,65 @@ "node": ">=14" } }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" + "@emmetio/scanner": "^1.0.4" } }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "@emmetio/scanner": "^1.0.4" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "node_modules/@emmetio/css-parser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", + "license": "MIT" + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "license": "MIT", "optional": true, "dependencies": { @@ -629,9 +438,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", "cpu": [ "ppc64" ], @@ -645,9 +454,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", "cpu": [ "arm" ], @@ -661,9 +470,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", "cpu": [ "arm64" ], @@ -677,9 +486,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", "cpu": [ "x64" ], @@ -693,9 +502,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", "cpu": [ "arm64" ], @@ -709,9 +518,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", "cpu": [ "x64" ], @@ -725,9 +534,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", "cpu": [ "arm64" ], @@ -741,9 +550,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", "cpu": [ "x64" ], @@ -757,9 +566,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", "cpu": [ "arm" ], @@ -773,9 +582,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", "cpu": [ "arm64" ], @@ -789,9 +598,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", "cpu": [ "ia32" ], @@ -805,9 +614,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", "cpu": [ "loong64" ], @@ -821,9 +630,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", "cpu": [ "mips64el" ], @@ -837,9 +646,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", "cpu": [ "ppc64" ], @@ -853,9 +662,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", "cpu": [ "riscv64" ], @@ -869,9 +678,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", "cpu": [ "s390x" ], @@ -885,9 +694,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", "cpu": [ "x64" ], @@ -901,9 +710,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", "cpu": [ "arm64" ], @@ -917,9 +726,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", "cpu": [ "x64" ], @@ -933,9 +742,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", "cpu": [ "arm64" ], @@ -949,9 +758,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", "cpu": [ "x64" ], @@ -965,9 +774,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", "cpu": [ "arm64" ], @@ -981,9 +790,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", "cpu": [ "x64" ], @@ -997,9 +806,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", "cpu": [ "arm64" ], @@ -1013,9 +822,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", "cpu": [ "ia32" ], @@ -1029,9 +838,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", "cpu": [ "x64" ], @@ -1045,9 +854,9 @@ } }, "node_modules/@expressive-code/core": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.44.0.tgz", - "integrity": "sha512-xgiF2P6tYUbrhi3+x0S8xHZWT1t3Bvb3U91tAtRbLb9HLejLvYc5GZUqKICKLaUN4iSGhhNJu2fM/aH8e5yCMg==", + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz", + "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==", "license": "MIT", "dependencies": { "@ctrl/tinycolor": "^4.0.4", @@ -1062,31 +871,108 @@ } }, "node_modules/@expressive-code/plugin-frames": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.44.0.tgz", - "integrity": "sha512-V6M6+zVc1GzqCvXkQHc2m5rcFOIVzJgMq5gnfrMnVf2gwtj/sg4H93c1f/mGeqHycubwkHFUDyParAOiGeDZeA==", + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz", + "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.44.0" + "@expressive-code/core": "^0.41.7" } }, "node_modules/@expressive-code/plugin-shiki": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.44.0.tgz", - "integrity": "sha512-RZsdaqlbGqyAQKuoX4myQXxjmiE2l5KBpJ/gKPh62tCdIdpWyjbzVqSo8+5XsezZxkfi8AJ/J6EUaBTPROFX/Q==", + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz", + "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "shiki": "^3.2.2" + } + }, + "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", + "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/engine-javascript": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", + "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + } + }, + "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.44.0", - "shiki": "^4.0.2" + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@expressive-code/plugin-shiki/node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@expressive-code/plugin-shiki/node_modules/shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", + "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/engine-javascript": "3.23.0", + "@shikijs/engine-oniguruma": "3.23.0", + "@shikijs/langs": "3.23.0", + "@shikijs/themes": "3.23.0", + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" } }, "node_modules/@expressive-code/plugin-text-markers": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.44.0.tgz", - "integrity": "sha512-0/m3A5b+lz2upyNq+wzZ1S69HRoJmyFs5LsR42lVZ9pmGRlBiSBYQpvqlji4DBj1+Riamxc0AvcCr5kuzOQeWA==", + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz", + "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.44.0" + "@expressive-code/core": "^0.41.7" } }, "node_modules/@img/colour": { @@ -1182,9 +1068,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1201,9 +1084,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1220,9 +1100,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1239,9 +1116,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1258,9 +1132,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1277,9 +1148,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1296,9 +1164,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1315,9 +1180,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1334,9 +1196,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1359,9 +1218,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1384,9 +1240,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1409,9 +1262,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1434,9 +1284,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1459,9 +1306,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1484,9 +1328,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1509,9 +1350,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1646,39 +1484,12 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "node_modules/@oslojs/encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", "license": "MIT" }, - "node_modules/@oxc-project/types": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", - "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/@pagefind/darwin-arm64": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", @@ -1776,10 +1587,51 @@ "win32" ] }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", - "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", "cpu": [ "arm64" ], @@ -1787,193 +1639,246 @@ "optional": true, "os": [ "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", - "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "linux" + ] }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", - "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", "cpu": [ - "x64" + "loong64" ], "license": "MIT", "optional": true, "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "linux" + ] }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", - "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", "cpu": [ - "x64" + "loong64" ], "license": "MIT", "optional": true, "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "linux" + ] }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", - "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", "cpu": [ - "arm" + "ppc64" ], "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", - "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", "cpu": [ - "arm64" - ], - "libc": [ - "glibc" + "ppc64" ], "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", - "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", "cpu": [ - "arm64" - ], - "libc": [ - "musl" + "riscv64" ], "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", - "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" + "riscv64" ], "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", - "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", - "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", - "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", - "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", "cpu": [ "arm64" ], @@ -1981,49 +1886,38 @@ "optional": true, "os": [ "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", - "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", "cpu": [ - "wasm32" + "arm64" ], "license": "MIT", "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + "os": [ + "win32" + ] }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", - "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", "cpu": [ - "arm64" + "ia32" ], "license": "MIT", "optional": true, "os": [ "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", - "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", "cpu": [ "x64" ], @@ -2031,44 +1925,20 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "license": "MIT" + ] }, - "node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@shikijs/core": { "version": "4.0.2", @@ -2170,16 +2040,6 @@ "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -2190,9 +2050,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -2229,9 +2089,9 @@ } }, "node_modules/@types/mdx": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz", - "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", "license": "MIT" }, "node_modules/@types/ms": { @@ -2279,10 +2139,100 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, + "node_modules/@volar/kit": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz", + "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==", + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz", + "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "path-browserify": "^1.0.1", + "request-light": "^0.7.0", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz", + "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "license": "MIT" + }, "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2300,16 +2250,58 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/am-i-vibing": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", - "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { - "process-ancestry": "^0.1.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "bin": { - "am-i-vibing": "dist/cli.mjs" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { @@ -2378,39 +2370,36 @@ } }, "node_modules/astro": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/astro/-/astro-7.0.3.tgz", - "integrity": "sha512-CK+G+Tl2DMV1EXCwVG45vyurxf2IfRTklMxDhRKn+tst9Yl8rWXpudL62Fa6zin5Bt968FBvuyASj1aJShROZg==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/astro/-/astro-6.1.9.tgz", + "integrity": "sha512-NsAHzMzpznB281g2aM5qnBt2QjfH6ttKiZ3hSZw52If8JJ+62kbnBKbyKhR2glQcJLl7Jfe4GSl0DihFZ36rRQ==", "license": "MIT", "dependencies": { - "@astrojs/compiler-rs": "^0.2.2", - "@astrojs/internal-helpers": "0.10.0", - "@astrojs/markdown-satteri": "0.3.2", - "@astrojs/telemetry": "3.3.2", + "@astrojs/compiler": "^3.0.1", + "@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.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", - "am-i-vibing": "^0.4.0", "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.1", + "devalue": "^5.6.3", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", - "esbuild": "^0.28.0", + "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", - "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", - "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", @@ -2429,12 +2418,13 @@ "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", + "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": "^8.0.13", + "vite": "^7.3.2", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", @@ -2454,27 +2444,18 @@ }, "optionalDependencies": { "sharp": "^0.34.0" - }, - "peerDependencies": { - "@astrojs/markdown-remark": "7.2.0" - }, - "peerDependenciesMeta": { - "@astrojs/markdown-remark": { - "optional": true - } } }, "node_modules/astro-expressive-code": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.44.0.tgz", - "integrity": "sha512-b1wN/ZvbJprzxlGKIpIes2kQrCY5KRLwys2tWbZAZyjGZcW5ZtgneZnBwzNRiBna9/48d4mQl19KLjcRuhO1hw==", + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz", + "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==", "license": "MIT", "dependencies": { - "rehype-expressive-code": "^0.44.0", - "url-extras": "^0.1.0" + "rehype-expressive-code": "^0.41.7" }, "peerDependencies": { - "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta || ^7.0.0" + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" } }, "node_modules/axobject-query": { @@ -2616,6 +2597,20 @@ "node": ">=8" } }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2635,6 +2630,24 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -2855,14 +2868,15 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", + "optional": true, "engines": { "node": ">=8" } }, "node_modules/devalue": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", - "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", + "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", "license": "MIT" }, "node_modules/devlop": { @@ -2900,6 +2914,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -2976,6 +2996,28 @@ "node": ">=4" } }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -2989,9 +3031,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz", - "integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "license": "MIT" }, "node_modules/esast-util-from-estree": { @@ -3027,9 +3069,9 @@ } }, "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -3039,32 +3081,41 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/escape-string-regexp": { @@ -3190,15 +3241,15 @@ "license": "MIT" }, "node_modules/expressive-code": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.44.0.tgz", - "integrity": "sha512-JXVWVNCKlLuZLMQH8cOiDUSosT0Bb+elwE/dbAkpwFwDFmyFyWlECoWZIohh2FkIF1iI67TQJ+Ts9k7oNDh2qA==", + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz", + "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.44.0", - "@expressive-code/plugin-frames": "^0.44.0", - "@expressive-code/plugin-shiki": "^0.44.0", - "@expressive-code/plugin-text-markers": "^0.44.0" + "@expressive-code/core": "^0.41.7", + "@expressive-code/plugin-frames": "^0.41.7", + "@expressive-code/plugin-shiki": "^0.41.7", + "@expressive-code/plugin-text-markers": "^0.41.7" } }, "node_modules/extend": { @@ -3219,28 +3270,50 @@ "node": ">=0.10.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-1.2.1.tgz", + "integrity": "sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow==", "license": "MIT" }, "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-1.1.0.tgz", + "integrity": "sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ==", "license": "MIT", "dependencies": { - "fast-string-truncated-width": "^3.0.2" + "fast-string-truncated-width": "^1.2.0" } }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fast-wrap-ansi": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", - "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.1.6.tgz", + "integrity": "sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w==", "license": "MIT", "dependencies": { - "fast-string-width": "^3.0.2" + "fast-string-width": "^1.1.0" } }, "node_modules/fdir": { @@ -3348,19 +3421,13 @@ "url": "https://github.com/sponsors/krisk" } }, - "node_modules/get-tsconfig": { - "version": "5.0.0-beta.4", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", - "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { - "node": ">=20.20.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "node": "6.* || 8.* || >= 10.*" } }, "node_modules/github-slugger": { @@ -3821,31 +3888,26 @@ "license": "BSD-2-Clause" }, "node_modules/i18next": { - "version": "26.3.3", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.3.tgz", - "integrity": "sha512-aYVegyBdXSO93CMMihvr47jI7GHSOcIahMpJX+qzUXDzW4xDJf2uenIA+45vDU+YhiVdcfsql70AC9RVdMNrHg==", + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", "funding": [ { "type": "individual", - "url": "https://www.locize.com/i18next" + "url": "https://locize.com" }, { "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + "url": "https://locize.com/i18next.html" }, { "type": "individual", - "url": "https://www.locize.com" + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" } ], "license": "MIT", - "peerDependencies": { - "typescript": "^5 || ^6" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "dependencies": { + "@babel/runtime": "^7.23.2" } }, "node_modules/immediate": { @@ -3933,6 +3995,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-hexadecimal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", @@ -4010,19 +4081,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4031,10 +4092,16 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", "license": "MIT" }, "node_modules/jszip": { @@ -4058,6 +4125,15 @@ "node": ">=0.10.0" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/klona": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", @@ -4076,267 +4152,6 @@ "immediate": "~3.0.5" } }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -4348,9 +4163,9 @@ } }, "node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -4366,12 +4181,12 @@ } }, "node_modules/magicast": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", - "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.3", + "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } @@ -4809,9 +4624,9 @@ } }, "node_modules/micromark-extension-directive": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", - "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -5490,10 +5305,16 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "license": "MIT" + }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -5564,17 +5385,14 @@ } }, "node_modules/obug": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", - "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT", - "engines": { - "node": ">=12.20.0" - } + "license": "MIT" }, "node_modules/ofetch": { "version": "1.5.1", @@ -5626,9 +5444,9 @@ } }, "node_modules/p-queue": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.0.tgz", - "integrity": "sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.2.0.tgz", + "integrity": "sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g==", "license": "MIT", "dependencies": { "eventemitter3": "^5.0.4", @@ -5738,6 +5556,12 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/piccolore": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", @@ -5763,9 +5587,9 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "funding": [ { "type": "opencollective", @@ -5782,7 +5606,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5816,9 +5640,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", - "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -5828,6 +5652,21 @@ "node": ">=4" } }, + "node_modules/prettier": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", + "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -5837,15 +5676,6 @@ "node": ">=6" } }, - "node_modules/process-ancestry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", - "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -6004,12 +5834,12 @@ } }, "node_modules/rehype-expressive-code": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.44.0.tgz", - "integrity": "sha512-5r74C5F2sMR3X+QJH8OKWgZBO/cqRw5W1fLT6GVlSfLqepk+4j8tGFkyPqZYWjwntsBHzKPDH2zI68sZ7ScLfA==", + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz", + "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==", "license": "MIT", "dependencies": { - "expressive-code": "^0.44.0" + "expressive-code": "^0.41.7" } }, "node_modules/rehype-format": { @@ -6087,14 +5917,14 @@ } }, "node_modules/remark-directive": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-4.0.0.tgz", - "integrity": "sha512-7sxn4RfF1o3izevPV1DheyGDD6X4c9hrGpfdUpm7uC++dqrnJxIZVkk7CoKqcLm0VUMAuOol7Mno3m6g8cfMuA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", + "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", - "micromark-extension-directive": "^4.0.0", + "micromark-extension-directive": "^3.0.0", "unified": "^11.0.0" }, "funding": { @@ -6197,13 +6027,28 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "engines": { + "node": ">=0.10.0" } }, "node_modules/retext": { @@ -6267,37 +6112,48 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/rolldown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", - "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.137.0", - "@rolldown/pluginutils": "^1.0.0" + "@types/estree": "1.0.8" }, "bin": { - "rolldown": "bin/cli.mjs" + "rollup": "dist/bin/rollup" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.3", - "@rolldown/binding-darwin-arm64": "1.1.3", - "@rolldown/binding-darwin-x64": "1.1.3", - "@rolldown/binding-freebsd-x64": "1.1.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", - "@rolldown/binding-linux-arm64-gnu": "1.1.3", - "@rolldown/binding-linux-arm64-musl": "1.1.3", - "@rolldown/binding-linux-ppc64-gnu": "1.1.3", - "@rolldown/binding-linux-s390x-gnu": "1.1.3", - "@rolldown/binding-linux-x64-gnu": "1.1.3", - "@rolldown/binding-linux-x64-musl": "1.1.3", - "@rolldown/binding-openharmony-arm64": "1.1.3", - "@rolldown/binding-wasm32-wasi": "1.1.3", - "@rolldown/binding-win32-arm64-msvc": "1.1.3", - "@rolldown/binding-win32-x64-msvc": "1.1.3" + "@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.2" } }, "node_modules/safe-buffer": { @@ -6306,28 +6162,6 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, - "node_modules/satteri": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.4.tgz", - "integrity": "sha512-BKob126Tay84diOZsnVNH/Q/c+3njPJTCad3w5zLKa6j8bVjxskPNHDtxrMwYK4bN/RlqUSdMnPwKY4k65EMOQ==", - "dependencies": { - "@types/estree-jsx": "^1.0.5", - "@types/hast": "^3.0.4", - "@types/mdast": "^4.0.4", - "@types/unist": "^3.0.3" - }, - "optionalDependencies": { - "@bruits/satteri-darwin-arm64": "0.9.4", - "@bruits/satteri-darwin-x64": "0.9.4", - "@bruits/satteri-linux-arm64-gnu": "0.9.4", - "@bruits/satteri-linux-arm64-musl": "0.9.4", - "@bruits/satteri-linux-x64-gnu": "0.9.4", - "@bruits/satteri-linux-x64-musl": "0.9.4", - "@bruits/satteri-wasm32-wasi": "0.9.4", - "@bruits/satteri-win32-arm64-msvc": "0.9.4", - "@bruits/satteri-win32-x64-msvc": "0.9.4" - } - }, "node_modules/sax": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", @@ -6351,9 +6185,9 @@ } }, "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6458,9 +6292,9 @@ } }, "node_modules/smol-toml": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", - "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -6518,6 +6352,20 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -6532,6 +6380,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", @@ -6591,27 +6451,27 @@ "license": "MIT" }, "node_modules/tinyclip": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", - "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.12.tgz", + "integrity": "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA==", "license": "MIT", "engines": { "node": "^16.14.0 || >= 17.3.0" } }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -6644,6 +6504,27 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "deprecated": "unmaintained", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6651,10 +6532,38 @@ "license": "0BSD", "optional": true }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, "node_modules/ufo": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", - "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", "license": "MIT" }, "node_modules/ultrahtml": { @@ -6937,18 +6846,6 @@ } } }, - "node_modules/url-extras": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/url-extras/-/url-extras-0.1.0.tgz", - "integrity": "sha512-8tzwTeXFPuX/5PHuCDQE5Dd9Ts4rwoq2t9aIT+HS4iAVpmj5l4Ao7Q+BuuFjvWRqrLswBhQDk8O96ZicgCqQqw==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6998,16 +6895,17 @@ } }, "node_modules/vite": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", - "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "~1.1.2", - "tinyglobby": "^0.2.17" + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -7023,10 +6921,9 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", - "esbuild": "^0.27.0 || ^0.28.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", @@ -7039,18 +6936,15 @@ "@types/node": { "optional": true }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, "jiti": { "optional": true }, "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -7093,6 +6987,274 @@ } } }, + "node_modules/volar-service-css": { + "version": "0.0.70", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.70.tgz", + "integrity": "sha512-K1qyOvBpE3rzdAv3e4/6Rv5yizrYPy5R/ne3IWCAzLBuMO4qBMV3kSqWzj6KUVe6S0AnN6wxF7cRkiaKfYMYJw==", + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.70", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.70.tgz", + "integrity": "sha512-xi5bC4m/VyE3zy/n2CXspKeDZs3qA41tHLTw275/7dNWM/RqE2z3BnDICQybHIVp/6G1iOQj5c1qXMgQC08TNg==", + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.1", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.70", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.70.tgz", + "integrity": "sha512-eR6vCgMdmYAo4n+gcT7DSyBQbwB8S3HZZvSagTf0sxNaD4WppMCFfpqWnkrlGStPKMZvMiejRRVmqsX9dYcTvQ==", + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.70", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.70.tgz", + "integrity": "sha512-Z6BCFSpGVCd8BPAsZ785Kce1BGlWd5ODqmqZGVuB14MJvrR4+CYz6cDy4F+igmE1gMifqfvMhdgT8Aud4M5ngg==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.70", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.70.tgz", + "integrity": "sha512-l46Bx4cokkUedTd74ojO5H/zqHZJ8SUuyZ0IB8JN4jfRqUM3bQFBHoOwlZCyZmOeO0A3RQNkMnFclxO4c++gsg==", + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.70", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.70.tgz", + "integrity": "sha512-IdD13Z9N2Bu8EM6CM0fDV1E69olEYGHDU25X51YXmq8Y0CmJ2LNj6gOiBJgpS5JGUqFzECVhMNBW7R0sPdRTMQ==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.70", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.70.tgz", + "integrity": "sha512-0c8bXDBeoATF9F6iPIlOuYTuZAC4c+yi0siQo920u7eiBJk8oQmUmg9cDUbR4+Gl++bvGP4plj3fErbJuPqdcQ==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.20.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-html-languageservice": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", + "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0.tgz", + "integrity": "sha512-+VvMmQPJhtvJ+8O+zu2JKIRiLxXF8NW7krWgyMGeOHrp4Cn23T5hc0v2LknNeopDOB70wghHAds7mKtcZ0I4Sg==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.1.tgz", + "integrity": "sha512-RTiiVHdpxpYcJVI5sq6S5TLjQ4WDR/rBrIWru+kPXe6sGQ9PFQ3GamrTKLvPqbR4ylr1SoodhmcqbFII0WXVuw==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.0", + "vscode-languageserver-types": "3.18.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz", + "integrity": "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==", + "license": "MIT" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -7112,12 +7274,111 @@ "node": ">=4" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/xxhash-wasm": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", "license": "MIT" }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-language-server": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.20.0.tgz", + "integrity": "sha512-qhjK/bzSRZ6HtTvgeFvjNPJGWdZ0+x5NREV/9XZWFjIGezew2b4r5JPy66IfOhd5OA7KeFwk1JfmEbnTvev0cA==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "prettier": "^3.5.0", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.2", + "yaml": "2.7.1" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/yargs-parser": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", @@ -7127,6 +7388,15 @@ "node": "^20.19.0 || ^22.12.0 || >=23" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", diff --git a/website/package.json b/website/package.json index e4e5a18d3..78e54a9d5 100644 --- a/website/package.json +++ b/website/package.json @@ -18,6 +18,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { + "@astrojs/check": "^0.9.9", "@astrojs/sitemap": "^3.7.2", "@astrojs/starlight": "^0.41.1", "astro": "^7.0.3", @@ -26,6 +27,7 @@ "gray-matter": "^4.0.3", "jszip": "^3.10.1", "marked": "^18.0.2", - "shiki": "^4.0.2" + "shiki": "^4.0.2", + "typescript": "^5.9.3" } } diff --git a/website/src/components/BackToTop.astro b/website/src/components/BackToTop.astro index d65182838..38b3aef2f 100644 --- a/website/src/components/BackToTop.astro +++ b/website/src/components/BackToTop.astro @@ -5,10 +5,11 @@ @@ -35,9 +36,10 @@ // Scroll to top with smooth behavior function scrollToTop() { + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; window.scrollTo({ top: 0, - behavior: 'smooth' + behavior: prefersReducedMotion ? 'auto' : 'smooth' }); } @@ -79,7 +81,12 @@ opacity: 0; visibility: hidden; transform: translateY(20px) scale(0.8); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + transition: + background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), + transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), + visibility 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 4px 12px rgba(133, 52, 243, 0.4); z-index: 9999; } @@ -101,8 +108,8 @@ } .back-to-top:focus-visible { - outline: 2px solid var(--color-text-emphasis); - outline-offset: 2px; + outline: 3px solid var(--color-text-emphasis); + outline-offset: 4px; } /* Mobile adjustments */ diff --git a/website/src/components/Head.astro b/website/src/components/Head.astro index 6fb4a0c1f..77062220a 100644 --- a/website/src/components/Head.astro +++ b/website/src/components/Head.astro @@ -1,4 +1,5 @@ --- +import starlightOverrides from '../styles/starlight-overrides.css?raw'; import Default from '@astrojs/starlight/components/Head.astro'; const { head, entry } = Astro.locals.starlightRoute; const basePath = import.meta.env.BASE_URL; @@ -36,6 +37,7 @@ const twitterDomain = : undefined; --- + {twitterTitle && } {description && } diff --git a/website/src/components/Modal.astro b/website/src/components/Modal.astro index 82b2e3955..b7880c886 100644 --- a/website/src/components/Modal.astro +++ b/website/src/components/Modal.astro @@ -172,8 +172,6 @@ Install @@ -183,6 +181,7 @@ aria-label="Install options" aria-expanded="false" aria-haspopup="true" + aria-controls="install-dropdown-menu" > - + diff --git a/website/src/components/ThemeToggle.astro b/website/src/components/ThemeToggle.astro index 55077f3ad..5fa3eb4de 100644 --- a/website/src/components/ThemeToggle.astro +++ b/website/src/components/ThemeToggle.astro @@ -6,6 +6,7 @@
@@ -135,7 +121,8 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; type ActiveFilters, } from '../scripts/filters'; import { initModalFromHash } from '../scripts/modal-client'; - import { getQueryParam } from '../scripts/url-state'; + import { getQueryParam, updateQueryParams } from '../scripts/url-state'; + import { toggleFlag } from '../scripts/filters'; import type { Accent, ResultItem } from '../lib/types'; import type { AgentItem } from '../lib/upstream-types'; @@ -147,7 +134,7 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; items: AgentItem[]; } - type AgentClientItem = Pick & { + type AgentClientItem = Pick & { filterModel: string; filterTools: string[]; filterHandoffs: boolean; @@ -185,7 +172,7 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; function toClientItem(item: AgentItem): AgentClientItem { const model = getModel(item); return { - slug: item.id, + id: item.id, title: item.title, label: buildLabel(item), description: item.description, @@ -235,7 +222,7 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; function resolver(item: AgentClientItem): Record { return { model: [item.filterModel], - tool: item.filterTools, + hasMcpServers: item.filterTools.length > 0 ? ['1'] : [], handoffs: item.filterHandoffs ? ['1'] : [], }; } @@ -248,12 +235,11 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; .map((item) => { const accent = escapeHtml(item.accent ?? 'purple'); const path = escapeHtml(item.detail ?? ''); - const slug = item.slug ?? ''; + const id = item.id ?? ''; return ` - + ${escapeHtml(item.label)} - ${escapeHtml(item.title)} @@ -261,7 +247,7 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; ${item.tags.slice(0, 3).map(tag => `${escapeHtml(tag)}`).join('')} - Open agent details + Open details `; }) @@ -290,11 +276,16 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; } const models = [...new Set(items.map(a => a.filterModel))].sort(); - const tools = [...new Set(items.flatMap(a => a.filterTools))].sort(); + + const modelSelect = document.getElementById('filter-model') as HTMLSelectElement | null; + if (modelSelect) { + modelSelect.innerHTML = '' + + models.map(m => ``).join(''); + } const filterConfigs: FilterConfig[] = [ { param: 'model', label: 'Model', type: 'multi', options: models.map(m => ({ value: m, label: m })) }, - { param: 'tool', label: 'Tool', type: 'multi', options: tools.map(t => ({ value: t, label: t })) }, + { param: 'hasMcpServers', label: 'MCP Servers', type: 'flag', options: [] }, { param: 'handoffs', label: 'Has Handoffs', type: 'flag', options: [] }, { param: 'sort', label: 'Sort', type: 'single', options: [ { value: 'title', label: 'Name (A-Z)' }, @@ -302,6 +293,39 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; ]}, ]; + // Initialize custom dropdowns for single-select controls + const { initCustomDropdowns } = await import('../scripts/custom-dropdown'); + initCustomDropdowns(); + + // Wire event listeners for selects and toggles + const sortSelect = document.getElementById('sort-select') as HTMLSelectElement | null; + const mcpToggle = document.querySelector('[data-filter-param="hasMcpServers"]'); + const handoffsToggle = document.querySelector('[data-filter-param="handoffs"]'); + + modelSelect?.addEventListener('change', () => { + const selected = Array.from(modelSelect.selectedOptions).map(o => o.value).filter(Boolean); + updateQueryParams({ setAll: { model: selected } }); + applyAndRender(); + }); + + sortSelect?.addEventListener('change', () => { + const value = sortSelect.value; + updateQueryParams({ set: { sort: value || null } }); + applyAndRender(); + }); + + mcpToggle?.addEventListener('click', () => { + const active = mcpToggle.getAttribute('aria-pressed') === 'true'; + toggleFlag('hasMcpServers', active); + applyAndRender(); + }); + + handoffsToggle?.addEventListener('click', () => { + const active = handoffsToggle.getAttribute('aria-pressed') === 'true'; + toggleFlag('handoffs', active); + applyAndRender(); + }); + const engine = createSearch(items); let currentItems: AgentClientItem[] = []; let visibleCount = INITIAL_RENDER_COUNT; @@ -358,26 +382,30 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; function updateFilterDisplays(filters: ActiveFilters): void { const modelDisplay = document.querySelector('.filter-display[data-param="model"]'); - const toolDisplay = document.querySelector('.filter-display[data-param="tool"]'); const sortDisplay = document.querySelector('.filter-display[data-param="sort"]'); - const toggleBtn = document.querySelector('[data-filter-param="handoffs"]'); + const mcpToggleBtn = document.querySelector('[data-filter-param="hasMcpServers"]'); + const handoffsToggleBtn = document.querySelector('[data-filter-param="handoffs"]'); if (modelDisplay) { const vals = filters.multi['model'] ?? []; modelDisplay.textContent = vals.length > 0 ? `model: ${vals.join(', ')}` : 'model: all'; } - if (toolDisplay) { - const vals = filters.multi['tool'] ?? []; - toolDisplay.textContent = vals.length > 0 ? `tool: ${vals.join(', ')}` : 'tool: all'; - } if (sortDisplay) { const val = filters.single['sort'] || 'lastUpdated'; const label = val === 'title' ? 'Name (A-Z)' : 'Recently Updated'; sortDisplay.textContent = `sort: ${label}`; } - if (toggleBtn) { + if (mcpToggleBtn) { + const active = filters.flags['hasMcpServers'] === true; + mcpToggleBtn.setAttribute('aria-pressed', String(active)); + if (active) mcpToggleBtn.classList.add('console-toggle--active'); + else mcpToggleBtn.classList.remove('console-toggle--active'); + } + if (handoffsToggleBtn) { const active = filters.flags['handoffs'] === true; - toggleBtn.setAttribute('aria-pressed', String(active)); + handoffsToggleBtn.setAttribute('aria-pressed', String(active)); + if (active) handoffsToggleBtn.classList.add('console-toggle--active'); + else handoffsToggleBtn.classList.remove('console-toggle--active'); } } @@ -390,35 +418,6 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; observer.observe(loadSentinel); } - document.querySelectorAll('.console-select-control[data-filter-param]').forEach((el) => { - const param = el.dataset.filterParam!; - el.addEventListener('click', () => { - const config = filterConfigs.find(c => c.param === param); - if (!config || !config.options.length) return; - - if (config.type === 'single') { - const current = getQueryParam(param) || ''; - const idx = config.options.findIndex(o => o.value === current); - const next = config.options[(idx + 1) % config.options.length]; - const url = new URL(window.location.href); - url.searchParams.set(param, next.value); - history.replaceState(null, '', url.toString()); - applyAndRender(); - } - /* multi-mode toggling: cycle through options via a prompt or dropdown */ - }); - }); - - const handoffsToggle = document.querySelector('[data-filter-param="handoffs"]'); - handoffsToggle?.addEventListener('click', () => { - const url = new URL(window.location.href); - const isActive = url.searchParams.get('handoffs') === '1'; - if (isActive) url.searchParams.delete('handoffs'); - else url.searchParams.set('handoffs', '1'); - history.replaceState(null, '', url.toString()); - applyAndRender(); - }); - function doClear(): void { clearAllFilters(filterConfigs); applyAndRender(); @@ -539,8 +538,9 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; .filter-clear { display: inline-flex; align-items: center; + justify-content: center; gap: 0.5rem; - min-height: 2.5rem; + min-height: 2.75rem; border: 2px solid var(--color-green); border-radius: 0; background: transparent; @@ -554,4 +554,5 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length; transition: background var(--motion-fast); } .filter-clear:hover { background: var(--color-green); color: var(--color-black); } + .filter-clear:focus-visible { outline: 3px solid var(--color-primary); outline-offset: 3px; } diff --git a/website/src/pages/hook.astro b/website/src/pages/hook.astro deleted file mode 100644 index cb5b6b670..000000000 --- a/website/src/pages/hook.astro +++ /dev/null @@ -1,15 +0,0 @@ ---- -import BaseLayout from '../layouts/BaseLayout.astro'; -import ResourceDetailPage from '../components/ResourceDetailPage.astro'; ---- - - - - - - diff --git a/website/src/pages/hooks.astro b/website/src/pages/hooks.astro index 750b8ebe4..e2940462f 100644 --- a/website/src/pages/hooks.astro +++ b/website/src/pages/hooks.astro @@ -38,15 +38,25 @@ const tags = [...new Set(rawItems.flatMap((item) => item.tags))].sort(); -
@@ -113,7 +106,7 @@ const serializedInstructionsData = JSON.stringify(instructionsData).replace(/ { const accent = escapeHtml(item.accent ?? 'purple'); const path = escapeHtml(item.detail ?? ''); - const slug = item.slug ?? ''; + const id = item.id ?? ''; return ` - + ${escapeHtml(item.label)} - ${escapeHtml(item.title)} @@ -255,7 +247,7 @@ const serializedInstructionsData = JSON.stringify(instructionsData).replace(/ ${item.tags.slice(0, 3).map(tag => `${escapeHtml(tag)}`).join('')} - Open instruction details + Open details `; }) @@ -286,6 +278,12 @@ const serializedInstructionsData = JSON.stringify(instructionsData).replace(/ i.filterExtension))].sort(); const extOptions = extensions.map(e => ({ value: e, label: `.${e}` })); + const extSelect = document.getElementById('filter-extension') as HTMLSelectElement | null; + if (extSelect) { + extSelect.innerHTML = '' + + extOptions.map(opt => ``).join(''); + } + const filterConfigs: FilterConfig[] = [ { param: 'extension', label: 'Extension', type: 'multi', options: extOptions }, { param: 'sort', label: 'Sort', type: 'single', options: [ @@ -294,6 +292,25 @@ const serializedInstructionsData = JSON.stringify(instructionsData).replace(/ { + const selected = Array.from(extSelect.selectedOptions).map(o => o.value).filter(Boolean); + updateQueryParams({ setAll: { extension: selected } }); + applyAndRender(); + }); + + sortSelect?.addEventListener('change', () => { + const value = sortSelect.value; + updateQueryParams({ set: { sort: value || null } }); + applyAndRender(); + }); + const engine = createSearch(items); let currentItems: InstructionClientItem[] = []; let visibleCount = INITIAL_RENDER_COUNT; @@ -372,24 +389,7 @@ const serializedInstructionsData = JSON.stringify(instructionsData).replace(/('.console-select-control[data-filter-param]').forEach((el) => { - const param = el.dataset.filterParam!; - el.addEventListener('click', () => { - const config = filterConfigs.find(c => c.param === param); - if (!config || !config.options.length) return; - - if (config.type === 'single') { - const current = getQueryParam(param) || ''; - const idx = config.options.findIndex(o => o.value === current); - const next = config.options[(idx + 1) % config.options.length]; - const url = new URL(window.location.href); - url.searchParams.set(param, next.value); - history.replaceState(null, '', url.toString()); - applyAndRender(); - } - }); - }); + function doClear(): void { clearAllFilters(filterConfigs); @@ -510,8 +510,9 @@ const serializedInstructionsData = JSON.stringify(instructionsData).replace(/ - - - - diff --git a/website/src/pages/plugins.astro b/website/src/pages/plugins.astro index 3ab745998..dbcf1368e 100644 --- a/website/src/pages/plugins.astro +++ b/website/src/pages/plugins.astro @@ -3,6 +3,7 @@ import BaseLayout from '../layouts/BaseLayout.astro'; import Container from '../components/ui/Container.astro'; import Card from '../components/ui/Card.astro'; import Icon from '../components/ui/Icon.astro'; +import InlineAlert from '../components/ui/InlineAlert.astro'; import ContributeCTA from '../components/ContributeCTA.astro'; import BackToTop from '../components/BackToTop.astro'; import pluginsData from '../../public/data/plugins.json'; @@ -30,10 +31,26 @@ const tags = [...new Set(rawItems.flatMap((item) => item.tags))].sort(); - -

Awesome Copilot is a default plugin marketplace - no setup required. Access it directly from GitHub Copilot CLI or VS Code.

-

/plugin marketplace browse awesome-copilot Β· copilot plugin install <plugin-name>@awesome-copilot

-
+
+
+
+ + No setup required. Access it directly from GitHub Copilot CLI or VS Code. + +
+ +
+
+ > + /plugin marketplace browse awesome-copilot +
+
+ > + copilot plugin install <plugin-name>@awesome-copilot +
+
+
+
- + + +
+ + + +
+
- + -
- + @@ -468,10 +467,7 @@ const previewFiles = [ background: rgb(29 35 38 / 74%); } - .file-tree__folder:focus-visible, - .file-tree__file:focus-visible { - outline: none; - } + .file-tree__folder[aria-expanded='true'] .file-tree__chevron { transform: rotate(90deg); } diff --git a/website/src/pages/skills.astro b/website/src/pages/skills.astro index c56e6a93c..34874e6ba 100644 --- a/website/src/pages/skills.astro +++ b/website/src/pages/skills.astro @@ -10,7 +10,6 @@ import skillsData from '../../public/data/skills.json'; const rawItems = skillsData.items; const assetEnabledCount = rawItems.filter((item) => item.hasAssets).length; const bundledFileCount = rawItems.reduce((total, item) => total + item.files.length, 0); -const categories = [...new Set(rawItems.map((item) => item.category).filter(Boolean))].sort(); --- diff --git a/website/src/scripts/pages/samples.ts b/website/src/scripts/pages/samples.ts index f3ef2475c..60d499a50 100644 --- a/website/src/scripts/pages/samples.ts +++ b/website/src/scripts/pages/samples.ts @@ -5,7 +5,7 @@ import { FuzzySearch, type SearchableItem } from "../search"; import { fetchData, debounce } from "../utils"; import { createChoices, getChoicesValues, type Choices } from "../choices"; -import { setupModal } from "../modal"; +import { setupModal, openFileModal } from "../modal"; import { getRecipeResultsCountText, renderCookbookSectionsHtml, @@ -56,8 +56,8 @@ export async function initSamplesPage(): Promise { ...recipe, title: recipe.name, cookbookId: cookbook.id, - } as SearchableItem & { cookbookId: string }) - ) + }) as SearchableItem & { cookbookId: string }, + ), ); search = new FuzzySearch(allRecipes); @@ -100,7 +100,7 @@ function setupFilters(): void { // Language filter const languageSelect = document.getElementById( - "filter-language" + "filter-language", ) as HTMLSelectElement; if (languageSelect) { // Get unique languages across all cookbooks @@ -137,7 +137,7 @@ function setupFilters(): void { samplesData.filters.tags.map((tag) => ({ value: tag, label: tag })), "value", "label", - true + true, ); tagSelect.addEventListener("change", () => { @@ -157,7 +157,7 @@ function setupFilters(): void { */ function setupSearch(): void { const searchInput = document.getElementById( - "search-input" + "search-input", ) as HTMLInputElement; if (!searchInput) return; @@ -166,7 +166,7 @@ function setupSearch(): void { debounce(() => { renderCookbooks(); updateResultsCount(); - }, 200) + }, 200), ); } @@ -178,7 +178,7 @@ function clearFilters(): void { selectedTags = []; const languageSelect = document.getElementById( - "filter-language" + "filter-language", ) as HTMLSelectElement; if (languageSelect) languageSelect.value = ""; @@ -188,7 +188,7 @@ function clearFilters(): void { } const searchInput = document.getElementById( - "search-input" + "search-input", ) as HTMLInputElement; if (searchInput) searchInput.value = ""; @@ -203,7 +203,7 @@ function getFilteredRecipes(): CookbookRecipeMatch[] { if (!samplesData || !search) return []; const searchInput = document.getElementById( - "search-input" + "search-input", ) as HTMLInputElement; const query = searchInput?.value.trim() || ""; @@ -215,7 +215,7 @@ function getFilteredRecipes(): CookbookRecipeMatch[] { results = searchResults.map((item) => { const recipe = item as SearchableItem & { cookbookId: string }; const cookbook = samplesData!.cookbooks.find( - (c) => c.id === recipe.cookbookId + (c) => c.id === recipe.cookbookId, )!; return { cookbook, @@ -226,21 +226,21 @@ function getFilteredRecipes(): CookbookRecipeMatch[] { } else { // No search query - return all recipes results = samplesData.cookbooks.flatMap((cookbook) => - cookbook.recipes.map((recipe) => ({ cookbook, recipe })) + cookbook.recipes.map((recipe) => ({ cookbook, recipe })), ); } // Apply language filter using per-recipe languages array if (selectedLanguage) { results = results.filter(({ recipe }) => - recipe.languages.includes(selectedLanguage!) + recipe.languages.includes(selectedLanguage!), ); } // Apply tag filter if (selectedTags.length > 0) { results = results.filter(({ recipe }) => - selectedTags.some((tag) => recipe.tags.includes(tag)) + selectedTags.some((tag) => recipe.tags.includes(tag)), ); } @@ -290,13 +290,13 @@ function setupRecipeListeners(): void { // Language tab clicks document.querySelectorAll(".lang-tab").forEach((tab) => { - tab.addEventListener("click", (e) => { + tab.addEventListener("click", () => { const langId = (tab as HTMLElement).dataset.lang; if (langId) { selectedLanguage = langId; // Update language filter select const languageSelect = document.getElementById( - "filter-language" + "filter-language", ) as HTMLSelectElement; if (languageSelect) languageSelect.value = langId; renderCookbooks(); @@ -311,10 +311,9 @@ function setupRecipeListeners(): void { */ async function showRecipeContent( filePath: string, - type: "recipe" | "example" + type: "recipe" | "example", ): Promise { // Use existing modal infrastructure - const { openFileModal } = await import("../modal"); await openFileModal(filePath, type); } diff --git a/website/src/scripts/resource-catalog.ts b/website/src/scripts/resource-catalog.ts index dba35e897..0ac878632 100644 --- a/website/src/scripts/resource-catalog.ts +++ b/website/src/scripts/resource-catalog.ts @@ -1,9 +1,13 @@ -import type { Searchable } from '../lib/search/fuzzy'; -import type { Accent } from '../lib/types'; -import { createSearch, initSearch } from './search-client'; -import { getQueryParam, getQueryParamValues, updateQueryParams } from './url-state'; +import type { Searchable } from "../lib/search/fuzzy"; +import type { Accent } from "../lib/types"; +import { createSearch, initSearch } from "./search-client"; +import { + getQueryParam, + getQueryParamValues, + updateQueryParams, +} from "./url-state"; -type FilterType = 'multi' | 'single' | 'flag'; +type FilterType = "multi" | "single" | "flag"; export interface CatalogFilterConfig { param: string; @@ -49,72 +53,100 @@ const INITIAL_RENDER_COUNT = 36; const RENDER_BATCH_SIZE = 24; export function stableAccent(source: string): Accent { - const accents: Accent[] = ['purple', 'blue', 'green', 'yellow']; + const accents: Accent[] = ["purple", "blue", "green", "yellow"]; let hash = 0; for (const char of source) hash = (hash * 31 + char.charCodeAt(0)) >>> 0; return accents[hash % accents.length]; } function escapeHtml(value: unknown): string { - return String(value ?? '').replace(/[&<>'"]/g, (char) => { + return String(value ?? "").replace(/[&<>'"]/g, (char) => { switch (char) { - case '&': return '&'; - case '<': return '<'; - case '>': return '>'; - case "'": return '''; - case '"': return '"'; - default: return char; + case "&": + return "&"; + case "<": + return "<"; + case ">": + return ">"; + case "'": + return "'"; + case '"': + return """; + default: + return char; } }); } -async function loadItems(dataUrl: string, toClientItem: (item: TRaw) => CatalogClientItem): Promise { +async function loadItems( + dataUrl: string, + toClientItem: (item: TRaw) => CatalogClientItem, +): Promise { const response = await fetch(`${import.meta.env.BASE_URL}${dataUrl}`); - if (!response.ok) throw new Error(`Failed to load ${dataUrl}: ${response.status}`); - const data = await response.json() as { items: TRaw[] }; + if (!response.ok) + throw new Error(`Failed to load ${dataUrl}: ${response.status}`); + const data = (await response.json()) as { items: TRaw[] }; return data.items.map(toClientItem); } -function readFilterState(configs: CatalogFilterConfig[]): Record { +function readFilterState( + configs: CatalogFilterConfig[], +): Record { const state: Record = {}; for (const config of configs) { - if (config.type === 'flag') { + if (config.type === "flag") { const val = getQueryParam(config.param); - state[config.param] = val === '1' ? ['1'] : []; + state[config.param] = val === "1" ? ["1"] : []; } else { - state[config.param] = config.type === 'multi' - ? getQueryParamValues(config.param) - : [getQueryParam(config.param) || config.defaultValue || ''].filter(Boolean); + state[config.param] = + config.type === "multi" + ? getQueryParamValues(config.param) + : [getQueryParam(config.param) || config.defaultValue || ""].filter( + Boolean, + ); } } return state; } -function applyFilterState(items: CatalogClientItem[], state: Record, configs: CatalogFilterConfig[]): CatalogClientItem[] { - return items.filter((item) => configs.every((config) => { - if (config.param === 'sort') return true; - const selected = state[config.param] ?? []; +function applyFilterState( + items: CatalogClientItem[], + state: Record, + configs: CatalogFilterConfig[], +): CatalogClientItem[] { + return items.filter((item) => + configs.every((config) => { + if (config.param === "sort") return true; + const selected = state[config.param] ?? []; + + if (config.type === "flag") { + if (selected.length === 0) return true; + const values = item.filters[config.param] ?? []; + return values.includes("1"); + } - if (config.type === 'flag') { - if (selected.length === 0) return true; + const active = + config.type === "single" + ? selected.filter((value) => value && value !== config.defaultValue) + : selected; + if (active.length === 0) return true; const values = item.filters[config.param] ?? []; - return values.includes('1'); - } - - const active = config.type === 'single' - ? selected.filter(value => value && value !== config.defaultValue) - : selected; - if (active.length === 0) return true; - const values = item.filters[config.param] ?? []; - return active.some(value => values.includes(value)); - })); + return active.some((value) => values.includes(value)); + }), + ); } -function sortItems(items: CatalogClientItem[], sortBy: string): CatalogClientItem[] { +function sortItems( + items: CatalogClientItem[], + sortBy: string, +): CatalogClientItem[] { return [...items].sort((a, b) => { - if (sortBy === 'lastUpdated') return (b.sortDate ?? '').localeCompare(a.sortDate ?? ''); - if (sortBy === 'featured') { - const featured = Number((b.filters.featured ?? []).includes('true')) - Number((a.filters.featured ?? []).includes('true')); + if (sortBy === "lastUpdated") + return (b.sortDate ?? "").localeCompare(a.sortDate ?? ""); + if (sortBy === "featured") { + const featured = + Number((b.filters.featured ?? []).includes("true")) - + Number((a.filters.featured ?? []).includes("true")); if (featured !== 0) return featured; } return (a.sortTitle ?? a.title).localeCompare(b.sortTitle ?? b.title); @@ -125,8 +157,10 @@ function renderCard(item: CatalogClientItem): string { const accent = escapeHtml(item.accent); const tags = item.tags .slice(0, 3) - .map(tag => `${escapeHtml(tag)}`) - .join(''); + .map( + (tag) => `${escapeHtml(tag)}`, + ) + .join(""); return ` @@ -144,85 +178,133 @@ function renderCard(item: CatalogClientItem): string { `; } -function updateFilterDisplays(configs: CatalogFilterConfig[], state: Record): void { +function updateFilterDisplays( + configs: CatalogFilterConfig[], + state: Record, +): void { for (const config of configs) { - const display = document.querySelector(`.filter-display[data-param="${config.param}"]`); + const display = document.querySelector( + `.filter-display[data-param="${config.param}"]`, + ); const values = state[config.param] ?? []; if (!display) continue; - const active = config.type === 'single' - ? values.filter(value => value && value !== config.defaultValue) - : values; - display.textContent = active.length > 0 ? `${config.label.toLowerCase()}: ${active.join(', ')}` : `${config.label.toLowerCase()}: all`; + const active = + config.type === "single" + ? values.filter((value) => value && value !== config.defaultValue) + : values; + display.textContent = + active.length > 0 + ? `${config.label.toLowerCase()}: ${active.join(", ")}` + : `${config.label.toLowerCase()}: all`; } } -function wireFilterSelects(configs: CatalogFilterConfig[], onChange: () => void): void { - document.querySelectorAll('select[data-filter-param]').forEach((select) => { - const config = configs.find(item => item.param === select.dataset.filterParam); - if (!config) return; +function wireFilterSelects( + configs: CatalogFilterConfig[], + onChange: () => void, +): void { + document + .querySelectorAll("select[data-filter-param]") + .forEach((select) => { + const config = configs.find( + (item) => item.param === select.dataset.filterParam, + ); + if (!config) return; + + if (config.type === "flag") { + const val = getQueryParam(config.param); + select.value = val === "1" ? "1" : ""; + select.addEventListener("change", () => { + const value = select.value === "1" ? "1" : null; + updateQueryParams({ set: { [config.param]: value } }); + onChange(); + }); + return; + } - if (config.type === 'flag') { - const val = getQueryParam(config.param); - select.value = val === '1' ? '1' : ''; - select.addEventListener('change', () => { - const value = select.value === '1' ? '1' : null; - updateQueryParams({ set: { [config.param]: value } }); + const values = + config.type === "multi" + ? getQueryParamValues(config.param) + : [getQueryParam(config.param) || config.defaultValue || ""]; + for (const option of select.options) + option.selected = values.includes(option.value); + + select.addEventListener("change", () => { + if (config.type === "multi") { + updateQueryParams({ + setAll: { + [config.param]: Array.from(select.selectedOptions) + .map((option) => option.value) + .filter(Boolean), + }, + }); + } else { + const value = + select.value && select.value !== config.defaultValue + ? select.value + : null; + updateQueryParams({ set: { [config.param]: value } }); + } onChange(); }); - return; - } - - const values = config.type === 'multi' ? getQueryParamValues(config.param) : [getQueryParam(config.param) || config.defaultValue || '']; - for (const option of select.options) option.selected = values.includes(option.value); - - select.addEventListener('change', () => { - if (config.type === 'multi') { - updateQueryParams({ setAll: { [config.param]: Array.from(select.selectedOptions).map(option => option.value).filter(Boolean) } }); - } else { - const value = select.value && select.value !== config.defaultValue ? select.value : null; - updateQueryParams({ set: { [config.param]: value } }); - } - onChange(); }); - }); } -function wireFilterToggles(configs: CatalogFilterConfig[], onChange: () => void): void { - document.querySelectorAll('button[data-filter-param]').forEach((button) => { - const config = configs.find(item => item.param === button.dataset.filterParam); - if (!config || config.type !== 'flag') return; - - const val = getQueryParam(config.param); - const isActive = val === '1'; - button.setAttribute('aria-pressed', String(isActive)); - if (isActive) button.classList.add('console-toggle--active'); - - button.addEventListener('click', () => { - const url = new URL(window.location.href); - const currentlyActive = url.searchParams.get(config.param) === '1'; - if (currentlyActive) { - url.searchParams.delete(config.param); - button.classList.remove('console-toggle--active'); - } else { - url.searchParams.set(config.param, '1'); - button.classList.add('console-toggle--active'); - } - button.setAttribute('aria-pressed', String(!currentlyActive)); - history.replaceState(null, '', url.toString()); - onChange(); +function wireFilterToggles( + configs: CatalogFilterConfig[], + onChange: () => void, +): void { + document + .querySelectorAll("button[data-filter-param]") + .forEach((button) => { + const config = configs.find( + (item) => item.param === button.dataset.filterParam, + ); + if (!config || config.type !== "flag") return; + + const val = getQueryParam(config.param); + const isActive = val === "1"; + button.setAttribute("aria-pressed", String(isActive)); + if (isActive) button.classList.add("console-toggle--active"); + + button.addEventListener("click", () => { + const url = new URL(window.location.href); + const currentlyActive = url.searchParams.get(config.param) === "1"; + if (currentlyActive) { + url.searchParams.delete(config.param); + button.classList.remove("console-toggle--active"); + } else { + url.searchParams.set(config.param, "1"); + button.classList.add("console-toggle--active"); + } + button.setAttribute("aria-pressed", String(!currentlyActive)); + history.replaceState(null, "", url.toString()); + onChange(); + }); }); - }); } -export async function initResourceCatalog(config: CatalogConfig): Promise<{ applyAndRender: () => void }> { +export async function initResourceCatalog( + config: CatalogConfig, +): Promise<{ applyAndRender: () => void }> { const grid = document.querySelector(config.gridSelector); const countEl = document.querySelector(config.countSelector); const emptyState = document.querySelector(config.emptySelector); - const loadMore = document.querySelector(config.loadMoreSelector); - const loadSentinel = document.querySelector(config.loadSentinelSelector); - const clearButton = document.querySelector(config.clearSelector); - const emptyClear = document.querySelector(config.emptyClearSelector); - const searchInput = document.querySelector(config.searchInputSelector); + const loadMore = document.querySelector( + config.loadMoreSelector, + ); + const loadSentinel = document.querySelector( + config.loadSentinelSelector, + ); + const clearButton = document.querySelector( + config.clearSelector, + ); + const emptyClear = document.querySelector( + config.emptyClearSelector, + ); + const searchInput = document.querySelector( + config.searchInputSelector, + ); if (countEl) countEl.textContent = `Loading ${config.resourceNamePlural}...`; @@ -231,7 +313,8 @@ export async function initResourceCatalog(config: CatalogConfig): Pr items = await loadItems(config.dataUrl, config.toClientItem); } catch (error) { console.error(error); - if (countEl) countEl.textContent = `Failed to load ${config.resourceNamePlural}`; + if (countEl) + countEl.textContent = `Failed to load ${config.resourceNamePlural}`; if (emptyState) emptyState.hidden = false; return { applyAndRender: () => {} }; } @@ -242,8 +325,13 @@ export async function initResourceCatalog(config: CatalogConfig): Pr function renderProgress(): void { const visibleItems = Math.min(visibleCount, currentItems.length); - if (grid) grid.innerHTML = currentItems.slice(0, visibleItems).map(renderCard).join(''); - if (countEl) countEl.textContent = `Showing ${visibleItems} of ${currentItems.length} matching ${config.resourceName}${currentItems.length === 1 ? '' : 's'} (${items.length} total)`; + if (grid) + grid.innerHTML = currentItems + .slice(0, visibleItems) + .map(renderCard) + .join(""); + if (countEl) + countEl.textContent = `Showing ${visibleItems} of ${currentItems.length} matching ${config.resourceName}${currentItems.length === 1 ? "" : "s"} (${items.length} total)`; if (loadMore) { loadMore.hidden = visibleItems >= currentItems.length; loadMore.textContent = `Load more ${config.resourceNamePlural} (${visibleItems}/${currentItems.length})`; @@ -252,74 +340,103 @@ export async function initResourceCatalog(config: CatalogConfig): Pr } function applyAndRender(): void { - const query = searchInput?.value.trim() || getQueryParam('q') || ''; - const filteredBySearch = query ? engine.search(query).filter((item): item is CatalogClientItem => item != null) : [...items]; + const query = searchInput?.value.trim() || getQueryParam("q") || ""; + const filteredBySearch = query + ? engine + .search(query) + .filter((item): item is CatalogClientItem => item != null) + : [...items]; const state = readFilterState(config.filterConfigs); - const sortBy = getQueryParam('sort') || config.defaultSort; + const sortBy = getQueryParam("sort") || config.defaultSort; state.sort = [sortBy]; - currentItems = sortItems(applyFilterState(filteredBySearch, state, config.filterConfigs), sortBy); + currentItems = sortItems( + applyFilterState(filteredBySearch, state, config.filterConfigs), + sortBy, + ); visibleCount = INITIAL_RENDER_COUNT; renderProgress(); updateFilterDisplays(config.filterConfigs, state); if (emptyState) emptyState.hidden = currentItems.length > 0; - if (grid) grid.style.display = currentItems.length > 0 ? '' : 'none'; + if (grid) grid.style.display = currentItems.length > 0 ? "" : "none"; } function clearFilters(): void { const remove: Record = { q: null }; - for (const filterConfig of config.filterConfigs) remove[filterConfig.param] = null; + for (const filterConfig of config.filterConfigs) + remove[filterConfig.param] = null; updateQueryParams({ set: remove }); - if (searchInput) searchInput.value = ''; - document.querySelectorAll('select[data-filter-param]').forEach((select) => { - for (const option of select.options) option.selected = option.value === '' || option.value === select.dataset.defaultValue; - }); + if (searchInput) searchInput.value = ""; + document + .querySelectorAll("select[data-filter-param]") + .forEach((select) => { + for (const option of select.options) + option.selected = + option.value === "" || option.value === select.dataset.defaultValue; + }); applyAndRender(); } wireFilterSelects(config.filterConfigs, applyAndRender); wireFilterToggles(config.filterConfigs, applyAndRender); - clearButton?.addEventListener('click', clearFilters); - emptyClear?.addEventListener('click', clearFilters); - loadMore?.addEventListener('click', () => { - visibleCount = Math.min(visibleCount + RENDER_BATCH_SIZE, currentItems.length); + clearButton?.addEventListener("click", clearFilters); + emptyClear?.addEventListener("click", clearFilters); + loadMore?.addEventListener("click", () => { + visibleCount = Math.min( + visibleCount + RENDER_BATCH_SIZE, + currentItems.length, + ); renderProgress(); }); - if (loadSentinel && 'IntersectionObserver' in window) { - const observer = new IntersectionObserver((entries) => { - if (!entries.some(entry => entry.isIntersecting)) return; - visibleCount = Math.min(visibleCount + RENDER_BATCH_SIZE, currentItems.length); - renderProgress(); - }, { rootMargin: '320px' }); + if (loadSentinel && "IntersectionObserver" in window) { + const observer = new IntersectionObserver( + (entries) => { + if (!entries.some((entry) => entry.isIntersecting)) return; + visibleCount = Math.min( + visibleCount + RENDER_BATCH_SIZE, + currentItems.length, + ); + renderProgress(); + }, + { rootMargin: "320px" }, + ); observer.observe(loadSentinel); } let searchTimer = 0; - searchInput?.addEventListener('input', () => { + searchInput?.addEventListener("input", () => { window.clearTimeout(searchTimer); searchTimer = window.setTimeout(applyAndRender, 220); }); - initSearch(engine, { - inputSelector: config.searchInputSelector, - resultsSelector: config.searchResultsSelector, - statusSelector: config.searchStatusSelector, - minLength: 1, - maxResults: 40, - }, { - render(item, idx, query, highlight) { - const desc = item.description.length > 100 ? `${item.description.slice(0, 100)}...` : item.description; - return `
${highlight(escapeHtml(item.title), query)}${highlight(escapeHtml(desc), query)}
`; + initSearch( + engine, + { + inputSelector: config.searchInputSelector, + resultsSelector: config.searchResultsSelector, + statusSelector: config.searchStatusSelector, + minLength: 1, + maxResults: 40, }, - onSelect(item) { - window.location.href = item.href; + { + render(item, idx, query, highlight) { + const desc = + item.description.length > 100 + ? `${item.description.slice(0, 100)}…` + : item.description; + return `
${highlight(escapeHtml(item.title), query)}${highlight(escapeHtml(desc), query)}
`; + }, + onSelect(item) { + window.location.href = item.href; + }, + statusText(query, resultCount, totalCount) { + if (!query) return ""; + if (resultCount === 0) + return `No ${config.resourceNamePlural} match "${query}"`; + return `${resultCount} of ${totalCount} ${config.resourceNamePlural} match "${query}"`; + }, }, - statusText(query, resultCount, totalCount) { - if (!query) return ''; - if (resultCount === 0) return `No ${config.resourceNamePlural} match "${query}"`; - return `${resultCount} of ${totalCount} ${config.resourceNamePlural} match "${query}"`; - }, - }); + ); applyAndRender(); diff --git a/website/src/styles/global.css b/website/src/styles/global.css index 026acc5d2..6be7dfed3 100644 --- a/website/src/styles/global.css +++ b/website/src/styles/global.css @@ -33,7 +33,11 @@ body { margin: 0; min-height: 100vh; background: - radial-gradient(circle at 82% 10%, rgb(169 112 255 / 5%), transparent 26rem), + radial-gradient( + circle at 82% 10%, + rgb(169 112 255 / 5%), + transparent 26rem + ), radial-gradient(circle at 18% 28%, rgb(45 140 255 / 3%), transparent 24rem), var(--color-bg); color: var(--color-text); @@ -82,6 +86,23 @@ img { } /* ── Accessibility utilities ───────────────────────────────── */ +.skip-link { + position: absolute; + left: 1rem; + top: -3rem; + z-index: 100; + border: 2px solid var(--color-primary); + background: var(--color-bg-elevated); + color: var(--color-text); + padding: 0.5rem 0.75rem; + font-weight: 700; + transition: top var(--motion-fast); +} + +.skip-link:focus-visible { + top: 0.8rem; +} + .sr-only { position: absolute; width: 1px; @@ -232,10 +253,22 @@ img { transform var(--motion-fast); } -.console-search-control { min-width: min(100%, 24rem); } -.console-select-control { min-width: 13.5rem; cursor: pointer; justify-content: space-between; text-align: left; line-height: 1; } -.console-select-control--wide { min-width: 15rem; } -.console-select-control--sort { min-width: 13.5rem; } +.console-search-control { + min-width: min(100%, 24rem); +} +.console-select-control { + min-width: 13.5rem; + cursor: pointer; + justify-content: space-between; + text-align: left; + line-height: 1; +} +.console-select-control--wide { + min-width: 15rem; +} +.console-select-control--sort { + min-width: 13.5rem; +} .console-search-control:hover, .console-search-control:focus-within, @@ -272,9 +305,16 @@ img { box-shadow: none; } -.console-search-control input::placeholder { color: var(--color-muted); opacity: 1; } -.console-search-control svg { color: var(--color-subtle); } -.console-select-control svg { color: var(--color-muted); } +.console-search-control input::placeholder { + color: var(--color-muted); + opacity: 1; +} +.console-search-control svg { + color: var(--color-subtle); +} +.console-select-control svg { + color: var(--color-muted); +} .console-select-control { position: relative; @@ -339,8 +379,13 @@ img { white-space: nowrap; } -.console-prompt { color: var(--color-green); font-weight: 900; } -.console-prompt--muted { color: var(--color-muted); } +.console-prompt { + color: var(--color-green); + font-weight: 900; +} +.console-prompt--muted { + color: var(--color-muted); +} /* ── Console toggle (handoffs) ────────────────────────────── */ .console-toggle { @@ -359,7 +404,9 @@ img { font-size: 0.88rem; font-weight: 900; white-space: nowrap; - transition: background var(--motion-fast), color var(--motion-fast); + transition: + background var(--motion-fast), + color var(--motion-fast); } .console-toggle--active { @@ -381,7 +428,9 @@ img { transition: background var(--motion-fast); } -.console-reset-control:hover { background: var(--color-danger); } +.console-reset-control:hover { + background: var(--color-danger); +} /* ── Copy button (used on detail pages) ───────────────────── */ .copy-button, @@ -416,10 +465,12 @@ img { } .copy-button svg, -.button[data-copy] svg { flex: 0 0 auto; } +.button[data-copy] svg { + flex: 0 0 auto; +} .copy-button:hover, -.copy-button[data-copied='true'], +.copy-button[data-copied="true"], .button[data-copy]:hover { background: rgb(29 35 38 / 92%); border-color: var(--color-border); @@ -432,7 +483,7 @@ img { } .copy-button--preview:hover, -.copy-button--preview[data-copied='true'] { +.copy-button--preview[data-copied="true"] { background: rgb(29 35 38 / 92%); border-color: var(--color-border); color: var(--color-text); @@ -444,7 +495,9 @@ img { outline-offset: 2px; } -.copy-button--preview:focus-visible { outline-color: rgb(255 255 255 / 35%); } +.copy-button--preview:focus-visible { + outline-color: rgb(255 255 255 / 35%); +} .copy-button:active, .button[data-copy]:active { @@ -476,7 +529,7 @@ img { .badge::before, .badge::after { - content: ''; + content: ""; position: absolute; top: 4px; bottom: 4px; @@ -484,15 +537,43 @@ img { background-color: inherit; } -.badge::before { left: -6px; } -.badge::after { right: -6px; } +.badge::before { + left: -6px; +} +.badge::after { + right: -6px; +} -.badge--purple { background: var(--color-primary); color: var(--color-black); border-color: var(--color-primary); } -.badge--blue { background: var(--color-blue); color: var(--color-black); border-color: var(--color-blue); } -.badge--green { background: var(--color-green); color: var(--color-black); border-color: var(--color-green); } -.badge--yellow { background: var(--color-yellow); color: var(--color-black); border-color: var(--color-yellow); } -.badge--danger { background: var(--color-danger); color: var(--color-black); border-color: var(--color-danger); } -.badge--neutral { background: var(--color-surface-2); color: var(--color-text); border-color: var(--color-border-soft); } +.badge--purple { + background: var(--color-primary); + color: var(--color-black); + border-color: var(--color-primary); +} +.badge--blue { + background: var(--color-blue); + color: var(--color-black); + border-color: var(--color-blue); +} +.badge--green { + background: var(--color-green); + color: var(--color-black); + border-color: var(--color-green); +} +.badge--yellow { + background: var(--color-yellow); + color: var(--color-black); + border-color: var(--color-yellow); +} +.badge--danger { + background: var(--color-danger); + color: var(--color-black); + border-color: var(--color-danger); +} +.badge--neutral { + background: var(--color-surface-2); + color: var(--color-text); + border-color: var(--color-border-soft); +} /* ── Pixel resource cards (agents grid) ───────────────────── */ .resource-grid { @@ -520,7 +601,10 @@ img { padding: 1rem 1rem 1rem 1.25rem; color: inherit; text-decoration: none; - transition: background var(--motion-fast), box-shadow var(--motion-fast), transform var(--motion-fast); + transition: + background var(--motion-fast), + box-shadow var(--motion-fast), + transform var(--motion-fast); } .resource-card::before { @@ -550,13 +634,35 @@ img { box-shadow: 5px 5px 0 var(--color-black); } -.resource-card:focus-visible { outline: 2px solid rgb(255 255 255 / 35%); outline-offset: 2px; box-shadow: 5px 5px 0 var(--color-black); } -.resource-card--purple { --card-accent: var(--color-primary); --card-accent-rgb: 169 112 255; } -.resource-card--blue { --card-accent: var(--color-blue); --card-accent-rgb: 45 140 255; } -.resource-card--green { --card-accent: var(--color-green); --card-accent-rgb: 144 255 177; } -.resource-card--yellow { --card-accent: var(--color-yellow); --card-accent-rgb: 255 192 87; } -.resource-card--danger { --card-accent: var(--color-danger); --card-accent-rgb: 255 80 124; } -.resource-card--neutral { --card-accent: var(--color-muted); --card-accent-rgb: 160 170 180; } +.resource-card:focus-visible { + outline: 2px solid rgb(255 255 255 / 35%); + outline-offset: 2px; + box-shadow: 5px 5px 0 var(--color-black); +} +.resource-card--purple { + --card-accent: var(--color-primary); + --card-accent-rgb: 169 112 255; +} +.resource-card--blue { + --card-accent: var(--color-blue); + --card-accent-rgb: 45 140 255; +} +.resource-card--green { + --card-accent: var(--color-green); + --card-accent-rgb: 144 255 177; +} +.resource-card--yellow { + --card-accent: var(--color-yellow); + --card-accent-rgb: 255 192 87; +} +.resource-card--danger { + --card-accent: var(--color-danger); + --card-accent-rgb: 255 80 124; +} +.resource-card--neutral { + --card-accent: var(--color-muted); + --card-accent-rgb: 160 170 180; +} .resource-card__topline, .resource-card__tags, @@ -569,7 +675,9 @@ img { } .resource-card__topline, -.resource-card__footer { justify-content: space-between; } +.resource-card__footer { + justify-content: space-between; +} .resource-card__footer { align-items: flex-end; gap: 0.8rem 1rem; @@ -578,11 +686,36 @@ img { flex: 1 1 auto; min-width: 0; } -.resource-card__arrow { display: grid; place-items: center; color: var(--color-muted); transition: color var(--motion-fast), transform var(--motion-fast); } -.resource-card:hover .resource-card__arrow { color: var(--card-accent); transform: translateX(2px); } -.resource-card__body { display: grid; align-content: start; gap: 0.65rem; } -.resource-card__title { color: var(--color-text); font-family: var(--font-heading); font-size: clamp(1.2rem, 2vw, 1.45rem); font-weight: 800; letter-spacing: -0.035em; line-height: 1.08; } -.resource-card__description { color: var(--color-muted); font-size: 0.95rem; line-height: 1.55; } +.resource-card__arrow { + display: grid; + place-items: center; + color: var(--color-muted); + transition: + color var(--motion-fast), + transform var(--motion-fast); +} +.resource-card:hover .resource-card__arrow { + color: var(--card-accent); + transform: translateX(2px); +} +.resource-card__body { + display: grid; + align-content: start; + gap: 0.65rem; +} +.resource-card__title { + color: var(--color-text); + font-family: var(--font-heading); + font-size: clamp(1.2rem, 2vw, 1.45rem); + font-weight: 800; + letter-spacing: -0.035em; + line-height: 1.08; +} +.resource-card__description { + color: var(--color-muted); + font-size: 0.95rem; + line-height: 1.55; +} .resource-card__title, .resource-card__description { margin: 0; @@ -690,14 +823,28 @@ img { text-underline-offset: 0.22em; white-space: nowrap; } -.resource-card__cta :global(.icon) { transition: transform var(--motion-fast); } +.resource-card__cta :global(.icon) { + transition: transform var(--motion-fast); +} .resource-card:hover .resource-card__cta :global(.icon), -.resource-card:focus-visible .resource-card__cta :global(.icon) { transform: translateX(2px); } +.resource-card:focus-visible .resource-card__cta :global(.icon) { + transform: translateX(2px); +} /* ── Results meta ─────────────────────────────────────────── */ -.section { padding-block: 1.75rem 3rem; } -.marketplace-results { display: grid; gap: 0.85rem; margin-top: 1.25rem; } -.results-meta p { margin: 0; color: var(--color-muted); font-size: 0.88rem; } +.section { + padding-block: 1.75rem 3rem; +} +.marketplace-results { + display: grid; + gap: 0.85rem; + margin-top: 1.25rem; +} +.results-meta p { + margin: 0; + color: var(--color-muted); + font-size: 0.88rem; +} /* ── Catalog search / progressive loading ─────────────────── */ .search-results-dropdown { @@ -730,9 +877,13 @@ img { transition: background var(--motion-fast); } -.search-result:last-child { border-bottom: 0; } +.search-result:last-child { + border-bottom: 0; +} .search-result:hover, -.search-result--active { background: var(--color-surface-2); } +.search-result--active { + background: var(--color-surface-2); +} .search-result-title { color: var(--color-text); @@ -792,7 +943,9 @@ img { font-family: var(--font-mono); font-size: 0.88rem; font-weight: 900; - transition: background var(--motion-fast), color var(--motion-fast); + transition: + background var(--motion-fast), + color var(--motion-fast); } .filter-clear:hover { @@ -868,18 +1021,39 @@ img { /* ── Responsive: console ──────────────────────────────────── */ @media (max-width: 980px) { - .console-header-card { grid-template-columns: 1fr; } - .console-stats-grid { justify-content: end; justify-self: end; } - .resource-grid { column-count: 2; } + .console-header-card { + grid-template-columns: 1fr; + } + .console-stats-grid { + justify-content: end; + justify-self: end; + } + .resource-grid { + column-count: 2; + } } @media (max-width: 640px) { - .console-stats-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; } - .console-stat-card { width: auto; } - .console-filter-bar { grid-template-columns: 1fr; overflow-x: visible; } + .console-stats-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + width: 100%; + } + .console-stat-card { + width: auto; + } + .console-filter-bar { + grid-template-columns: 1fr; + overflow-x: visible; + } .console-search-control, .console-select-control, .console-toggle, - .console-reset-control { width: 100%; min-width: 0; } - .resource-grid { column-count: 1; } + .console-reset-control { + width: 100%; + min-width: 0; + } + .resource-grid { + column-count: 1; + } } From 51a88093ec3af27f00182d2fb60fa9fecb82f2a5 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Mon, 29 Jun 2026 19:51:56 -0500 Subject: [PATCH 20/26] feat(website-contributors): add contributors section component and page --- .../sections/ContributorsSection.astro | 24 +- website/src/pages/contributors.astro | 873 ++++-------------- 2 files changed, 198 insertions(+), 699 deletions(-) diff --git a/website/src/components/sections/ContributorsSection.astro b/website/src/components/sections/ContributorsSection.astro index 8a6ab562e..331a4f62a 100644 --- a/website/src/components/sections/ContributorsSection.astro +++ b/website/src/components/sections/ContributorsSection.astro @@ -5,6 +5,7 @@ * contributors, linking to the full /contributors/ page. */ import fs from 'node:fs'; +import path from 'node:path'; import BitButton from '../ui/8BitButton.astro'; import Container from '../ui/Container.astro'; import Icon from '../ui/Icon.astro'; @@ -22,7 +23,7 @@ interface AllContributorsRc { contributors: AllContributor[]; } -const rcPath = new URL('../../../../.all-contributorsrc', import.meta.url); +const rcPath = path.resolve(process.cwd(), '../.all-contributorsrc'); const rc = JSON.parse(fs.readFileSync(rcPath, 'utf-8')) as AllContributorsRc; const PREVIEW_COUNT = 24; @@ -105,8 +106,8 @@ const total = rc.contributors.length; margin: 0; padding: 0; display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 0.75rem; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 1rem; } .contributor { @@ -117,10 +118,10 @@ const total = rc.contributors.length; .contributor__link { display: flex; align-items: center; - gap: 0.7rem; + gap: 0.9rem; width: 100%; height: 100%; - padding: 0.6rem 0.75rem; + padding: 0.9rem 1rem; border: 2px solid var(--color-subtle); border-radius: 0; background: var(--color-bg-elevated); @@ -140,11 +141,10 @@ const total = rc.contributors.length; .contributor__avatar { flex: none; - width: 40px; - height: 40px; + width: 52px; + height: 52px; display: grid; place-items: center; - border: 2px solid var(--color-subtle); overflow: hidden; background: var(--color-bg); } @@ -165,7 +165,7 @@ const total = rc.contributors.length; .contributor__name { font-family: var(--font-mono); - font-size: 0.8rem; + font-size: 0.9rem; font-weight: 700; color: var(--color-text); white-space: nowrap; @@ -175,18 +175,18 @@ const total = rc.contributors.length; .contributor__handle { font-family: var(--font-mono); - font-size: 0.68rem; + font-size: 0.74rem; color: var(--color-muted); } .contributor__count { margin-top: 0.15rem; font-family: var(--font-mono); - font-size: 0.62rem; + font-size: 0.68rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; - color: var(--color-green); + color: var(--color-muted); } .contributors__foot { diff --git a/website/src/pages/contributors.astro b/website/src/pages/contributors.astro index 2f9a4e9ff..0cd63c5b9 100644 --- a/website/src/pages/contributors.astro +++ b/website/src/pages/contributors.astro @@ -1,692 +1,191 @@ --- -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; -import PageHeader from '../components/PageHeader.astro'; +import BaseLayout from '../layouts/BaseLayout.astro'; +import Container from '../components/ui/Container.astro'; +import { SITE } from '../config/site'; +import fs from 'node:fs'; +import path from 'node:path'; + +interface AllContributor { + login: string; + name: string; + avatar_url: string; + profile: string; + contributions: string[]; +} + +interface AllContributorsRc { + projectName: string; + contributors: AllContributor[]; +} + +const rcPath = path.resolve(process.cwd(), '../.all-contributorsrc'); +const rc = JSON.parse(fs.readFileSync(rcPath, 'utf-8')) as AllContributorsRc; + +const contributors = rc.contributors; +const total = contributors.length; --- - -
- - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Aaron Powell

Matt Soucoup

Troy Simeon Taylor

Abbas

Peter StrΓΆmberg

Daniel Scott-Raynsford

John Haugabook

Pavel Simsa

Harald Kirschner

Muhammad Ubaid Raza

Tom Meschter

Aung Myo Kyaw

JasonYeMSFT

Jon Corbin

troytaylor-msft

Emerson Delatorre

Burke Holland

Kent Yao

Daniel Meppiel

Gordon Lam

Mads Kristensen

Shinji Takenaka

spectatora

Yohan Lasorsa

Vamshi Verma

James Montemagno

Alessandro Fragnani

Ambily

krushideep

devopsfan

Tugdual Grall

Oren Me

Mike Rousos

Justin Yoo

Guilherme do Amaral Alves

Griffin Ashe

Ashley Childress

Adrien Clerbois

ANGELELLI David

Mark Davis

Matt Vevang

Maximilian Irro

NULLchimp

Peter Karda

Saul Dolgin

Shubham Gaikwad

Theo van Kraay

Tianqi Zhang

Will 保ε“₯

Yuta Matsumura

anschnapp

hizahizi-hizumi

黃ε₯ζ—» Vincent Huang

Bruno Borges

Steve Magne

Shane Neuville

AndrΓ© Silva

Allen Greaves

Amelia Payne

BBoyBen

Brooke Hamilton

Christopher Harrison

Dan

Dan Wahlin

Debbie O'Brien

Ed Harrod

Genevieve Warren

Guillaume

Henrique Nunes

Jeremiah Snee

Kartik Dhiman

Kristiyan Velkov

msalaman

Per SΓΈderlind

Peter Smulovics

Ravish Rathod

Rick Smit

Rob Simpson

Robert Altman

Salih

Sebastian GrΓ€f

Sebastien DEGODEZ

Sergiy Smyrnov

SomeSolutionsArchitect

Stu Mace

SΓΈren TrudsΓΈ Mahon

Tj Vita

Peli de Halleux

Paulo Morgado

Paul Crane

Pamela Fox

Oskar Thornblad

Nischay Sharma

Nikolay Marinov

Nik Sachdeva

Nick Taylor

Nick Brady

Nathan Stanford Sr

MΓ‘tΓ© BarabΓ‘s

Mike Parker

Mike Kistler

Giovanni de Almeida Martins

μ΄μƒν˜„

Ankur Sharma

Wendy Breiding

voidfnc

shane lee

sdanzo-hrb

sauran

samqbush

pareenaverma

oleksiyyurchyna

oceans-of-time

kshashank57

Meii

factory-davidgu

dangelov-qa

BenoitMaucotel

benjisho-aidome

Yuki Omoto

Will Schultz

Waren Gonzaga

Vincent Koc

Victor Williams

Ve Sharma

Vasileios Lahanas

Udaya Veeramreddygari

TΓ i LΓͺ

Tsubasa Ogawa

Troy Witthoeft (glsauto)

Gerald Versluis

George Dernikos

Gautam

Furkan Enes

Florian MΓΌcke

Felix Arjuna

Eldrick Wega

Dobri Danchev

Diego Gamboa

Derek Clair

David Ortinau

Daniel Abbatt

CypherHK

Craig Bekker

Christophe Peugnet

Christian Lechner

Chris Harris

Artem Saveliev

Antoine Rey

Ankit Das

Aline Ávila

Alexander Martinkevich

Aleksandar Dunchev

Alan Sprecacenere

Akash Kumar Shaw

Abdi Daud

AIAlchemyForge

4regab

Miguel P Z

Michael Fairchild

Michael A. Volz (Flynn)

Michael

Mehmet Ali EROL

Max Prilutskiy

Matteo Bianchi

Mark Noble

Manish Jayaswal

Luke Murray

Louella Creemers

Sai Koumudi Kaluvakolanu

Kenny White

KaloyanGenev

Kim Skov Rasmussen

Julien Dubois

JosΓ© Antonio Garrido

Joseph Gonzales

Jorge Balderas

John Papa

John

Joe Watkins

Jan de Vries

Jakub JareΕ‘

Jackson Miller

Ioana A

Hunter Hogan

Hashim Warren

Gonzalo

Gisela Torres

Shibi Ramachandran

lupritz

HΓ©ctor Benedicte

Ted Vilutis

Anthony Shaw

Chris McKee

CASTResearchLabs

η™½ζ°΄ζ·³

Imran Siddique

ε…±δΊ§δΈ»δΉ‰ζŽ₯班人

Ivan Charapanau

Tadas Labudis

Alvin Ashcraft

Jan Krivanek

Gregg Cochran

Josh N

ian zhang

Garrett Siegel

Roberto Perez

Dan Velton

Lee Reilly

Daniel Coelho

Vahid Faraji

Ashley Wolf

Noah Jenkins

Jeremy Kohn

Harri Sipola

Toru Makabe

Pham Tien Thuan Phat

Benji Shohet

Amaury LevΓ©

Tim Deschryver

Mohammad Asad Alahmadi

fondoger

Yuval Avidani

Csaba IvΓ‘ncza

Tim Heuer

lance2k

Andrea Liliana Griffiths

Ajith Raghavan

Catherine Han

Igor Shishkin

Burrito Verde

Joseph Van der Wee

Luiz Bon

Sanjay Ramassery Babu

Russ Rimmerman [MSFT]

Roberto Perez

Shehab Sherif

Smit Patel

Steven Vore

Subhashis Bhowmik

Tim Mulholland

Niels Laute

Pavel Sulimau

PrimedPaul

Zhiqi Pu

Ramyashree Shetty

ZdaPhp

pigd0g

rahulbats

suyask-msft

tagedeep

tinkeringDev

Travis Hill

Utkarsh patrikar

Yauhen

Yiou Li

Yuki Omoto

Abhi Bavishi

augustus-0

Branislav Buna

connerlambden

David Raygoza

Diego Porto Ritzel

Eric Scherlinger

Fatih

Felipe Pessoto

FranΓ§ois

Geoffrey Casaubon

Anddd7

Anders Eide

Aymen

Kevin van Zonneveld

Luis Cantero

MV Karan

Marcel Deutzer

Jon Galloway

Josh Beard

Julian

Simon Kurtz

Temitayo Afolabi

JoeVenner

Pasindu Premarathna

ecosystem

Punit

Onur Senturk

Andrew Stellman

Jeonghoon Lee

Satya K

Samik Roy

Simina Pasat

Tyler Garner

Vijay Chegu

DTIBeograd

Anmol Behl

Brad Kinnard

Chad Bentz

Marcello Cuoghi

Josh Johanning

jennyf19

Saravanan Rajaraman

Patel Dhruv

Renee Noble

jjpinto

moeyui1

mohammadali2549

Vladislav Guzey

aparna198809

Ed McAdams

Emil Andersson

Mikael

Mrigank Singh

Jim Bennett

Alishahzad1903

Antonio Villanueva

Tim Hanewich

ming

Scott O'Hara

Salih

Shailesh

Shubham Jiyani

Srinivas Vaddi

Philippe D

Rajesh Goldy

dstrupl

wuwen

Tilak Patel

Vijay Bandi

Zixuan Jiang

Dennis Lembree

Dev Shah

Falco

AJ

Anush

Ayush Saklani

Carlos Alexandro Becker

Mangokernel

Mario Codes

Gonzalo Fleming

Steve Magne

Sertxito

Rayner Zeng

ilderaj

mvanderbend-msoft

Parveen Sharma

pmorong

vinod kumar

Vidhart Bhatia

Xiaoyun Ding

denis-a-evdokimov

Adriano Nogueira

Aezan

Andy Anderson

Kweku Dzata

Marcel

Navaneeth Reddy

James

Joseph Counts

Neha Mandge

Srikanth Patchava

Thomas Ray

Nixon Kurian

Petr Stupka

Pieter de Bruin

sudeepghatak

tlietz

dawright22

Alejandro Fernando Suarez Gomez

Burak BayΔ±r

MUHAMMAD SAMIULLAH

Nikola Metulev

Joseph Kasprzyk

Lovy Jain
- - Add your contributions - -
- - - - - -
+ +
+ +
+

$ git shortlog -sne | wc -l

+

All Contributors

+

{total} humans shipping agents, skills, plugins, and instructions to the console.

-
-
- - - - + + + + + + From 09166a08b0150659816b04423c97006726bf996c Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Mon, 29 Jun 2026 19:51:59 -0500 Subject: [PATCH 21/26] refactor(website-design): update design tokens and type definitions --- .../src/components/sections/HeroSection.astro | 59 ++++++++++---- .../src/components/ui/RetroResourceCard.astro | 53 ++++++++----- website/src/lib/adapters/manifest.ts | 14 ++-- website/src/lib/types.ts | 2 +- website/src/styles/starlight-overrides.css | 12 +-- website/src/styles/tokens.css | 77 +++++++++++++++++++ 6 files changed, 168 insertions(+), 49 deletions(-) create mode 100644 website/src/styles/tokens.css diff --git a/website/src/components/sections/HeroSection.astro b/website/src/components/sections/HeroSection.astro index 817924e19..1dfd6c975 100644 --- a/website/src/components/sections/HeroSection.astro +++ b/website/src/components/sections/HeroSection.astro @@ -20,6 +20,14 @@ interface Props { const { resources, searchEntries } = Astro.props; +/** Pick one representative per resource type for the hero preview so icons differ. */ +function pickHeroPreview(entries: SearchResultEntry[]): SearchResultEntry[] { + const types = ['agent', 'skill', 'hook']; + return types + .map((t) => entries.find((e) => e.type === t)) + .filter((e): e is SearchResultEntry => e !== undefined); +} + /** Icon name per section β€” maps manifest icon keys to Icon component names. */ const SECTION_ICON_MAP: Record = { agent: 'agents', @@ -54,9 +62,9 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green'];
- {searchEntries.slice(0, 3).map((entry, i) => ( + {pickHeroPreview(searchEntries).map((entry, i) => ( - + > {entry.title} {entry.description.length > 42 ? `${entry.description.slice(0, 42)}...` : entry.description} @@ -116,8 +124,8 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; max-width: 1440px; margin-inline: auto; display: grid; - gap: clamp(2rem, 4vw, 3rem); - padding: clamp(2.8rem, 5vw, 4rem) clamp(1rem, 3vw, 2.5rem) 2rem; + gap: clamp(1.5rem, 3vw, 2.5rem); + padding: clamp(2.8rem, 5vw, 4rem) clamp(1rem, 3vw, 2.5rem) 0; } .hero-shell { @@ -125,20 +133,22 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; grid-template-columns: minmax(0, 1.2fr) minmax(20rem, 0.8fr); align-items: center; gap: clamp(2rem, 5vw, 5rem); - min-height: clamp(31rem, 47vw, 39rem); + min-height: clamp(30rem, 46vw, 38rem); border-bottom: 1px solid var(--color-border-soft); - padding-bottom: clamp(2rem, 4vw, 3.5rem); + padding-bottom: clamp(0.5rem, 1.5vw, 1.5rem); } .hero-copy { display: grid; gap: 1rem; z-index: 1; + min-width: 0; max-width: 58rem; } .terminal-label { width: fit-content; + max-width: 100%; margin: 0; border: 2px solid rgb(255 192 87 / 55%); border-radius: 0; @@ -148,6 +158,7 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; padding: 0.4rem 0.8rem; font-size: clamp(0.78rem, 1.4vw, 0.9rem); font-weight: 800; + overflow-wrap: anywhere; } h1 { @@ -159,6 +170,7 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; letter-spacing: -0.04em; line-height: 1.05; text-wrap: balance; + overflow-wrap: anywhere; text-shadow: 0 3px 0 rgb(0 0 0 / 40%), 3px 0 0 rgb(0 0 0 / 25%); @@ -189,6 +201,7 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; align-items: center; gap: 0.85rem; width: min(100%, 45rem); + min-width: 0; min-height: 3.5rem; margin-top: 0.75rem; border-top: 2px solid rgb(169 112 255 / 16%); @@ -207,6 +220,7 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; } .placeholder { + flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; @@ -266,9 +280,10 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; .hero-visual { position: relative; display: grid; - place-items: center; + align-content: center; + justify-items: center; align-self: stretch; - min-height: 29rem; + min-height: 26rem; overflow: hidden; padding-left: 2.5rem; } @@ -304,11 +319,8 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; .pixel-star--three { right: 10%; top: 32%; color: var(--color-blue); } .status-console { - position: absolute; + position: relative; z-index: 1; - left: 50%; - bottom: 2%; - transform: translateX(-50%); display: grid; gap: 0.45rem; min-width: min(100%, 25rem); @@ -316,6 +328,7 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; border-radius: 0; background: rgb(5 7 7 / 88%); padding: 1rem 1.25rem; + margin-top: 1.5rem; } .status-console p { @@ -341,6 +354,7 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.25rem; + width: 100%; max-width: 1440px; margin-inline: auto; padding-block: 2rem; @@ -350,7 +364,6 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; .hero-shell { grid-template-columns: 1fr; } .hero-visual { min-height: 24rem; } .mascot { width: min(58%, 24rem); } - .status-console { left: 50%; transform: translateX(-50%); } .resource-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @@ -366,9 +379,6 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; .hero-visual { min-height: 21rem; } .mascot { width: min(76%, 22rem); } .status-console { - left: 50%; - transform: translateX(-50%); - bottom: 1rem; min-width: 0; width: calc(100% - 2rem); } @@ -378,6 +388,21 @@ const RESULT_ACCENT: Accent[] = ['purple', 'blue', 'green']; .hero-console { padding-inline: 1rem; } .hero-shell { min-height: auto; } .hero-visual { display: none; } - .resource-grid { grid-template-columns: 1fr; } + .command-search { + gap: 0.65rem; + padding-inline: 0.85rem; + } + .placeholder { + white-space: normal; + overflow-wrap: anywhere; + text-overflow: clip; + } + .resource-grid { + grid-template-columns: 1fr; + justify-items: center; + } + .resource-grid > * { + width: min(100%, 30rem); + } } diff --git a/website/src/components/ui/RetroResourceCard.astro b/website/src/components/ui/RetroResourceCard.astro index b9c282363..a3c1209e9 100644 --- a/website/src/components/ui/RetroResourceCard.astro +++ b/website/src/components/ui/RetroResourceCard.astro @@ -14,9 +14,20 @@ interface Props { const { title, description, href, icon, accent, label } = Astro.props; const ctaLabel = label ?? title; + +const ACCENT_CSS: Record = { + purple: 'var(--color-primary)', + blue: 'var(--color-blue)', + green: 'var(--color-green)', + yellow: 'var(--color-yellow)', + danger: 'var(--color-danger)', + cyan: 'var(--color-cyan)', + orange: 'var(--color-orange)', + neutral:'var(--color-muted)', +}; --- -
+

{title}

@@ -33,6 +44,8 @@ const ctaLabel = label ?? title; .retro-card { display: flex; height: 100%; + min-width: 0; + padding-inline: 4px; background: var(--color-bg-elevated); } @@ -45,9 +58,10 @@ const ctaLabel = label ?? title; align-items: stretch; flex: 1; height: 100%; + min-width: 0; min-height: 9.75rem; - border-top: 4px solid var(--color-subtle); - border-bottom: 4px solid var(--color-subtle); + border-top: 2px solid var(--color-border-soft); + border-bottom: 2px solid var(--color-border-soft); border-left: none; border-right: none; border-radius: 0; @@ -55,22 +69,28 @@ const ctaLabel = label ?? title; background: var(--color-bg-elevated); color: inherit; text-decoration: none; - transition: background var(--motion-fast); + transition: background var(--motion-fast), border-color var(--motion-fast); } .retro-card__link::before { content: ""; position: absolute; inset: 0; - border-left: 4px solid var(--color-subtle); - border-right: 4px solid var(--color-subtle); - margin-left: -4px; - margin-right: -4px; + border-left: 2px solid var(--color-border-soft); + border-right: 2px solid var(--color-border-soft); + margin-left: -2px; + margin-right: -2px; pointer-events: none; + transition: border-color var(--motion-fast); } .retro-card__link:hover { background: rgb(255 255 255 / 4%); + border-color: var(--color-border); + } + + .retro-card__link:hover::before { + border-color: var(--color-border); } .retro-card__link:focus-visible { @@ -82,6 +102,7 @@ const ctaLabel = label ?? title; display: flex; flex-direction: column; gap: 0.6rem; + min-width: 0; } h3 { @@ -90,6 +111,7 @@ const ctaLabel = label ?? title; font-size: 1.15rem; letter-spacing: -0.02em; color: var(--color-text); + overflow-wrap: anywhere; } p { @@ -97,6 +119,7 @@ const ctaLabel = label ?? title; color: var(--color-muted); font-size: 0.8rem; line-height: 1.4; + overflow-wrap: anywhere; } .retro-card__learn { @@ -104,7 +127,7 @@ const ctaLabel = label ?? title; align-items: center; gap: 0.2rem; margin-top: auto; - color: var(--color-primary); + color: var(--card-accent, var(--color-primary)); font-size: 0.88rem; font-weight: 800; text-decoration: underline; @@ -124,14 +147,8 @@ const ctaLabel = label ?? title; align-self: start; justify-self: end; line-height: 0; + color: var(--card-accent, var(--color-muted)); + background: var(--color-surface-2); + border: 1px solid var(--color-border-soft); } - - .retro-card__glyph :global(.icon) { - display: block; - } - - .retro-card__glyph--purple { color: var(--color-primary); background: rgb(169 112 255 / 12%); } - .retro-card__glyph--blue { color: var(--color-blue); background: rgb(45 140 255 / 12%); } - .retro-card__glyph--green { color: var(--color-green); background: rgb(144 255 177 / 12%); } - .retro-card__glyph--yellow { color: var(--color-yellow); background: rgb(255 192 87 / 12%); } diff --git a/website/src/lib/adapters/manifest.ts b/website/src/lib/adapters/manifest.ts index 2bcf39315..dbe9d4092 100644 --- a/website/src/lib/adapters/manifest.ts +++ b/website/src/lib/adapters/manifest.ts @@ -15,13 +15,13 @@ export interface ManifestSummary { } const SECTION_CONFIG: Record = { - agents: { icon: 'agent', accent: 'purple', href: '/agents/' }, - instructions: { icon: 'instruction', accent: 'blue', href: '/instructions/' }, - skills: { icon: 'skill', accent: 'blue', href: '/skills/' }, - hooks: { icon: 'hook', accent: 'purple', href: '/hooks/' }, - workflows: { icon: 'workflow', accent: 'purple', href: '/workflows/' }, - plugins: { icon: 'plugin', accent: 'blue', href: '/plugins/' }, - tools: { icon: 'tool', accent: 'purple', href: '/tools/' }, + agents: { icon: 'agent', accent: 'green', href: '/agents/' }, + instructions: { icon: 'instruction', accent: 'blue', href: '/instructions/' }, + skills: { icon: 'skill', accent: 'purple', href: '/skills/' }, + hooks: { icon: 'hook', accent: 'yellow', href: '/hooks/' }, + workflows: { icon: 'workflow', accent: 'cyan', href: '/workflows/' }, + plugins: { icon: 'plugin', accent: 'orange', href: '/plugins/' }, + tools: { icon: 'tool', accent: 'danger', href: '/tools/' }, }; const SECTION_LABELS: Record = { diff --git a/website/src/lib/types.ts b/website/src/lib/types.ts index b6eec1ac7..d97c4ecba 100644 --- a/website/src/lib/types.ts +++ b/website/src/lib/types.ts @@ -1,5 +1,5 @@ /** Visual accent categories used by badges and cards. */ -export type Accent = 'purple' | 'blue' | 'neutral' | 'green' | 'yellow' | 'danger'; +export type Accent = 'purple' | 'blue' | 'neutral' | 'green' | 'yellow' | 'danger' | 'cyan' | 'orange'; /** A single navigation link. */ export interface NavItem { diff --git a/website/src/styles/starlight-overrides.css b/website/src/styles/starlight-overrides.css index 4f7ac9ab6..1a7cd9073 100644 --- a/website/src/styles/starlight-overrides.css +++ b/website/src/styles/starlight-overrides.css @@ -388,14 +388,14 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-ma } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content a:not(:where(.not-content *)) { - color: var(--learning-hub-accent); - text-decoration-color: rgb(var(--learning-hub-accent-rgb) / 45%); + color: var(--learning-hub-accent-alt); + text-decoration-color: rgb(var(--learning-hub-accent-alt-rgb) / 45%); text-underline-offset: 0.2em; } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content a:hover:not(:where(.not-content *)) { - color: var(--color-primary); - text-decoration-color: var(--color-primary); + color: var(--color-blue); + text-decoration-color: var(--color-blue); } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content pre:not(:where(.not-content *)), @@ -552,7 +552,7 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-ma html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content th:not(:where(.not-content *)) { border-bottom: 2px solid var(--color-border); background: rgb(var(--learning-hub-accent-rgb) / 12%); - color: var(--learning-hub-accent); + color: var(--color-text); font-family: var(--font-mono); font-size: 0.78rem; font-weight: 900; @@ -581,7 +581,7 @@ html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-ma background: var(--color-bg-elevated); padding: 0.15rem 0.4rem; font-size: 0.85rem; - color: var(--learning-hub-accent); + color: var(--color-text); } html[data-has-sidebar]:has(link[rel="canonical"][href*="/learning-hub/"]) .sl-markdown-content blockquote:not(:where(.not-content *)), diff --git a/website/src/styles/tokens.css b/website/src/styles/tokens.css new file mode 100644 index 000000000..758279d52 --- /dev/null +++ b/website/src/styles/tokens.css @@ -0,0 +1,77 @@ +/* + * Design Tokens β€” visual foundation for the Awesome Copilot website. + * + * Ported from the awesome-copilot-jh retro-futuristic design system. + * All component styles reference these custom properties exclusively. + */ + +:root { + color-scheme: dark; + + /* ── Palette ─────────────────────────────────────────────── */ + --color-bg: #0a0d0d; + --color-bg-elevated: #101314; + --color-bg-soft: #121016; + --color-surface: #0b0e0e; + --color-surface-2: #15191a; + --color-text: #f5f7f7; + --color-muted: #a7adb2; + --color-subtle: #7a838b; + --color-border: rgb(255 255 255 / 22%); + --color-border-soft: rgb(255 255 255 / 15%); + --color-primary: #a970ff; + --color-primary-strong: #8f4dff; + --color-blue: #2d8cff; + --color-green: #90ffb1; + --color-yellow: #ffc057; + --color-danger: #ff507c; + --color-cyan: #00d9ff; + --color-orange: #ff9f43; + --color-black: #050707; + + /* ── Typography ──────────────────────────────────────────── */ + --font-heading: "Space Grotesk", system-ui, sans-serif; + --font-mono: "JetBrains Mono", "SFMono-Regular", Consolas, monospace; + + /* ── Radii ───────────────────────────────────────────────── */ + --radius-1: 4px; + --radius-2: 8px; + --radius-3: 12px; + + /* ── Shadows ─────────────────────────────────────────────── */ + --shadow-card: 0 8px 18px rgb(0 0 0 / 42%); + --shadow-primary: 0 0 14px rgb(169 112 255 / 14%); + + /* ── Spacing scale ───────────────────────────────────────── */ + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.25rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-10: 2.5rem; + --space-12: 3rem; + --space-16: 4rem; + + /* ── Layout ──────────────────────────────────────────────── */ + --container: 1760px; + + /* ── Motion ──────────────────────────────────────────────── */ + --motion-fast: 140ms ease; + + /* ── Accessibility ───────────────────────────────────────── */ + --focus-ring: 0 0 0 3px rgb(144 255 177 / 28%); +} + +/* ── Reduced motion ────────────────────────────────────────── */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + } +} From 5659e0645e9aba12b9a69ebe633c7695d825183e Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Mon, 29 Jun 2026 19:52:46 -0500 Subject: [PATCH 22/26] chore(deps): update pnpm lockfile --- pnpm-lock.yaml | 563 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 pnpm-lock.yaml diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..c6d978b00 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,563 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + js-yaml: + specifier: ^4.1.1 + version: 4.2.0 + vfile: + specifier: ^6.0.3 + version: 6.0.3 + vfile-matter: + specifier: ^5.0.1 + version: 5.0.1 + devDependencies: + all-contributors-cli: + specifier: ^6.26.1 + version: 6.26.1 + +packages: + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + all-contributors-cli@6.26.1: + resolution: {integrity: sha512-Ymgo3FJACRBEd1eE653FD1J/+uD0kqpUNYfr9zNC1Qby0LgbhDBzB3EF6uvkAbYpycStkk41J+0oo37Lc02yEw==} + engines: {node: '>=4'} + hasBin: true + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + inquirer@7.3.3: + resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} + engines: {node: '>=8.0.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + hasBin: true + + json-fixer@1.6.15: + resolution: {integrity: sha512-TuDuZ5KrgyjoCIppdPXBMqiGfota55+odM+j2cQ5rt/XKyKmqGB3Whz1F8SN8+60yYGy/Nu5lbRZ+rx8kBIvBw==} + engines: {node: '>=10'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + pegjs@0.10.0: + resolution: {integrity: sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==} + engines: {node: '>=0.10'} + hasBin: true + + pify@5.0.0: + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + vfile-matter@5.0.1: + resolution: {integrity: sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + +snapshots: + + '@babel/runtime@7.29.7': {} + + '@types/unist@3.0.3': {} + + all-contributors-cli@6.26.1: + dependencies: + '@babel/runtime': 7.29.7 + async: 3.2.6 + chalk: 4.1.2 + didyoumean: 1.2.2 + inquirer: 7.3.3 + json-fixer: 1.6.15 + lodash: 4.18.1 + node-fetch: 2.7.0 + pify: 5.0.0 + yargs: 15.4.1 + optionalDependencies: + prettier: 2.8.8 + transitivePeerDependencies: + - encoding + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + async@3.2.6: {} + + camelcase@5.3.1: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chardet@0.7.0: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-width@3.0.0: {} + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + decamelize@1.2.0: {} + + didyoumean@1.2.2: {} + + emoji-regex@8.0.0: {} + + escape-string-regexp@1.0.5: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + get-caller-file@2.0.5: {} + + has-flag@4.0.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + inquirer@7.3.3: + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.18.1 + mute-stream: 0.0.8 + run-async: 2.4.1 + rxjs: 6.6.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + + is-fullwidth-code-point@3.0.0: {} + + js-yaml@4.2.0: + dependencies: + argparse: 2.0.1 + + json-fixer@1.6.15: + dependencies: + '@babel/runtime': 7.29.7 + chalk: 4.1.2 + pegjs: 0.10.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash@4.18.1: {} + + mimic-fn@2.1.0: {} + + mute-stream@0.0.8: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + os-tmpdir@1.0.2: {} + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + path-exists@4.0.0: {} + + pegjs@0.10.0: {} + + pify@5.0.0: {} + + prettier@2.8.8: + optional: true + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + run-async@2.4.1: {} + + rxjs@6.6.7: + dependencies: + tslib: 1.14.1 + + safer-buffer@2.1.2: {} + + set-blocking@2.0.0: {} + + signal-exit@3.0.7: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + through@2.3.8: {} + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tr46@0.0.3: {} + + tslib@1.14.1: {} + + type-fest@0.21.3: {} + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + vfile-matter@5.0.1: + dependencies: + vfile: 6.0.3 + yaml: 2.9.0 + + 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 + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-module@2.0.1: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@4.0.3: {} + + yaml@2.9.0: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 From 1bea91d86eb8f6a56bcda4f0d4bcec11e4a99990 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:03:57 -0500 Subject: [PATCH 23/26] refactor(website-adapters): update resource adapters and type definitions --- website/src/env.d.ts | 8 +++ website/src/lib/adapters/agents.ts | 50 ++++++++--------- website/src/lib/adapters/hooks.ts | 18 +++---- website/src/lib/adapters/instructions.ts | 20 +++---- website/src/lib/adapters/manifest.ts | 69 +++++++++++++----------- website/src/lib/adapters/plugins.ts | 21 ++++---- website/src/lib/adapters/samples.ts | 22 ++++++-- website/src/lib/frontmatter.ts | 13 +++++ website/tsconfig.json | 4 +- 9 files changed, 130 insertions(+), 95 deletions(-) diff --git a/website/src/env.d.ts b/website/src/env.d.ts index f964fe0cf..92df5b8c6 100644 --- a/website/src/env.d.ts +++ b/website/src/env.d.ts @@ -1 +1,9 @@ /// + +// Starlight virtual component modules are resolved at build time by the +// Starlight Vite plugin but have no static type declarations. Declaring them +// as any-typed ambient modules silences ts(2307) without affecting runtime. +declare module "virtual:starlight/components/MobileMenuToggle" { + const Component: import("astro").AstroComponentFactory; + export default Component; +} diff --git a/website/src/lib/adapters/agents.ts b/website/src/lib/adapters/agents.ts index d2eb027ca..5b5590863 100644 --- a/website/src/lib/adapters/agents.ts +++ b/website/src/lib/adapters/agents.ts @@ -5,13 +5,12 @@ * Handles null models, empty handoffs/tools, and missing optional fields. */ -import type { Accent, ResultItem, ResourceFile } from '../types'; -import type { AgentItem } from '../upstream-types'; -import { rawUrl, githubBlobUrl } from './config'; +import type { ResultItem, ResourceFile } from "../types"; +import type { AgentItem } from "../upstream-types"; +import { stableAccent } from "../../scripts/resource-catalog"; /** Maximum number of tools shown as tags before a "+N more" overflow. */ const MAX_TOOL_TAGS = 3; -const AGENT_ACCENTS: Accent[] = ['purple', 'blue', 'neutral', 'green', 'yellow']; export function adaptAgent(item: AgentItem): ResultItem { const tags = buildTags(item); @@ -22,12 +21,16 @@ export function adaptAgent(item: AgentItem): ResultItem { label: buildLabel(item), description: item.description, tags, - accent: getStableAccent(item), + accent: stableAccent(item.id || item.filename || item.title), detail: item.path, - model: Array.isArray(item.model) ? item.model.join(', ') : (item.model ?? undefined), + model: Array.isArray(item.model) + ? item.model.join(", ") + : (item.model ?? undefined), tools: item.tools.length > 0 ? item.tools : undefined, - handoffs: item.hasHandoffs ? item.handoffs.map(h => h.label || h.agent).filter(Boolean) : undefined, - actions: buildActions(item), + // handoffs is string[] in the upstream schema; use the strings directly. + handoffs: + item.hasHandoffs && item.handoffs.length > 0 ? item.handoffs : undefined, + actions: buildActions(), resourceFiles: buildResourceFiles(item), }; @@ -35,19 +38,10 @@ export function adaptAgent(item: AgentItem): ResultItem { } function buildLabel(item: AgentItem): string { - if (item.hasHandoffs) return 'handoff enabled'; - if (item.tools.length > 0) return 'tool-enabled'; - if (item.model) return 'model configured'; - return 'agent'; -} - -function getStableAccent(item: AgentItem): Accent { - const source = item.id || item.filename || item.title; - let hash = 0; - for (const char of source) { - hash = (hash * 31 + char.charCodeAt(0)) >>> 0; - } - return AGENT_ACCENTS[hash % AGENT_ACCENTS.length]; + if (item.hasHandoffs) return "handoff enabled"; + if (item.tools.length > 0) return "tool-enabled"; + if (item.model) return "model configured"; + return "agent"; } function buildTags(item: AgentItem): string[] { @@ -55,11 +49,11 @@ function buildTags(item: AgentItem): string[] { // Model tag if (item.model && Array.isArray(item.model) && item.model.length > 0) { - tags.push(item.model.join(', ')); - } else if (typeof item.model === 'string' && item.model) { + tags.push(item.model.join(", ")); + } else if (typeof item.model === "string" && item.model) { tags.push(item.model); } else { - tags.push('(none)'); + tags.push("(none)"); } // Tool tags β€” show up to MAX_TOOL_TAGS @@ -71,14 +65,14 @@ function buildTags(item: AgentItem): string[] { // Handoff flag if (item.hasHandoffs) { - tags.push('handoffs'); + tags.push("handoffs"); } return tags; } -function buildActions(_item: AgentItem): string[] { - return ['install', 'download', 'share', 'github']; +function buildActions(): string[] { + return ["install", "download", "share", "github"]; } function buildResourceFiles(item: AgentItem): ResourceFile[] { @@ -86,7 +80,7 @@ function buildResourceFiles(item: AgentItem): ResourceFile[] { if (item.path) { files.push({ path: item.path, - type: 'reference', + type: "reference", description: item.filename || item.path, }); } diff --git a/website/src/lib/adapters/hooks.ts b/website/src/lib/adapters/hooks.ts index f71403518..5c36072ab 100644 --- a/website/src/lib/adapters/hooks.ts +++ b/website/src/lib/adapters/hooks.ts @@ -2,10 +2,9 @@ * HookItem β†’ ResultItem adapter. */ -import type { ResultItem } from '../types'; -import type { HookItem } from '../upstream-types'; -import { stableAccent } from '../../scripts/resource-catalog'; -import { rawUrl } from './config'; +import type { ResultItem } from "../types"; +import type { HookItem } from "../upstream-types"; +import { stableAccent } from "../../scripts/resource-catalog"; export function adaptHook(item: HookItem): ResultItem { const tags = buildTags(item); @@ -13,23 +12,20 @@ export function adaptHook(item: HookItem): ResultItem { const result: ResultItem = { id: item.id, title: item.title, - label: 'Hook', + label: "Hook", description: item.description, tags, accent: stableAccent(item.id), detail: item.readmeFile ?? undefined, - event: item.hooks.length > 0 ? item.hooks.join(', ') : undefined, - actions: ['install', 'download', 'share', 'github'], + event: item.hooks.length > 0 ? item.hooks.join(", ") : undefined, + actions: ["install", "download", "share", "github"], }; return result; } function buildTags(item: HookItem): string[] { - return [ - ...(item.hooks ?? []), - ...(item.tags ?? []), - ]; + return [...(item.hooks ?? []), ...(item.tags ?? [])]; } /** Convenience: adapt an entire array. */ diff --git a/website/src/lib/adapters/instructions.ts b/website/src/lib/adapters/instructions.ts index e7fcdd8bc..220385c78 100644 --- a/website/src/lib/adapters/instructions.ts +++ b/website/src/lib/adapters/instructions.ts @@ -2,10 +2,9 @@ * InstructionItem β†’ ResultItem adapter. */ -import type { ResultItem } from '../types'; -import type { InstructionItem } from '../upstream-types'; -import { stableAccent } from '../../scripts/resource-catalog'; -import { rawUrl } from './config'; +import type { ResultItem } from "../types"; +import type { InstructionItem } from "../upstream-types"; +import { stableAccent } from "../../scripts/resource-catalog"; export function adaptInstruction(item: InstructionItem): ResultItem { const tags = buildTags(item); @@ -13,22 +12,23 @@ export function adaptInstruction(item: InstructionItem): ResultItem { const result: ResultItem = { id: item.id, title: item.title, - label: 'Instruction', + label: "Instruction", description: item.description, tags, accent: stableAccent(item.id), detail: item.path, applyTo: resolveApplyTo(item), - actions: ['install', 'download', 'share', 'github'], + actions: ["install", "download", "share", "github"], }; return result; } function resolveApplyTo(item: InstructionItem): string | undefined { - if (typeof item.applyTo === 'string' && item.applyTo) return item.applyTo; - if (Array.isArray(item.applyTo) && item.applyTo.length > 0) return item.applyTo.join(', '); - if (item.applyToPatterns.length > 0) return item.applyToPatterns.join(', '); + if (typeof item.applyTo === "string" && item.applyTo) return item.applyTo; + if (Array.isArray(item.applyTo) && item.applyTo.length > 0) + return item.applyTo.join(", "); + if (item.applyToPatterns.length > 0) return item.applyToPatterns.join(", "); return undefined; } @@ -38,7 +38,7 @@ function buildTags(item: InstructionItem): string[] { if (item.extensions.length > 0) { tags.push(...item.extensions); } else { - tags.push('(none)'); + tags.push("(none)"); } return tags; diff --git a/website/src/lib/adapters/manifest.ts b/website/src/lib/adapters/manifest.ts index dbe9d4092..5c2544d9f 100644 --- a/website/src/lib/adapters/manifest.ts +++ b/website/src/lib/adapters/manifest.ts @@ -5,8 +5,8 @@ * summary counts used by the landing page. */ -import type { ResourceCard, Accent } from '../types'; -import type { ManifestItem } from '../upstream-types'; +import type { ResourceCard, Accent } from "../types"; +import type { ManifestItem } from "../upstream-types"; export interface ManifestSummary { generated: string; @@ -14,44 +14,49 @@ export interface ManifestSummary { cards: ResourceCard[]; } -const SECTION_CONFIG: Record = { - agents: { icon: 'agent', accent: 'green', href: '/agents/' }, - instructions: { icon: 'instruction', accent: 'blue', href: '/instructions/' }, - skills: { icon: 'skill', accent: 'purple', href: '/skills/' }, - hooks: { icon: 'hook', accent: 'yellow', href: '/hooks/' }, - workflows: { icon: 'workflow', accent: 'cyan', href: '/workflows/' }, - plugins: { icon: 'plugin', accent: 'orange', href: '/plugins/' }, - tools: { icon: 'tool', accent: 'danger', href: '/tools/' }, +const SECTION_CONFIG: Record< + string, + { icon: string; accent: Accent; href: string } +> = { + agents: { icon: "agent", accent: "green", href: "/agents/" }, + instructions: { icon: "instruction", accent: "blue", href: "/instructions/" }, + skills: { icon: "skill", accent: "purple", href: "/skills/" }, + hooks: { icon: "hook", accent: "yellow", href: "/hooks/" }, + workflows: { icon: "workflow", accent: "cyan", href: "/workflows/" }, + plugins: { icon: "plugin", accent: "orange", href: "/plugins/" }, + tools: { icon: "tool", accent: "danger", href: "/tools/" }, }; const SECTION_LABELS: Record = { - agents: 'Agents', - instructions: 'Instructions', - skills: 'Skills', - hooks: 'Hooks', - workflows: 'Workflows', - plugins: 'Plugins', - tools: 'Tools', + agents: "Agents", + instructions: "Instructions", + skills: "Skills", + hooks: "Hooks", + workflows: "Workflows", + plugins: "Plugins", + tools: "Tools", }; const SECTION_DESCRIPTIONS: Record = { - agents: 'Community-contributed custom agents for GitHub Copilot', - instructions: 'Custom coding instructions for languages and frameworks', - skills: 'Agent Skills with bundled resources for specialized tasks', - hooks: 'Automated hooks triggered by Copilot session events', - workflows: 'AI-powered repository automations for GitHub Actions', - plugins: 'Curated plugin bundles of agents and skills', - tools: 'MCP servers, editor extensions, and CLI tools', + agents: "Community-contributed custom agents for GitHub Copilot", + instructions: "Custom coding instructions for languages and frameworks", + skills: "Agent Skills with bundled resources for specialized tasks", + hooks: "Automated hooks triggered by Copilot session events", + workflows: "AI-powered repository automations for GitHub Actions", + plugins: "Curated plugin bundles of agents and skills", + tools: "MCP servers, editor extensions, and CLI tools", }; export function adaptManifest(manifest: ManifestItem): ManifestSummary { - const counts = { ...manifest.counts }; + // Spread into a plain Record so we can index it by string key without TypeScript + // complaining that a specific-key interface is not indexable by arbitrary strings. + const counts: Record = { ...manifest.counts }; const cards: ResourceCard[] = []; for (const [key, config] of Object.entries(SECTION_CONFIG)) { const count = counts[key] ?? 0; const label = SECTION_LABELS[key] ?? key; - const description = SECTION_DESCRIPTIONS[key] ?? ''; + const description = SECTION_DESCRIPTIONS[key] ?? ""; cards.push({ title: count > 0 ? `${label} (${count})` : label, @@ -65,12 +70,12 @@ export function adaptManifest(manifest: ManifestItem): ManifestSummary { // Always add Learning Hub (not in manifest counts) cards.push({ - title: 'Learning Hub', - label: 'Learning Hub', - description: 'Guides, reference docs, and hands-on cookbook recipes', - icon: 'docs', - accent: 'blue', - href: '/learning-hub/', + title: "Learning Hub", + label: "Learning Hub", + description: "Guides, reference docs, and hands-on cookbook recipes", + icon: "docs", + accent: "blue", + href: "/learning-hub/", }); return { diff --git a/website/src/lib/adapters/plugins.ts b/website/src/lib/adapters/plugins.ts index 905a8512c..aee0a7311 100644 --- a/website/src/lib/adapters/plugins.ts +++ b/website/src/lib/adapters/plugins.ts @@ -5,10 +5,9 @@ * and no file-backed modal. Local plugins expose their bundled items. */ -import type { ResultItem } from '../types'; -import type { PluginItem } from '../upstream-types'; -import { stableAccent } from '../../scripts/resource-catalog'; -import { rawUrl } from './config'; +import type { ResultItem } from "../types"; +import type { PluginItem } from "../upstream-types"; +import { stableAccent } from "../../scripts/resource-catalog"; export function adaptPlugin(item: PluginItem): ResultItem { const tags = [...(item.tags ?? [])]; @@ -16,14 +15,18 @@ export function adaptPlugin(item: PluginItem): ResultItem { const result: ResultItem = { id: item.id, title: item.name, - label: 'Plugin', + label: "Plugin", description: item.description, tags, accent: stableAccent(item.id), detail: item.path, items: item.itemCount, - contains: item.items.map(i => i.kind), - author: item.author ?? undefined, + contains: item.items.map((i) => i.kind), + // Upstream author can be a string or a PluginAuthor object; normalise to string. + author: + typeof item.author === "string" + ? item.author + : (item.author?.name ?? undefined), license: item.license ?? undefined, actions: buildActions(item), }; @@ -32,8 +35,8 @@ export function adaptPlugin(item: PluginItem): ResultItem { } function buildActions(item: PluginItem): string[] { - if (item.external) return ['view-repo']; - return ['install', 'github']; + if (item.external) return ["view-repo"]; + return ["install", "github"]; } /** Convenience: adapt an entire array. */ diff --git a/website/src/lib/adapters/samples.ts b/website/src/lib/adapters/samples.ts index 0780073c4..d212bebcc 100644 --- a/website/src/lib/adapters/samples.ts +++ b/website/src/lib/adapters/samples.ts @@ -5,7 +5,12 @@ * for the Learning Hub cookbook page. */ -import type { SamplesData, Cookbook, CookbookRecipe, CookbookLanguage } from '../upstream-types'; +import type { + SamplesData, + Cookbook, + CookbookRecipe, + CookbookLanguage, +} from "../upstream-types"; export interface CookbookRecipeEntry { id: string; @@ -67,15 +72,24 @@ export function adaptSamples(data: SamplesData): { }; } -function adaptRecipe(recipe: CookbookRecipe, cookbook: Cookbook): CookbookRecipeEntry { +function adaptRecipe( + recipe: CookbookRecipe, + cookbook: Cookbook, +): CookbookRecipeEntry { return { id: recipe.id, name: recipe.name, - description: recipe.description ?? '', + description: recipe.description ?? "", tags: recipe.tags ?? [], cookbookId: cookbook.id, cookbookName: cookbook.name, - languages: Array.isArray(recipe.languages) ? recipe.languages : [], + // `languages` is not declared in the upstream CookbookRecipe type but may be + // present at runtime. Cast through `unknown` to avoid modifying the shared type. + languages: Array.isArray( + (recipe as unknown as Record).languages, + ) + ? ((recipe as unknown as Record).languages as string[]) + : [], external: !!(recipe as any).external, url: recipe.url ?? null, variants: recipe.variants ?? {}, diff --git a/website/src/lib/frontmatter.ts b/website/src/lib/frontmatter.ts index 99b4154bf..4b0176b58 100644 --- a/website/src/lib/frontmatter.ts +++ b/website/src/lib/frontmatter.ts @@ -1,3 +1,16 @@ +/** + * Lightweight YAML front matter parser. + * + * Splits a Markdown (or similar) document into its front matter block and body. + * Used by the file-viewer modal to strip YAML before passing Markdown to the renderer. + * + * Regex breakdown for the front matter pattern: + * ^---\r?\n opening fence (β€˜---’), handles both LF and CRLF line endings + * ([\s\S]*?) capture group 1: front matter content (non-greedy to stop at the + * first closing fence rather than consuming the whole file) + * \r?\n---\r?\n? closing fence, with an optional trailing newline + * ([\s\S]*)$ capture group 2: the document body that follows + */ export interface ParsedFrontmatter { frontmatter: string | null; body: string; diff --git a/website/tsconfig.json b/website/tsconfig.json index d78f81ec4..a1e6d60b3 100644 --- a/website/tsconfig.json +++ b/website/tsconfig.json @@ -1,3 +1,5 @@ { - "extends": "astro/tsconfigs/base" + // "strict" enables strictNullChecks, which Astro docs recommend for projects + // using content collections and Zod schema validation. + "extends": "astro/tsconfigs/strict", } From 657a1469cc3ce134259b2fa5c4bc580c00885ae8 Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:04:16 -0500 Subject: [PATCH 24/26] refactor(website-pages): update page data loading and rendering --- website/src/pages/agents.astro | 2 +- website/src/pages/hooks.astro | 3 ++- website/src/pages/index.astro | 2 ++ website/src/pages/skills.astro | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/website/src/pages/agents.astro b/website/src/pages/agents.astro index 7fe16a3a4..850d5b603 100644 --- a/website/src/pages/agents.astro +++ b/website/src/pages/agents.astro @@ -84,7 +84,7 @@ const handoffCount = rawItems.filter(a => a.hasHandoffs).length;
-

{rawItems.length} matching agents Β· debounce 200ms Β· q/model/tool/handoffs/sort in URL

+

{rawItems.length} agents

diff --git a/website/src/pages/hooks.astro b/website/src/pages/hooks.astro index e2940462f..f9e5452f7 100644 --- a/website/src/pages/hooks.astro +++ b/website/src/pages/hooks.astro @@ -111,7 +111,8 @@ const tags = [...new Set(rawItems.flatMap((item) => item.tags))].sort(); const data = await response.json(); const hooks = [...new Set(data.items.flatMap((item: any) => item.hooks))].sort(); hookSelect.innerHTML = '' + - hooks.map((h: string) => ``).join(''); + // Cast to string[] β€” flatMap on untyped JSON data produces unknown[]. + (hooks as string[]).map((h) => ``).join(''); } catch (e) { console.error('Failed to load hook events:', e); } diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 9f3d629d9..05e5b5cb3 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -61,6 +61,8 @@ const searchEntries: SearchResultEntry[] = adaptSearchIndex(searchIndexData as S route: string; label: string; path: string; + // Index signature required by the Searchable constraint in FuzzySearch. + [key: string]: unknown; } const items: GlobalSearchItem[] = entries.map((e) => ({ diff --git a/website/src/pages/skills.astro b/website/src/pages/skills.astro index 34874e6ba..16c493333 100644 --- a/website/src/pages/skills.astro +++ b/website/src/pages/skills.astro @@ -88,7 +88,7 @@ const bundledFileCount = rawItems.reduce((total, item) => total + item.files.len title: item.title, label, description: item.description || 'No description', - tags: [item.category, item.hasAssets ? 'assets' : 'single file', `${item.files.length} files`].filter(Boolean), + tags: ([item.category, item.hasAssets ? 'assets' : 'single file', `${item.files.length} files`].filter(Boolean) as string[]), accent: stableAccent(item.id), href: `/skill/${encodeURIComponent(item.id)}/`, detail: item.skillFile, From ec68dd7a4ff045d5b6dac68a2690cd83fa5906dd Mon Sep 17 00:00:00 2001 From: Jhon Mejia <139589555+JhonHander@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:04:22 -0500 Subject: [PATCH 25/26] refactor(website-client): refine modal and search client logic --- website/src/components/BackToTop.astro | 4 +- website/src/components/Footer.astro | 3 +- website/src/components/InstallModal.astro | 3 +- .../components/resource/ResourceCard.astro | 46 +- website/src/scripts/embedded-data.ts | 25 + website/src/scripts/modal-client.ts | 140 ++--- website/src/scripts/modal.ts | 267 ++++----- website/src/scripts/resource-detail.ts | 556 +++++++++++------- website/src/scripts/search-client.ts | 120 +++- 9 files changed, 652 insertions(+), 512 deletions(-) diff --git a/website/src/components/BackToTop.astro b/website/src/components/BackToTop.astro index 2a2062d34..81d0a31e2 100644 --- a/website/src/components/BackToTop.astro +++ b/website/src/components/BackToTop.astro @@ -28,9 +28,9 @@ function toggleVisibility() { const scrollY = window.scrollY || document.documentElement.scrollTop; if (scrollY > 400) { - button.classList.add('visible'); + button!.classList.add('visible'); } else { - button.classList.remove('visible'); + button!.classList.remove('visible'); } } diff --git a/website/src/components/Footer.astro b/website/src/components/Footer.astro index 7077d8b8d..b5caeacb3 100644 --- a/website/src/components/Footer.astro +++ b/website/src/components/Footer.astro @@ -17,6 +17,7 @@ import ThemeToggle from "./ThemeToggle.astro"; { config.credits && (
+ {/* @ts-ignore β€” Starlight i18n key exists at runtime but isn't in the generated I18nKeys union yet. */} {Astro.locals.t("starlight:builtWithStarlight.label")} ) @@ -84,4 +85,4 @@ import ThemeToggle from "./ThemeToggle.astro"; .made-by a:hover { color: var(--sl-color-accent); } - \ No newline at end of file + diff --git a/website/src/components/InstallModal.astro b/website/src/components/InstallModal.astro index 0ee526d4b..9506ab7a6 100644 --- a/website/src/components/InstallModal.astro +++ b/website/src/components/InstallModal.astro @@ -4,7 +4,6 @@ export interface Props { } const { resourceType } = Astro.props; -const label = resourceType; ---
-

Choose your VS Code edition to install this {label}.

+

Choose your VS Code edition to install this {resourceType}.

+ + + \ No newline at end of file diff --git a/website/src/components/layout/Footer.astro b/website/src/components/layout/Footer.astro index c1865b85a..54224e5dd 100644 --- a/website/src/components/layout/Footer.astro +++ b/website/src/components/layout/Footer.astro @@ -3,9 +3,11 @@ import Icon from '../ui/Icon.astro'; import { REPO, SITE } from '../../config/site'; ---