{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "button", "type": "registry:component", "title": "Dither Button", "description": "Dithered buttons — a native \n )\n}\n" }, { "path": "components/dither-kit/pixel.ts", "type": "registry:component", "target": "components/dither-kit/pixel.ts", "content": "// Standalone pixel primitives for the non-chart Dither Kit pieces (avatar,\n// gradient). Deliberately free of the chart engine so those items install\n// without `core` — only palette.ts is shared. The Bayer matrix and bloom\n// presets mirror dither-paint.ts so everything reads as one texture.\n\nimport { type DitherColor, PALETTE, type Rgb } from \"./palette\"\n\n// 4×4 ordered (Bayer) matrix, normalized to 0–1 thresholds — the same matrix\n// the charts dither with.\nexport const BAYER4 = [\n [0, 8, 2, 10],\n [12, 4, 14, 6],\n [3, 11, 1, 9],\n [15, 7, 13, 5],\n].map((row) => row.map((v) => (v + 0.5) / 16))\n\nexport const clamp01 = (t: number) => (t < 0 ? 0 : t > 1 ? 1 : t)\n\n/** 32-bit FNV-1a hash — turns any string seed into a stable uint32. */\nexport function fnv1a(str: string): number {\n let h = 0x811c9dc5\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i)\n h = Math.imul(h, 0x01000193)\n }\n return h >>> 0\n}\n\n/** Tiny deterministic PRNG (xorshift32) — returns floats in [0, 1). */\nexport function xorshift32(seed: number): () => number {\n let s = seed || 0x9e3779b9\n return () => {\n s ^= s << 13\n s >>>= 0\n s ^= s >>> 17\n s ^= s << 5\n s >>>= 0\n return s / 0x100000000\n }\n}\n\n/** A named palette colour or a raw hue (0–360). */\nexport type PixelColor = DitherColor | number\n\n/** Hue (0–360) → an rgb fill tuned to sit alongside the chart palette. */\nexport function hueFill(hue: number): Rgb {\n const h = ((hue % 360) + 360) % 360\n const s = 0.85\n const l = 0.58\n const c = (1 - Math.abs(2 * l - 1)) * s\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = l - c / 2\n const [r, g, b] =\n h < 60\n ? [c, x, 0]\n : h < 120\n ? [x, c, 0]\n : h < 180\n ? [0, c, x]\n : h < 240\n ? [0, x, c]\n : h < 300\n ? [x, 0, c]\n : [c, 0, x]\n return [\n Math.round((r + m) * 255),\n Math.round((g + m) * 255),\n Math.round((b + m) * 255),\n ]\n}\n\n/** Resolve a {@link PixelColor} to its rgb fill. */\nexport function fillOf(color: PixelColor): Rgb {\n return typeof color === \"number\" ? hueFill(color) : PALETTE[color].fill\n}\n\n// Bloom — same recipe as the charts: a blurred copy of the crisp canvas,\n// composited additively so the glow stays in the dither's own colour.\nexport type PixelBloom = \"off\" | \"low\" | \"high\" | \"aura\"\n\nconst BLOOM_PRESET: Record<\n Exclude,\n { blur: number; brightness: number; opacity: number; saturate: number }\n> = {\n low: { blur: 3, brightness: 1.35, opacity: 0.7, saturate: 1.4 },\n high: { blur: 5, brightness: 1.5, opacity: 0.78, saturate: 1.5 },\n aura: { blur: 15, brightness: 2.9, opacity: 0.1, saturate: 3 },\n}\n\nexport type PixelBloomStyle = {\n filter: string\n opacity: number\n mixBlendMode: \"plus-lighter\"\n imageRendering: \"auto\"\n}\n\n/** Style for the bloom layer canvas. null when off. */\nexport function pixelBloomStyle(bloom: PixelBloom): PixelBloomStyle | null {\n if (bloom === \"off\") return null\n const cfg = BLOOM_PRESET[bloom]\n return {\n filter: `blur(${cfg.blur}px) brightness(${cfg.brightness}) saturate(${cfg.saturate})`,\n opacity: cfg.opacity,\n mixBlendMode: \"plus-lighter\",\n imageRendering: \"auto\",\n }\n}\n\n/** Whether the OS asks for reduced motion (skip entrances). */\nexport function pixelPrefersReducedMotion(): boolean {\n return (\n window.matchMedia?.(\"(prefers-reduced-motion: reduce)\")?.matches ?? false\n )\n}\n" }, { "path": "components/dither-kit/palette.ts", "type": "registry:component", "target": "components/dither-kit/palette.ts", "content": "// Shared seed palette for the dither chart family. Mirrors the seeds in\n// `dither-chart.tsx` so a series rendered through the composable engine reads\n// with the exact same fill / line / star hues as the legacy sparkline.\n\nexport type Rgb = [number, number, number]\n\nexport type DitherColor =\n | \"green\"\n | \"blue\"\n | \"purple\"\n | \"pink\"\n | \"orange\"\n | \"red\"\n | \"grey\"\n\nexport type Seed = { fill: Rgb; line: Rgb; star: Rgb }\n\n// Each seed: the area-fill hue, the bright series line, and the star sparkle.\nexport const PALETTE: Record = {\n green: { fill: [40, 210, 110], line: [150, 255, 180], star: [200, 255, 220] },\n blue: { fill: [53, 143, 243], line: [150, 200, 255], star: [205, 228, 255] },\n purple: {\n fill: [150, 110, 255],\n line: [200, 175, 255],\n star: [225, 210, 255],\n },\n pink: { fill: [240, 90, 190], line: [255, 170, 220], star: [255, 205, 235] },\n orange: {\n fill: [255, 150, 50],\n line: [255, 195, 130],\n star: [255, 220, 175],\n },\n red: { fill: [240, 70, 70], line: [255, 150, 140], star: [255, 195, 185] },\n // No-data: a muted grey so empty metrics read as \"nothing here\".\n grey: { fill: [92, 92, 100], line: [140, 140, 150], star: [165, 165, 175] },\n}\n\nexport const rgb = ([r, g, b]: Rgb, k = 1, a = 1) =>\n `rgba(${Math.round(r * k)},${Math.round(g * k)},${Math.round(b * k)},${a})`\n\nexport const seedOfColor = (color: DitherColor): Seed => PALETTE[color]\n\nexport const isDitherColor = (value: unknown): value is DitherColor =>\n typeof value === \"string\" && value in PALETTE\n" }, { "path": "components/dither-kit/lib.ts", "type": "registry:component", "target": "components/dither-kit/lib.ts", "content": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\n/** Tailwind-aware className combiner — local copy so the chart pack is\n * self-contained and portable as a registry. */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n" } ] }